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.
Một trường hợp khác phổ biến hơn trong kỹ thuật tấn công Sql Injection, khi người dùng request một tài liệu mà các tham số của nó được truyền qua url (như ở ảnh minh họa bên dưới, tham số id được truyền qua url theo phương thức Http GET ). Khi nhận được request, ứng dụng web tìm trong cơ sở dữ liệu và trả về cho người dùng bài viết có id=31.
2. Thông qua cookies
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.
3. Thông qua các biến server
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.
4. Second-order 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 :
Với username đã đăng ký ở trên, câu truy vấn trên trở thành :
Như vậy, hacker đã thay đổi được password của tài khoản administrator và hoàn toàn có thể đăng nhập dưới tên tài khoản administrator. Ý đồ của hắn đã thành công
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ó.
Time based : Giống như boolean based attacks chỉ khác nhau về cách suy diễn.
2. UNION QUERY BASED
Đâ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
4. ORDER BY CLAUSE
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 :
Trong trường hợp này chúng ta không thể thêm trực tiếp một mệnh đề sub select thông qua từ khóa union như mọi khi được. Một cách khai thác đó là sử dụng BATCHED QUERY hoặc có thể tham khảo cách sau
Trong phương pháp trên, chúng ta đã inject được một sub select nhưng rõ ràng cách thực hiện này giờ đây phải kết hợp cả với kỹ thuật BOOLEAN BASED BLIND SQLI.
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 Sql Injection, 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 Sql Injection, 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ể :
Ở đây, trong mệnh đề select ban đầu chọn ra 3 trường là id, content và author. Do đó mệnh đề select sau từ khóa union cũng cần phải có đúng 3 trường. Nếu số trường select ở mệnh đề select sau union không bằng số lượng các trường được select trong mệnh đề select đầu tiên, chúng ta sẽ nhận được thông báo lỗi. Vậy làm sao để biết để biết được chính xác mệnh đề select đầu tiền chọn ra bao nhiêu trường. Chúng ta có thể thực hiện thử dần bằng cách tăng dần số lượng cột trong mệnh đề select sau union (bắt đầu từ 1). Khi nào không thấy thông báo lỗi xuất hiện thì đó chính là số lượng cột cần tìm. 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.
4. Xác định thông tin
Để 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
Chúng ta có nhiều cách để làm được công việc này, một trong những cách đó là “đoán” vì nó nhanh chóng và trong những trường hợp cụ thể, đây là cách rất hữu ích. Ví dụ khi tên bảng là một tên quen thuộc như : user, users, admin, administrator, staff, account… (chú ý tiền tố tbl_ rất hay được các lập trình viên sử dụng để đặt cho tên bảng).
Ở đây chúng ta không quan trọng mệnh đề select phía sau trả giá trị là bao nhiêu bản ghi. Cái mà ta quan tâm là ‘tablename’ có đúng là một tên bảng hay không. Cũng theo cách này chúng ta cũng có thể đoán biết được tên cột, username hay password… 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 :
Khi khai thác Sql Injection với hệ quản trị Microsoft SQL Server thường gặp phải những bó buộc với kiểu dữ liệu của các trường. Trong các mệnh đề select trước và sau union, kiểu dữ liệu của các trường tương ứng phải thống nhất với nhau.
Một thủ thuật nhỏ có thể được áp dụng để bypass trường hợp này đó là thay vì select 1,2 như ví dụ trên, ta hãy select giá trị null.
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 request mà 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 :
Chúng ta dễ dàng nhận thấy rằng đoạn mã xử lý trên còn thiếu xót. Nếu đơn thuần chỉ là pattern matching thì cách bypass cực kỳ đơn giản. Chúng ta hãy áp dụng case sensitive, khi đó chữ viết hoa và viết thường được hiểu khác nhau. Lúc này thay vì sử dụng từ khóa select chúng ta hãy sử dụng SeLEcT. Cơ sở của cách bypass này là những hệ quản trị cơ sở dữ liệu không phân biệt hoa thường với những từ khóa. 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 (--, #, /**/, //, ;%00, …) 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ả.
Sau khi comment, truy vấn của chúng ta trở thành :
2. Bypass việc lọc các từ khóa
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ụ:
Hay bypass lọc các từ khóa (khả dụng với MySql). Trong ví dụ dưới đây, từ khóa union và password nằm trong blacklist nên đã bị chặn, chúng ta có thể bypass bằng cách:
b. Character encoding
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.
- 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.
Trong kịch bản này, chúng ta đã biết được một bảng trong cơ sở dữ liệu có tên là users. Công việc tiếp theo là phải biết được tên cột trong bảng để lấy được thông tin của nó. Như trong câu truy vấn trên, chúng ta sử dụng điều kiện : table_name=’users’. Nhưng nếu cả dấu nháy đơn (‘) và dấu nháy kép (“) đều bị WAF chặn thì chúng ta không thể sử dụng ‘users’ hay “users” được nữa. Vậy phải giải quyết vấn đề này như thế nào? Trong các hệ cơ sở dữ liệu built sẵn cho chúng ta function giải quyết rất tốt vấn đề này đó là hàm CHAR( ) (đối với Oracle là CHR()). Ví dụ trong câu truy vấn trên chúng ta sẽ bypass bằng cách:
Những lập trình viên php đều đã rất quen thuộc với hàm addslashes(). Hàm addslashes() có tác dụng thêm vào trước những ký tự đặc biệt như single quote (‘), double quote (“), backslash (), NUL (null byte) ký tự ” ” giúp hệ quản trị cơ sở dữ liệu không gặp khó khăn và nhầm lẫn khi xử lý chuỗi chứa các ký tự đó. Như vậy, khi chúng ta muốn inject vào câu truy vấn theo như kịch bản : name=’someName’ or ‘1’=’1’ -- thì kết quả không còn đúng như chúng ta mong đợi nữa. Tuy vậy, đã có kỹ thuật giúp hacker bypass hàm addslashes() để inject ký tự single quote (‘). Kỹ thuật này đã được public từ khá lâu, và để thực hiện kỹ thuật này cũng khá khó khăn vì nó bị ràng buộc với kiểu mã hóa áp dụng cho website. Các bạn có thể tìm hiểu thêm về kỹ thuật này qua một số bài viết sau : - http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string - http://blog.bkis.vn/display/BSERESEARCH/2012/06/25/By+pass+addslashes()+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 :
Trong câu truy vấn trên, nếu column1 đã được setcollation là Unicode-UTF8 hay _latin1 chẳng hạn, thì những gì được select từ column2 sẽ phải được convert thành mã tương ứng. Ta có thể ép kiểu như sau :
Chúng ta có thấy nhược điểm trong cách bypass này là chúng ta phải biết được mã được collation là _latin1. Một cách bypass theo mình là tốt hơn đó là sử dụng hàm mã hóa và giải mã hex và unhex.
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
- Kiểm tra kiểu của dữ liệu : đối với dữ liệu kiểu số, kiểu chuỗi … chúng ta phải có những hàm validate tương ứng.