SQL Injection - hướng/kỹ thuật khai thác, tools bypass, khai thác, cách phòng chống!
Wikipedia: “SQL Injection (SQLi) là một kỹ thuật cho phép những kẻ tấn công lợi dụng lỗ hổng của việc kiểm tra dữ liệu đầu vào trong các ứng dụng web và các thông báo lỗi của hệ quản trị cơ sở dữ liệu trả về để inject (tiêm vào) và thi hành các câu lệnh SQL bất hợp pháp, SQL Injection có thể cho phép những kẻ tấn công thực hiện các thao tác, thêm, sửa, xóa… trên cơ sở dữ liệu của ứng dụng. Lỗi này thường xảy ra trên các ứng dụng web có dữ liệu được quản lý bằng các hệ quản trị cơ sở dữ liệu như SQL Server, MySQL, Oracle, DB2, Sysbase...”SQL Injection được mô tả như là một trong những lỗ hổng bảo mật web nguy hiểm nhất. Khai thác SQL Injection, ngoài việc đoạt được quyền kiểm soát về mặt dữ liệu như đã nói ở trên, hacker còn có thể cài đặt backdoor trên server mà ứng dụng đang chạy, qua đó kiểm soát toàn bộ hệ thống…
Bài viết này bao gồm 7 phần:
- Phần I : Hướng khai thác
- Phần II : Các kỹ thuật khai thác
- Phần III : Các bước khai thác
- Phần IV : Một số kỹ thuật bypass
- Phần V : Một số tools khai thác
- Phần VI : Các cách phòng chống
- Phần VII : Kết luận
I. Hướng khai thác
Những script SQL nguy hiểm có thể được “tiêm” vào câu truy vấn thông qua nhiều cách khác nhau. Phần này trình bày những hướng khai thác được sử dụng phổ biến nhất, qua đó giúp chúng ta phát hiện và phòng chống tốt hơn.
1. Thông qua “user input”
User input điển hình thường đến từ các form nhập liệu, form search hay link… Những dữ liệu này được web browser gửi đến server thông qua phương thức HTTP GET hay POST và trở thành các tham số cho ứng dụng web truy cập tới cơ sở dữ liệu. Ví dụ như trong một form search, khi người dùng điền vào “SQL Injection”, đơn giản ứng dựng web sẽ truy cập cơ sở dữ liệu và tìm ra các bản ghi mà nội dung của nó chứa từ khóa “SQL Injection” để trả lại kết quả cho người dùng.
Cookies là những tệp tin lưu trữ thông tin trạng thái của người dùng khi truy cập các ứng dụng web. Những thông tin này do người lập trình quyết định, được tạo ra ở server và lưu trữ tại client. Khi người dùng truy cập lại ứng dụng web, cookies được browser gửi lên server giúp phục hồi lại những trạng thái của người dùng trong lần truy cập trước đó. Ở một số ứng dụng web thương mại, cookies còn được lưu trữ những sở thích của người dùng, khi đó ứng dụng web sẽ sử dụng cookies để đưa ra những gợi ý tốt nhất cho người dùng khi mua sản phẩm. Do được lưu trữ ở client nên người dùng có thể chỉnh sửa tùy ý, vì vậy nếu ứng dụng web sử dụng những thông tin lưu trong cookies để xây dựng các truy vấn tới cơ sở dữ liệu thì hacker hoàn toàn có thể chèn vào cookies những script SQL để thực hiện một cuộc tấn công SQL Injection.
Biến server có thể là một khái niệm tương đối lạ lẫm nhưng nó không hề mới. Một số ví dụ của biến server là HTTP header, Network header… Không phổ biến lắm nhưng các giá trị được lưu trong biến server có thể được ứng dụng web sử dụng như trong việc logging truy cập hay thống kê truy cập theo user agent… Những công việc này đều có sự tương tác với cơ sở dữ liệu nên các hacker hoàn toàn có thể sử dụng các biến server trong việc khai thác SQL Injection.
Đây là kỹ thuật ít được sử dụng vì rất khó để nhận biết một ứng dụng web có bị mặc lỗi này hay không. Kỹ thuật này được mô tả như sau : Trước hết, hacker “inject” vào cơ sở dữ liệu một đoạn mã. Đoạn mã này chưa hề gây nguy hiểm cho hệ thống nhưng nó sẽ được sử dụng làm bàn đạp cho lần inject tiếp theo của hacker. Chúng ta hãy xem một ví dụ cụ thể để hiểu hơn về kỹ thuật này.
Một hacker truy cập vào một ứng dụng web và cố gắng đăng ký một tài khoản có username là “administrator’ --”. Sau đó hacker này thực hiện thao tác thay đổi mật khẩu. Thao tác thay đổi mật khẩu được ứng dựng web xử lý như sau :
II. Các kỹ thuật khai thác
1. BOOLEAN BASED và TIME BASED BLIND SQL INJECTION
Boolean based: Cơ sở của kỹ thuật này là việc so sánh đúng sai để tìm ra từng ký tự của những thông tin như tên bảng, tên cột… Do đó, với dải giá trị chữ số, chữ cái (bao gồm cả hoa, thường), và một số ký tự đặc biệt, việc so khớp trở nên rất khó khăn và đòi hỏi nhiều thời gian. Do đó việc khai thác lỗi chủ yếu được tiến hành bằng tools.
Trong kỹ thuật BQLi, chúng ta cũng có nhiều phương pháp khác nhau. Điểm khác biệt giữa các phương pháp này là sự tối ưu thời gian. Chúng ta sẽ có những tài liệu trình bày cụ thể về BQLi cũng như các phương pháp của nó.
Đây là phương pháp phổ biến khi khai thác SQL Injection. Cơ sở của nó là sử dụng từ khóa union để gộp các kết quả của các mệnh đề select, qua đó lấy được thông tin từ cơ sở dữ liệu.
3. BATCHED QUERY
Đây là phương pháp áp dụng khả năng thực thi cùng lúc nhiều câu lệnh SQL của một số hệ quản trị cơ sở dữ liệu và khả năng hỗ trợ của ngôn ngữ lập trình. Phương pháp này rất mạnh mẽ và gây nguy hiểm ngay với hệ thống. Bằng cách thêm vào một dòng lệnh Update, Insert hay Delete, dữ liệu trong cơ sở dữ liệu của ứng dụng web không còn toàn vẹn nữa.
Support
|
ASP
|
ASP.NET
|
PHP
|
MySQL
|
No
|
Yes
|
No
|
PostgreSQL
|
Yes
|
Yes
|
Yes
|
MS SQL
|
Yes
|
Yes
|
Yes
|
Không giống như các phương pháp trên, nội dung inject nằm trong mệnh đề điều kiện where. Trong phương pháp này, chúng ta sẽ cố gắng tiêm mã script vào mệnh đề order. Chúng ta hãy xem đến một kịch bản sau:
Người lập trình muốn liệt kế sản phẩm của công ty bao gồm các thông tin: Mã sản phẩm, Tên sản phầm, Ngày tháng… và có chức năng cho pháp người dùng tủy chỉnh xem họ muốn sắp xếp theo thứ tự ngày tháng, theo tên hay mã của sản phẩm.
Câu truy vấn được xây dựng như sau:
III. Các bước khai thác thông tin
1. Phát hiện
Một cách thông thường, để phát hiện một ứng dụng web có dính lỗi SQL Injection hay không là thêm vào câu truy vấn các meta character trong các hệ quản trị cơ sở dữ liệu, chẳng hạn như dấu nháy đơn (single quote), dấu nháy kép (double quote), dấu chấm phẩy (semi colon) và các ký tự comment (--, ##, /**/)… và chờ xem ứng dụng web sẽ xứ lý câu truy vấn đó như thế nào.
Ví dụ khi muốn xem item có id=2 ta request tới liên kết http://site/item.php?id=2. Để xem liên kết này có dính lỗi SQL Injection hay không ta thêm vào cuối liên kết một trong các meta character đã nói ở trên, chẳng hạn ta thêm vào dấu nháy đơn http://site/item.php?id=2’. Nếu như ứng dụng web vẫn trả về cho chúng ta nội dung của item có id=2 hoặc đưa ra một thông báo về việc không tìm được item hoặc đưa chúng ta tới một trang khác (một trang thông báo lỗi mặc định hay trang chủ chẳng hạn), như vậy ta có thể kết luận rằng ứng dụng đã xứ lý tốt tham số đầu vào trước khi thao tác cơ sở dữ liệu. Ngược lại, nếu thấy xuất hiện một thông báo lỗi (exception) từ MySQL, MSSQL… thì ứng dụng web đã dính lỗi SQL Injection.
2. Thu thập thông tin về hệ quản trị cơ sở dữ liệu
Khi phát hiện ứng dụng bị dính lỗi SQLInjection, công việc cần làm tiếp theo là thu thập thông tin về hệ quản trị cơ sở dữ liệu mà ứng dụng đang dùng, thông tin này bao gồm loại cơ sở dữ liệu (MySQL, MSSQL, Oracle…) và phiên bản của nó. Tùy vào ứng dụng sử dụng phiên bản hay loại hệ quản trị sơ sở dữ liệu nào mà chúng ta có những kỹ thuật khai thác khác nhau. Một ví dụ đơn giản cho thấy sự khác nhau giữa các loại hệ quản trị cơ sở dữ liệu đó là, trong khi mssql sử dụng ký tự comment là ‘--’ thì MySQL lại sử dụng ‘##’…
Để xác định hệ loại quản trị mà ứng dụng đang sử dụng, chúng ta có thể đánh giá theo nhiều tiêu chí. Có thể đánh giá qua thông báo lỗi :
Thông báo lỗi từ MS-SQL – IIS
Thông báo lỗi từ MySQL – Apache
Và sử dụng: id=1; select @@version-- để xem phiên bản của hệ quản trị cơ sở dữ liệu…
3. Xác định số lượng cột trong mệnh đề select
Khi khai thác SQLInjection, chúng ta thường sử dụng một hay nhiều mệnh đề select phụ (subselect), điều này được thực hiện thông qua từ khóa union. Union là từ khóa dùng để gộp kết quả của nhiều mệnh đề select do đó trong mỗi mệnh đề select đòi hỏi số lượng các trường đều phải bằng nhau và đều bằng số lượng các trường được select trong mệnh đề select ban đầu. Xét một ví dụ cụ thể :
Một cách khác để làm điều này, nhanh chóng hơn đó là sử dụng ‘order by’. Trong các hệ quản trị cơ sở dữ liệu từ khóa ‘order by’ được sử dụng để sắp xếp thứ tự cho các bản ghi thu được trong mệnh để select. Sau order bycó thể là tên một cột để xác định rằng kết quả thu về sẽ được sắp xếp theo giá trị của cột đó (có thể tăng dần hay giảm dần). Sau order by cũng có thể là số thứ tự vị trí của cột đó. Nếu giá trị sau order lớn hơn số cột được select thì chúng ta sẽ thấy thông báo lỗi.
Để khai thác được SQL Injection, chúng ta cần biết một số thông tin về cơ sở dữ liệu như tên bảng, tên cột, các kiểu dữ liệu của từng cột… Giai đoạn này đòi hỏi khá nhiều thời gian.
- Tên bảng và cột
Một cách chính qui hơn để biết được tên bảng, tên cột là sử dụng đối tượng information_schema. Đối tượng này cung cấp các thông tin về tables, columns, views và procedures… của cơ sở dữ liệu. Để đọc được những thông tin chứa trong information_schema chúng ta cũng cần một mệnh đềsubselect :
- Kiểu dữ liệu
IV. Một số kỹ thuật bypass
Trong quá trình khai thác SQL Injection chúng ta có thể gặp một số hạn chế khiến cho câu truy vấn mà chúng ta inject vào không thể thực thi được. Những hạn chế này có thể xuất phát từ những cấu hình filter trên web server, những ứng dụng được cài đặt trên máy chủ (WAF – Web Application Firewall) hay từ chủ định của người lập trình ứng dụng web hòng phát hiện những truy vấn bất thường và tìm cách loại bỏ chúng.
Ứng dụng WAF hoạt động như một request filter. Trong đó chúng ta xây dựng các luật lọc được thể hiện bằng regular expression hay kiểu như pattern matching. Những request lên server mà match với những luật này sẽ được lọc bỏ. Một Web Application Firewall được chúng ta biết đến đó là Mod Security. Các tính năng và cách sử dụng cũng như cấu hình của Mod Security đã được trình bày trong một tài liệu khác đã được BSE thực hiện.
Cũng với ý nghĩa đó, người lập trình ứng dụng web cố gắng xây dựng các đoạn code xử lý nhằm lọc bỏ những pattern hay keyword trong những requestmà họ cho là có thể gây hại cho hệ thống. Chẳng hạn trong những ví dụ về khai thác SQL Injection chúng ta thường sử dụng những từ khóa như : union, select, information_schema… Nhiều trường hợp, người lập trình chỉ đơn giản là replace những từ khóa đó đi :
Khi gặp phải những hàm filter do những lập trình viên thiết kế, chúng ta phải biết cách phán đoạn xem request của chúng ta được xử lý như thế nào mà có những phương pháp bypass phù hợp. Nhiều khi, một truy vấn được áp dụng nhiều luật lọc khác nhau nên đòi hỏi chúng ta phải áp dụng tổ hợp nhiều kỹ thuật khác nhau. Sau đây là một số ví dụ mà hi vọng qua đó mọi người có thể tự tìm cho mình những phương pháp bypass đúng đắn trong những trường hợp cụ thể.
1. Cắt bớt nội dung câu truy vấn
Trong trường hợp muốn lờ đi những đoạn script trong câu truy vấn. Ví dụ đối với đoạn xử lý dưới đây, trong câu truy vấn đòi hỏi điều kiện active=1 nhưng chúng ta có thể comment (--, #, /**/, //, ;, …) và lờ nó đi. Khi khai thác chúng ta thường không biết nội dung còn lại của câu truy vấn làm công việc gì nên sử dụng comment trong trường hợp nảy rất hiệu quả.
a. Inline Comment
Inline comment được sử dụng rất hiệu quả trong việc bypass lọc các khoảng trắng. Ví dụ:
Chúng ta có thể bypass khi WAF chặn các từ khóa bằng cách encode chúng. Rất nhiều ứng dụng WAF sẽ chỉ decode truy vấn một lần và lọc bỏ các từ khóa trong blacklist, khi đó chúng ta hãy encode 2 lần request như vậy có thể bypass được trong trường hợp này.
3. Bypass chặn nháy đơn, nháy kép
- Chúng ta hãy xét một ví dụ trước khi tìm hiểu cụ thể cách bypass này.
- http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string
- http://blog.bkis.vn/display/BSERESEARCH/2012/06/25/By+pass+addslashes%28%29+function
4. Bypass lỗi “illegal mix of collation for operation UNION”
Trong một số hệ quản trị (thường thấy trong MySQL), các database, các table khi đã được set collation thì khi sử dụng từ khóa UNION sẽ bị báo lỗi “illegal mix of collation for operation UNION”. Việc thiết lập collation (đối chiếu font mã hóa) có thể do chủ định của người thiết kế cơ sở dữ liệu hoặc do được thiết lập mặc định của MySQL. Trong trường hợp dùng union, chúng ta phải đảm bảo điều kiện giá trị select ở từng trường phải có kiểu mã tương ứng đã được định nghĩa. Theo mình đánh giá, lỗi này là khá phổ biến, đặc biệt đối với các CMS chạy Apache MySQL. Mọi người có thể tìm hiểu thêm tại địa chỉ :http://bugs.mysql.com/bug.php?id=57926 hoặc một bài viết tiếng Việt cũng khá kỹ cũng có liên quan tới collation:
Trong trường hợp này chúng ta có thể sử dụng các cách convert thành kiểu mã hóa phù hợp. Ví dụ trong trường hợp sau :
V. Một số tools khai thác
Hiện nay đã có rất nhiều công cụ quét lỗ hổng bảo mật (bao gồm SQL Injection). Những công cụ này chủ yếu cho công việc phát hiện và đánh giá mức độ nguy hiểm của các loại lỗ hổng chứ chưa có khả năng khai thác được xem có thể làm được gì từ lỗ hổng đó. Một số công cụ khai thác tự động hay được sử dụng đó là :
- The Mole (Digging up your data)
- sqlmap
- Havij
VI. Các cách phòng chống
Ngay từ khái niệm, chúng ta đã có thể biết được cách phòng chống hiệu quả SQL Injection chính là việc kiểm tra kỹ càng tham số đầu vào. Những tham số mà từ đó người lập trình website sử dụng để build lên câu truy vấn tới cơ sở dữ liệu.
Công việc kiểm tra tham số đầu vào (áp dụng phòng tránh lỗi SQL Injection) nên được tiến hành theo nhiều tầng:
- Client: javascript (có thể bypass bằng các phần mềm tamper)
- Server: C#, php, jsp
- Nếu là kiểu int:
- ASP.NET: Int32.Parse(/*Param*/);
- PHP: is_numeric($var), is_int($var), is_integer($var)...
- Nếu là kiểu chuỗi:
- Thiết lập độ dài tối đa cần thiết: length <= MAX_LENGTH
- Lọc bỏ ký tự nguy hiểm (bao gồm các meta characters trong DBMS)
- Kiểm tra xem định dạng chuỗi: email, username, password… đều có một định dạng nhất định, do đó có thể viết các biểu thức chính qui (Regular Expression) cho mỗi định dạng này.
- C#: System.Text.RegularExpressions;
- PHP: preg_match(); ...
- Java: Pattern.matcher(); ...
- Sử dụng các thư viện hỗ trợ :
- C#: Parameters
cmd.Parameters.Add("@var", SqlDbType.Int);
cmd.Parameters["@var"].Value = input; |
- Phân quyền SQL
- Tài khoản sử dụng cho ứng dụng web trong cơ sở dữ liệu dĩ nhiên phải có quyền đọc. Nhưng khi thực hiện thao tác thêm bài viết, thêm user thì nó phải cần thêm quyền ghi nữa. Do đó, tùy vào mỗi trường hợp mà ta thiết lập role phù hợp cho mỗi tài khoản web tương tác với cơ sở dữ liệu. Với những ứng dụng web thông thường, tài khoản cho ứng dụng web trong SQL SERVER nên chỉ được thiết lập quyền đọc (SELECT) (db_datareader) và quyền ghi (INSERT, UPDATE) (db_datawriter) như vậy sẽ không có quyền xóa một table.
SQL Injection luôn được đánh giá là một trong những lỗ hổng bảo mật web nguy hiểm nhất trong nhiều năm qua. Do đó, đối với những người lập trình web cần hiểu rõ nguyên nhân và cách khắc phục sao cho hiệu quả. Những người quản trị cần có những cấu hình cần thiết để trách nguy cơ và giảm thiểu tác hại trong trường hợp ứng dụng web có lỗi có thể khai thác SQL Injection.
Các kỹ thuật tấn công, bypass trên đây, tùy vào những trường hợp cụ thể mà có những cách áp dụng khác nhau. Do tính chất đặc thù trong mỗi trường hợp như thế nên bài viết không thể bao quát hết được tất cả các kỹ thuật. Với sự phát triển của các ứng dụng firewall (WAF) có thể những cách bypass nêu trên sẽ không còn có thể áp dụng được trong tương lai.