I. Đặt vấn đề
1. Giới thiệu
Một vấn đề lớn mà mỗi một hệ thống cần phải đối mặt đó chính là làm sao để lưu trữ các thông tin một cách hiệu quả và khi cần sử dụng có thể truy xuất nhanh chóng và chính xác. Từ đó, cơ sở dữ liệu (Database) ra đời, nó là một hệ thống bao gồm rất nhiều thông tin, dữ liệu được xây dựng theo một cấu trúc nhất định nhằm đáp ứng nhu cầu khai thác, sử dụng đồng thời của nhiều người hay nhiều chương trình ứng dụng. Không chỉ giúp khắc phục được những điểm yếu của việc lưu file thông thường trên máy tính, cơ sở dữ liệu còn đảm bảo thông tin lưu trữ được nhất quán, hạn chế tình trạng trùng lặp thông tin. Là nơi lưu trữ hầu như các thông tin dữ liệu quan trọng nhất của hệ thống, nên cơ sở dữ liệu thường là mục tiêu tấn công đầu tiên của kẻ xấu.
Trong môn học cơ sở dữ liệu, chúng ta được học về các mô hình database, đồng thời được thực hành sử dụng các câu lệnh SQL (Structured Query Language) nhằm truy xuất dữ liệu từ database. SQL Injection (thường được gọi tắt là SQLi) chính là một dạng tấn công trực tiếp can thiệp vào các câu lệnh truy vấn dữ liệu từ trang web tới cơ sở dữ liệu, qua đó có thể chỉnh sửa ý nghĩa câu truy vấn nhằm thực hiện mục đích xấu.
Lỗ hổng SQL Injection thường dẫn tới việc lộ thông tin nhạy cảm người dùng, dữ liệu cá nhân có nguy cơ bị sử dụng trái phép, kẻ xấu có thể truy cập vào các tài khoản có quyền hạn cao, hệ thống có nguy cơ bị mất quyền kiểm soát. Dạng lỗ hổng Injection được xếp vị trí đầu tiên trong Top lỗ hổng bảo mật web của OWASP vào năm , và SQL injection là dạng tấn công phổ biến nhất! (Thậm chí tới năm dạng lỗ hổng Injection vẫn được xếp ở vị trí số ) Các lỗ hổng SQL injection thường được đánh giá mức độ nghiêm trọng ở dạng High và đã gây ra thiệt hại rất lớn cho các nhà phát triển cũng như doanh nghiệp.
2. Kiến thức cần chuẩn bị
Với công nghệ tiên tiến hiện nay đã phát triển thành công nhiều hệ quản trị cơ sở dữ liệu khác nhau, có thể kể đến như MYSQL, MariaDB, Oracle, PostgreSQL, Microsoft SQL Server, ..., mỗi hệ có những ưu, nhược điểm riêng. Tất nhiên các câu lệnh truy xuất đối với mỗi hệ quản trị cơ sở dữ liệu cũng khác nhau, dẫn tới các hình thức, payload tấn công cũng đa dạng và luôn cần thay đổi linh hoạt. Ở đây tôi sẽ sử dụng cơ sở dữ liệu MYSQL làm ví dụ cho kiến thức cần chuẩn bị. Khi gặp các trường hợp cơ sở dữ liệu khác, các bạn có thể xây dựng các payload cho mình một cách tương tự, hoặc có thể tham khảo tại Payloads All The Things.
Trước hết, chúng ta cần biết tới INFORMATION_SCHEMA trong MySQL - là một cơ sở dữ liệu thông tin, tại đây lưu trữ thông tin các cơ sở dữ liệu khác của MySQL. Trong cơ sở dữ liệu thông tin này có nhiều bảng và view, tuy nhiên có lẽ hay sử dụng nhiều nhất là các bảng TABLES - chứa tất cả thông tin về các bảng, COLUMNS - chứa tất cả các thông tin về các cột của các bảng, USER_PRIVILEGES - chứa tất cả các thông tin về quyền truy cập của các người dùng đối với mỗi cơ sở dữ liệu.
Để truy xuất các thông tin mong muốn, chúng ta cần xác định tên cơ sở dữ liệu (database name), tên bảng (table name), tên cột (column name) cũng như các điều kiện cần thỏa mãn. Bởi vậy chúng ta cần nắm vững kiến thức các câu lệnh truy vấn đối với từng hệ quản trị cơ sở dữ liệu khác nhau để có thể thực hiện khai thác tối đa đối với dạng lỗ hổng SQL Injection.
II. Phân tích và khai thác các lỗ hổng SQL Injection
1. Nguyên nhân xuất hiện lỗ hổng SQL Injection
Hệ thống thực hiện lưu thông tin người dùng vào cơ sở dữ liệu, khi cần hiển thông các thông tin cho người dùng, các câu lệnh truy xuất dữ liệu từ database sẽ được thực thi. Chẳng hạn khi người dùng nhập vào ô tìm kiếm dữ liệu "linh", lúc này hệ thống cần lấy tất cả thông tin của người dùng có name = linh, đoạn mã thực thi được sử dụng trong hệ quản trị cơ sở dữ liệu MYSQL và ngôn ngữ PHP như sau:
$input = $_GET['search'];
$sql = "SELECT * FROM student WHERE name = '" . $input ."'";
$result = mysql_query($sql);
Biến $input
nhận giá linh
, sau khi ghép chuỗi thì chuỗi $sql = "SELECT * FROM student WHERE name = 'linh'"
, khi thực thi qua hàm mysql_query()
sẽ có thể truy xuất kết quả. Tuy nhiên, chú ý rằng giá trị biến $input
có thể thay đổi bởi người dùng, nếu không thực hiện các cơ chế phòng ngừa tấn công, kẻ xấu có thể nhập giá trị linh'
cho biến này, dẫn tới câu lệnh truy vấn trở thành:
SELECT * FROM student WHERE name = 'linh''"
Và khi thực thi sẽ gây ra lỗi do thừa một dấu '
. Lúc này chắc hẳn có nhiều bạn thắc mắc việc gây ra lỗi như này có tác dụng gì? Câu trả lời tất nhiên là có, vì chúng ta có thể sử dụng dấu '
nhằm "đóng" giá trị name
phía trước đi, hoặc kết thúc câu lệnh truy vấn này, sau đó sẽ có thể ghép nối tất cả mọi thứ mình mong muốn, chỉ cần thỏa mãn cấu trúc lệnh truy vấn. Tuy nhiên, còn một vấn đề cuối cùng nữa, đó là vì cuối câu lệnh luôn được ghép một dấu '
, hoặc trong các trường hợp có thể phía sau còn một phần câu lệnh truy vấn thì xử lý như thế nào? Yên tâm vì chúng ta có thể giải quyết vấn đề này đơn giản bằng cách sử dụng các ký tự comment trong MYSQL như --
(chú ý có một dấu khoảng trống phía sau), #
, /* */
để biến các chuỗi "dư thừa" trở thành phần comment.
2. Truy xuất các dữ liệu hiển thị trái phép
Ví dụ trong một trang web mua sắm trực tuyến, người dùng có thể xem các dòng sản phẩm theo tham số category
:
https://insecure-website.com/products?category=Gifts
Và khi hệ thống thực hiện truy xuất các sản phẩm từ cơ sở dữ liệu sẽ sử dụng thêm điều kiện released = 1
chỉ lấy các dòng sản phẩm đang phát hành:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
Do giá trị tham số category
có thể thay đổi tùy ý nên chúng ta có thể lợi dụng kỹ thuật tấn công SQL injection khiến trang web hiển thị thêm các dòng sản phẩm đã dừng phát hành hoặc sẽ phát hành trong tương lai. Xem xét một số cách tấn công như sau:
https://insecure-website.com/products?category=Gifts'--
Lúc này câu truy vấn trở thành:
SELECT * FROM products WHERE category = 'Gifts'-- AND released = 1
Ký hiệu --
đã khiến phần theo sau nó được hệ thống hiểu là comment nên đã vô hiệu hóa điều kiện released = 1
, khiến hệ thống lấy dữ liệu tất cả sản phẩm chỉ cần thỏa mãn điều kiện category = 'Gifts'
, trong đó bao gồm các sản phẩm released = 1
và released = 0
.
https://insecure-website.com/products?category=Gifts' OR 1=1--
Lúc này câu truy vấn trở thành:
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
Khi đó hệ thống kiểm tra điều kiện category = 'Gifts' OR 1=1
là một điều kiện luôn đúng (logic trong Đại số). Bởi vậy tất cả sản phẩm đều được hiển thị!
Phân tích lab SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
Miêu tả: Trang web chứa lỗ hổng SQL injection trong câu lệnh truy xuất dữ liệu. Để giải quyết bài lab, chúng ta cần khai thác lỗ hổng để giao diện web hiển thị tất cả sản phẩm bao gồm cả phát hành và chưa phát hành.
Chúng ta có thể xem các sản phẩm theo từng thể loại. Để ý rằng trang web xác định các dòng sản phẩm theo tham số category
trong thanh URL, giá trị này có thể thay đổi tùy ý.
Kiểm tra lỗ hổng SQL injection, thêm một dấu '
chúng ta thấy trang web xuất hiện lỗi:
/filter?category=Lifestyle'
Có thể dự đoán lỗi xuất hiện là do câu lệnh truy vấn bị sai cú pháp như đã phân tích phía trên. Thực hiện tấn công SQL injection, chúng ta chỉ cần thay đổi giá trị category
kết hợp với một biểu thức logic luôn đúng sẽ dẫn tới hệ thống truy xuất tất cả sản phẩm trong cơ sở dữ liệu:
/filter?category=Lifestyle' or 1=1--
Bài lab được giải quyết: