+11

NoSQL Injection $eq No SQL Injection? (Phần 1)

0. Khai bút đầu xuân

Bài viết đầu năm 2024, chúc toàn thể thành viên Viblo một năm mới thành công, may mắn và ngày càng đóng góp nhiều bài viết chất lượng cho cộng đồng.

I. Tổng quan về NoSQL

1. NoSQL là gì?

NoSQL là một thuật ngữ tổng quát được sử dụng để mô tả các hệ thống quản lý cơ sở dữ liệu (DBMS) không sử dụng mô hình quan hệ SQL (Structured Query Language) để tổ chức dữ liệu. Thay vì đó, NoSQL thường tập trung vào các mô hình lưu trữ dữ liệu linh hoạt hơn và có thể mở rộng tốt khi xử lý dữ liệu lớn và phân tán. Có nhiều loại NoSQL databases, bao gồm:

  • Cơ sở dữ liệu Key-Value (Ví dụ: Redis, DynamoDB): Lưu trữ dữ liệu dưới dạng cặp key-value, giúp truy xuất dữ liệu nhanh chóng.
  • Cơ sở dữ liệu Document (Ví dụ: MongoDB, CouchDB): Lưu trữ dữ liệu dưới dạng các tài liệu (document), thường sử dụng định dạng như JSON hoặc BSON.
  • Cơ sở dữ liệu Wide-Column Store (Ví dụ: Apache Cassandra, HBase): Dữ liệu được tổ chức thành các cột thay vì hàng, giúp tối ưu cho việc truy xuất dữ liệu theo cột.
  • Cơ sở dữ liệu Graph (Ví dụ: Neo4j, ArangoDB): Dùng để lưu trữ dữ liệu có mối quan hệ phức tạp, ví dụ như mạng lưới mối quan hệ giữa các đối tượng. image.png

Ví dụ, nếu bạn sử dụng MongoDB, bạn có thể lưu trữ thông tin về một người dùng trong một collection (tương đương với bảng trong SQL) dưới dạng tài liệu JSON như sau:

{
  "_id": ObjectId("5a934e000102030405000000"),
  "username": "example_user",
  "email": "user@example.com",
  "age": 25,
  "address": {
    "city": "Example City",
    "country": "Example Country"
  }
}

Ở đây, mỗi người dùng được biểu diễn bằng một tài liệu JSON với các trường khác nhau, và không có cấu trúc cố định cho mỗi người dùng.

2. NoSQL có những ưu điểm gì

NoSQL mang lại một số ưu điểm quan trọng cho các hệ thống lưu trữ và xử lý dữ liệu so với cơ sở dữ liệu quan hệ SQL truyền thống. Dưới đây là một số ưu điểm chính của NoSQL:

  • Khả năng Mở rộng Tốt: Các cơ sở dữ liệu NoSQL thường có khả năng mở rộng dễ dàng theo chiều ngang (horizontal scaling), tức là có thể thêm các node hoặc server mới để mở rộng hệ thống mà không làm ảnh hưởng đến hiệu suất.
  • Linh Hoạt về Schema: NoSQL thường hỗ trợ schema linh hoạt hoặc không có schema cứng nhắc, giúp ứng dụng thích ứng dễ dàng với sự thay đổi trong cấu trúc dữ liệu mà không yêu cầu sự can thiệp của DBA (Database Administrator).
  • Hiệu Suất Cao cho Các Truy vấn Cụ thể: Đối với các trường hợp sử dụng cụ thể, NoSQL có thể cung cấp hiệu suất cao hơn do tối ưu hóa cho các mô hình lưu trữ cụ thể (ví dụ: document-oriented, key-value store) và loại truy vấn mà họ hỗ trợ.
  • Hỗ Trợ cho Dữ Liệu Phi Cấu Trúc: NoSQL thích hợp cho việc lưu trữ và truy vấn dữ liệu phi cấu trúc hoặc có cấu trúc linh hoạt như JSON hoặc BSON, giúp đơn giản hóa việc làm việc với dữ liệu đa dạng và phức tạp.
  • Tương thích với Môi trường Đám mây: NoSQL thường tương thích tốt với môi trường đám mây và các kiến trúc phân tán, đặc biệt là khi cần mở rộng quy mô và tích hợp với các dịch vụ đám mây.
  • Giảm Bớt Chi phí về Dữ liệu Lớn:Với khả năng mở rộng tốt và sự linh hoạt, NoSQL có thể giúp giảm bớt chi phí của hệ thống xử lý dữ liệu lớn so với các cơ sở dữ liệu quan hệ truyền thống.
  • Dễ dàng tích hợp với Các Ngôn ngữ Lập trình Phổ biến: Cơ sở dữ liệu NoSQL thường có thư viện và driver hỗ trợ cho nhiều ngôn ngữ lập trình phổ biến, giúp dễ dàng tích hợp vào các ứng dụng.

3. Một số ví dụ về truy vấn NoSQL

Dưới đây là một số ví dụ về truy vấn NoSQL, sử dụng MongoDB làm cơ sở dữ liệu document-based. Đối với MongoDB, bạn có thể sử dụng ngôn ngữ truy vấn giống JSON, được gọi là BSON.

Truy vấn Tìm kiếm:

Tìm tất cả các người dùng có độ tuổi lớn hơn 21:


db.users.find({ "age": { $gt: 21 } })

Tìm người dùng có tên đăng nhập là "exampleuser":

db.users.find({ "username": "example_user" })

Truy vấn Thêm dữ liệu:

Thêm một người dùng mới:

db.users.insertOne({
  "username": "new_user",
  "email": "new_user@example.com",
  "age": 30,
  "address": {
    "city": "New City",
    "country": "New Country"
  }
})

Truy vấn Cập nhật dữ liệu:

Cập nhật độ tuổi của người dùng có tên đăng nhập là "exampleuser":

db.users.updateOne({ "username": "example_user" }, { $set: { "age": 26 } })

Truy vấn Xóa dữ liệu:

Xóa tất cả các người dùng có độ tuổi dưới 18:

db.users.deleteMany({ "age": { $lt: 18 } })

II. NoSQL Injection

1. NoSQL Injection là gì?

Quay trở lại câu hỏi ở tiêu đề bài viết. Vậy liệu NoSQL Injection có phải là No SQL Injection (không bị SQL Injection)? Câu trả lời là không đúng. Khi bạn sử dụng NoSQL, bạn vẫn có thể bị tấn công injection như bình thường.

NoSQL injection là một lỗ hổng xảy ra khi một kẻ tấn công có khả năng can thiệp vào các truy vấn mà một ứng dụng thực hiện đến cơ sở dữ liệu NoSQL. NoSQL injection có thể cho phép kẻ tấn công:

  • Vượt qua các cơ chế xác thực hoặc bảo vệ.
  • Trích xuất hoặc chỉnh sửa dữ liệu.
  • Tấn công từ chối dịch vụ.
  • Thực thi mã tùy ý trên máy chủ.

image.png

(Nguồn: https://knowledge-base.secureflag.com/vulnerabilities/nosql_injection/nosql_injection_vulnerability.html)

Có hai loại NoSQL injection khác nhau:

Syntax injection - Lỗ hổng này xảy ra khi bạn có thể phá vỡ cú pháp truy vấn NoSQL, cho phép bạn chèn vào payload của mình. Phương pháp này tương tự như trong SQL injection. Tuy nhiên, tính chất của cuộc tấn công khác nhau đáng kể, vì cơ sở dữ liệu NoSQL sử dụng nhiều ngôn ngữ truy vấn, loại cú pháp truy vấn khác nhau và cấu trúc dữ liệu khác nhau.

Operator injection - Lỗ hổng này xảy ra khi bạn có thể sử dụng các toán tử truy vấn NoSQL để thao tác truy vấn.

2. NoSQL syntax Injection

Chúng ta có thể kiểm tra lỗ hổng NoSQL injection có thể xảy ra bằng cách thử tìm cách thay đổi nhằm phá vỡ cú pháp truy vấn. Để làm điều này, chúng ta có thể fuzzing và ký tự đặc biệt có thể gây ra lỗi cơ sở dữ liệu. Chúng ta có thể thu thập thông tin ngôn ngữ API của cơ sở dữ liệu để từ đó sử dụng ký tự đặc biệt và chuỗi fuzz phù hợp với ngôn ngữ đó.

Phát hiện lỗ hổng syntax injection trong MongoDB

Giả sử có một ứng dụng mua sắm hiển thị sản phẩm trong các danh mục khác nhau. Khi người dùng chọn danh mục "fizzy", trình duyệt của họ yêu cầu URL sau:

https://insecure-website.com/product/lookup?category=fizzy

Điều này khiến ứng dụng gửi một truy vấn JSON để lấy các sản phẩm liên quan từ bộ sưu tập sản phẩm trong cơ sở dữ liệu MongoDB:

this.category == 'fizzy'

Để kiểm tra xem ứng dụng có thể bị lỗ hổng không, hãy gửi một chuỗi fuzz trong giá trị của tham số category. Một chuỗi mẫu cho MongoDB có thể là:

'"`{
;$Foo}
$Foo \xYZ

Sử dụng chuỗi fuzz này để tạo ra cuộc tấn công như sau:

https://insecure-website.com/product/lookup?category='%22%60%7b%0d%0a%3b%24Foo%7d%0d%0a%24Foo%20%5cxYZ%00

Khi chèn các ký tự đặc biệt, ứng dụng trả về response khác so với truy vấn ban đầu điều này có thể chỉ ra rằng đầu vào người dùng không được lọc hoặc làm sạch đúng cách.

Trong một số ứng dụng khác, chúng ta có thể cần chèn payload của mình thông qua một thuộc tính JSON. Trong trường hợp này, payload sẽ trở thành

'\"`{\r;$Foo}\n$Foo \\xYZ\u0000

a. Xác định ký tự nào được ứng dụng xử lý

Để xác định ký tự nào được ứng dụng hiểu là cú pháp, bạn có thể chèn từng ký tự một. Ví dụ, bạn có thể gửi ', dẫn đến truy vấn MongoDB như sau:

this.category == '''

Nếu điều này gây ra thay đổi so với phản hồi ban đầu, điều này có thể chỉ ra rằng ký tự ' đã làm hỏng cú pháp truy vấn và gây ra lỗi cú pháp. Bạn có thể xác nhận điều này bằng cách gửi một chuỗi truy vấn hợp lệ trong đầu vào, ví dụ như việc thoát khỏi dấu nháy đơn sử dụng \':


this.category == '\''

Nếu điều này không gây ra lỗi cú pháp, điều này có thể có nghĩa là ứng dụng có thể bị lỗ hổng NoSQL injection.

b. Kiểm tra điều kiện câu truy vấn

Sau khi phát hiện một lỗ hổng, bước tiếp theo là xác định liệu bạn có thể ảnh hưởng đến các điều kiện boolean bằng cú pháp NoSQL hay không.

Để kiểm thử điều này, gửi hai yêu cầu, một với điều kiện sai và một với điều kiện đúng. Ví dụ, bạn có thể sử dụng các câu lệnh điều kiện False: ' && 0 && 'xTrue: ' && 1 && 'x như sau:

https://insecure-website.com/product/lookup?category=fizzy'+%26%26+0+%26%26+'x
https://insecure-website.com/product/lookup?category=fizzy'+%26%26+1+%26%26+'x

Nếu ứng dụng trả về kết quả khác nhau, điều này có thể thấy rằng điều kiện sai ảnh hưởng đến logic truy vấn.

c. Ghi đè các điều kiện hiện tại

Sau khi bạn đã xác định bạn có thể ảnh hưởng đến điều kiện boolean, bạn có thể thử ghi đè các điều kiện hiện tại để lợi dụng lỗ hổng. Ví dụ, bạn có thể chèn một điều kiện luôn đúng, như '||1||':

https://insecure-website.com/product/lookup?category=fizzy%27%7c%7c%31%7c%7c%27

Điều này dẫn đến truy vấn MongoDB như sau:

this.category == 'fizzy'||'1'=='1'

Vì điều kiện được chèn luôn là đúng, truy vấn đã được sửa đổi trả về tất cả các mục. Điều này cho phép bạn xem tất cả các sản phẩm trong bất kỳ danh mục nào, bao gồm cả các danh mục ẩn hoặc không biết.

d. Sử dụng ký tự null

Chúng ta có thể sử dụng ký tự null (%00) vào câu truy vấn để kiểm tra xem ứng dụng có thực hiện xử lý ký tự này không.

Trong ví dụ trên, truy vấn MongoDB gốc bao gồm một điều kiện rằng sản phẩm phải thuộc danh mục 'fizzy' và phải đã phát hành (this.category == 'fizzy' && this.released == 1). Tuy nhiên, bằng cách chèn một ký tự null vào tham số danh mục (https://insecure-website.com/product/lookup?category=fizzy'%00), truy vấn NoSQL kết quả trở thành:

this.category == 'fizzy'\u0000' && this.released == 1

Nếu MongoDB thực sự bỏ qua tất cả các ký tự sau ký tự null, điều này hủy bỏ phần còn lại của truy vấn, kết quả là, tất cả sản phẩm trong danh mục 'fizzy' đều được hiển thị, kể cả những sản phẩm chưa được phát hành.

III. Demo tấn công NoSQL Syntack Injection

Bước 1: Truy vấn sản phẩm theo category https://0a6400e30407743b80624416008500a7.web-security-academy.net/filter?category=Gifts

Bước 2: Thêm ký tự đặc biệt ' để kiểm tra NoSQL Injection: image.png

Bước 3: Sử dụng truy vấn điều kiện để kiểm tra

  • Gifts' && 0 && 'x image.png Khi chèn điều kiện false, không có sản phẩm nào trả về

  • Gifts' && 1 && 'x image.png Khi chèn kết quả là true, trả về toàn bộ sản phẩm có trong category Gift

Bước 4: Chèn câu truy vấn điều kiện luôn đúng để trả về toàn bộ dữ liệu

Gifts'||1||'

image.png

Bước 5: Hoàn thành bài lab image.png

IV. Tổng kết

Ở phần 1, mình đã giới thiệu qua về NoSQL và cách sử dụng; Cách thức kiểm tra đơn giản một ứng dụng NoSQL có bị tấn công Injection hay không và một số phương pháp kiểm tra cũng như demo cách khai thác. Ở phần tiếp theo chúng ta sẽ tìm hiểu theo các cách thức tấn công khác cũng như cách phòng tránh lỗ hổng này. Mong các bạn đón đọc.

Tài liệu tham khảo:

https://portswigger.net/web-security/nosql-injection


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí