II. Phân tích và khai thác các lỗ hổng SQL Injection (tiếp)
3. Phá vỡ logic ứng dụng
Xét một chức năng đăng nhập:
Khi người dùng thực hiện đăng nhập, hai tham số username
và password
được truyền tới hệ thống, sau đó được "ghép" trực tiếp vào câu lệnh SQL. Ví dụ câu lệnh kiểm tra thông tin đăng nhập của người dùng trong ngôn ngữ php như sau:
$username = $_POST['username'];
$password = $_POST['password'];
$sql = "SELECT * FROM users WHERE username = '" . $username . "' AND password = '" . $password . "'";
Giả sử chúng ta đã biết tên đăng nhập của victim là victim
, cần tìm cách thay đổi logic câu lệnh trên sao cho khi thực thi thì hệ thống không kiểm tra mật khẩu (do chúng ta không có thông tin mật khẩu). Từ ý tưởng đó, thực hiện thay đổi giá trị username
thành victim'--
, tham số còn lại nhận giá trị tùy ý. Câu lệnh trở thành:
SELECT * FROM users WHERE username = 'victim'--' AND password = '123'
Lúc này hệ thống chỉ thực hiện lấy thông tin từ cơ sở dữ liệu với một điều kiện duy nhất là username = 'victim'
, điều này luôn thực hiện được nên chúng ta có thể đăng nhập thành công!
Xem xét một cách khai thác khác: thay đổi giá trị password
thành 123' or 1=1--
. Khi đó câu lệnh trở thành:
SELECT * FROM users WHERE username = 'victim' AND password = '123' or 1=1--'
Lúc này khi kiểm tra mật khẩu người dùng victim
thì điều kiện logic password = '123' or 1=1--
luôn đúng nên chúng ta cũng có thể đăng nhập vào tài khoản victim.
Phân tích lab SQL injection vulnerability allowing login bypass
Miêu tả: Chức năng đăng nhập của trang web chứa lỗ hổng SQL injection. Chúng ta cần khai thác lỗ hổng để truy cập vào tài khoản administrator
.
Kiểm tra lỗ hổng SQL injection bằng cách thêm một dấu '
trong trường username, trang web xuất hiện lỗi:
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. Hiện tại chúng ta đã biết username của quản trị viên là administrator
. Như phân tích phía trên, chúng ta có thể phá vỡ logic câu lệnh SQL bằng cách thay đổi giá trị username
thành administrator'--
, còn password
nhận giá trị tùy ý. Câu lệnh trở thành:
SELECT * FROM users WHERE username = 'administrator'--' AND password = '123'
Lúc này hệ thống chỉ thực hiện lấy thông tin từ cơ sở dữ liệu với một điều kiện duy nhất là username = 'administrator'
, điều này luôn thực hiện được nên chúng ta đăng nhập thành công!
4. Khai thác lỗ hổng SQL injection - Thu thập thông tin câu truy vấn
Khi chúng ta đã xác nhận điểm chứa lỗ hổng SQL injection và kết quả được trả về chúng ta có thể tiếp tục khai thác các thông tin dữ liệu từ các bảng trong cơ sở dữ liệu. Giả sử câu truy vấn được hệ thống sử dụng ở đây là:
SELECT name, description FROM products WHERE category = 'Gifts'
Ở đây tham số category
có thể thay đổi bởi người dùng, đồng thời đã biết tên bảng cần khai thác là users, nên chúng ta có thể tận dụng phép UNION để truy xuất dữ liệu từ bảng users như sau:
SELECT name, description FROM products WHERE category = 'Gifts' UNION SELECT username, password FROM users--
Vậy thì, phép UNION là gì? Xét cú pháp sử dụng UNION trong câu truy vấn sau:
SELECT a, b FROM table1 UNION SELECT c, d FROM table2
Câu truy vấn thực hiện lấy dữ liệu từ cột a, b trong bảng table1 và dữ liệu từ cột c, d trong bảng table2, sau đó "hợp" dữ liệu lấy từ bảng table2 vào dữ liệu lấy từ bảng table1. Cụ thể, xét hai bảng:
- Bảng people:
id | name |
---|---|
1 | Binh |
2 | Son |
- Bảng fruit:
id | fruit |
---|---|
1 | apple |
2 | watermelon |
Câu truy vấn SELECT name FROM people UNION SELECT fruit FROM fruit
trả về kết quả như sau:
name |
---|
apple |
Binh |
Son |
watermelon |
Và để thực hiện lệnh truy vấn với UNION chúng ta cần chú ý tới hai điều kiện sau cần được thỏa mãn:
- Các câu lệnh SELECT cần trả về số cột dữ liệu bằng nhau.
- Các cột tương ứng cần có cùng kiểu dữ liệu.
Như vậy, trong quá trình khai thác lỗ hổng SQL injection có kết hợp phép UNION: để thỏa mãn điều kiện , chúng ta cần xác định số cột dữ liệu trả về trong câu lệnh truy vấn hệ thống sử dụng; để thỏa mãn điều kiện , chúng ta cần tìm kiếm cột dữ liệu tương thích với kiểu dữ liệu chúng ta cần truy xuất (thường là kiểu string).
4.1. Xác định số cột dữ liệu trả về trong câu truy vấn
Xét câu truy vấn trả về cột dữ liệu như sau:
SELECT name, description FROM products WHERE category = 'gift'
- Cách 1: sử dụng phép UNION
Thay đổi giá trị tham số category
lần lượt là:
' UNION SELECT NULL--
' UNION SELECT NULL, NULL--
' UNION SELECT NULL, NULL, NULL--
...
Lí do chúng ta sử dụng kiểu dữ liệu NULL là vì kiểu MULL có thể tự chuyển hóa thành bất kỳ kiểu dữ liệu nào khác trong quá trình "hợp" kết quả. Do điều kiện trong phép UNION nên với các trường hợp có số lượng từ khóa NULL khác sẽ trả về error trong giao diện. Bằng cách thử này chúng ta có thể xác định số cột dữ liệu trả về trong câu truy vấn là .
- Cách 2: sử dụng phép ORDER BY
Phép ORDER BY thực hiện sắp xếp kết quả truy vấn theo điều kiện đưa ra. Trong trường hợp này, chúng ta cần lợi dụng nó để xác định số cột dữ liệu trả về trong câu truy vấn. Xét câu truy vấn như sau:
SELECT name, description FROM products WHERE category = 'gift' ORDER BY 1
Kết quả câu truy vấn trên sẽ sắp xếp dữ liệu trả về theo cột số - cột name. Tương tự, nếu sử dụng ORDER BY 2 sẽ sắp xếp cột dữ liệu theo cột số - cột description. Như vậy, chúng ta có thể thay đổi giá trị tham số category lần lượt là:
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
...
Cho tới khi câu truy vấn không tìm thấy cột thứ , sẽ trả về error, điều này cũng đồng nghĩa với cột có số thứ tự lớn nhất là , hay câu truy vấn trả về cột dữ liệu.
Phân tích lab SQL injection UNION attack, determining the number of columns returned by the query
Miêu tả: Trang web chứa lỗ hổng SQL injection trong chức năng bộ lọc hiển thị sản phẩm. Chúng ta có thể xác định số cột dữ liệu được trả về trong câu truy vấn. Để giải quyết bài lab, chúng ta cần sử dụng query UNION xác định số cột dữ liệu trả về bằng cách gộp các cột dữ liệu này với null.
Chúng ta có thể sử dụng chức năng bộ lọc hiển thị sản phẩm theo loại, được xác định qua tham số category
trong thanh URL truyền tới hệ thống qua phương thức GET. Tham số này có thể thay đổi tùy ý. Giá trị này cũng được hiển thị trong giao diện response.
Kiểm tra lỗ hổng SQL injection bằng cách thay đổi giá trị category='
, giao diện trả về error.
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. Bắt đầu kiểm tra với payload như sau:
Câu truy vấn trả về cột: /filter?category=Lifestyle' UNION SELECT NULL--
Câu truy vấn trả về cột: /filter?category=Lifestyle' UNION SELECT NULL, NULL--
Câu truy vấn trả về cột: /filter?category=Lifestyle' UNION SELECT NULL, NULL, NULL--
Tại payload kiểm tra số cột dữ liệu trả về là , giao diện phản hồi không còn lỗi xuất hiện, như vậy câu truy vấn dữ liệu trả về cột và bài lab được giải quyết.