目录
1.利用有缺陷的 JWT 签名验证
①接受任意签名
lab1:通过未验证的签名绕过 JWT 身份验证
②接受无签名的token
lab2:通过有缺陷的签名验证来绕过 JWT 身份验证
2.暴力破解密钥
①使用hashcat
lab3:通过弱签名密钥绕过 JWT 身份验证
3.JWT 标头参数注入
①通过 jwk 参数注入自签名 JWT
lab4:通过 jwk 标头注入绕过 JWT 身份验证
②通过 jku 参数注入自签名 JWT
lab5:通过 jku 标头注入绕过 JWT 身份验证
③通过 kid 参数注入自签名 JWT
lab6:通过 kid 头路径遍历绕过 JWT 身份验证
4.算法混淆攻击
算法混淆漏洞是如何产生的?
①执行算法混淆攻击
lab7:通过算法混淆绕过 JWT 身份验证
②从现有jwt派生公钥
lab8:通过算法混淆且无暴露密钥来绕过 JWT 身份验证
1.利用有缺陷的 JWT 签名验证
根据设计,服务器通常不会存储有关其发出的 JWT 的任何信息。相反,每个令牌都是一个完全独立的实体。这有几个优点,但也带来了一个根本问题 - 服务器实际上不知道令牌的原始内容,甚至不知道原始签名是什么。因此,如果服务器没有正确验证签名,就无法阻止攻击者对令牌的其余部分进行任意更改。
例如,考虑包含以下声明的 JWT:
{ "username": "carlos", "isAdmin": false }
如果服务器根据此 来识别会话username
,则修改其值可能会使攻击者能够冒充其他登录用户。同样,如果该isAdmin
值用于访问控制,则可能为特权升级提供一个简单的payload。
①接受任意签名
JWT 库通常提供一种验证令牌的方法和另一种仅解码令牌的方法。例如,Node.js 库jsonwebtoken
有verify()
和decode()
。
有时,开发人员会混淆这两种方法,只将传入的令牌传递给该decode()
方法。这实际上意味着应用程序根本不验证签名。
lab1:通过未验证的签名绕过 JWT 身份验证
wiener:peter登录
抓包拿到jwt
用JSON WEB TOKEN插件修改sub为administrator
拿着新生成的jwt访问./admin
删除carlos
②接受无签名的token
除其他事项外,JWT 标头还包含一个alg
参数。该参数会告诉服务器使用哪种算法对令牌进行签名,以及在验证签名时需要使用哪种算法。
{ "alg": "HS256", "typ": "JWT" }
这本身就存在缺陷,因为服务器别无选择,只能隐式地信任来自令牌的用户可控制的输入,而此时令牌根本没有经过验证。换句话说,攻击者可以直接影响服务器检查令牌是否可信的方式。
JWT 可以使用多种不同的算法进行签名,但也可以不签名。在这种情况下,alg
参数设置为none
,表示所谓的“不安全的 JWT”。由于这种做法的明显危险,服务器通常会拒绝没有签名的令牌。但是,由于这种过滤依赖于字符串解析,因此有时您可以使用经典的混淆技术(例如混合大小写和意外编码)绕过这些过滤器。
lab2:通过有缺陷的签名验证来绕过 JWT 身份验证
和上题一样,登录后用插件改jwt
将alg设置为none,sub设置为administrator
空算法伪造攻击后带着jwt去删除用户
2.暴力破解密钥
某些签名算法(例如 HS256 (HMAC + SHA-256))使用任意独立字符串作为密钥。就像密码一样,这个密钥不能被攻击者轻易猜出或暴力破解,这一点至关重要。否则,他们可能能够使用他们喜欢的任何标头和有效负载值创建 JWT,然后使用该密钥通过有效签名重新签署令牌。
在实施 JWT 应用程序时,开发人员有时会犯一些错误,例如忘记更改默认或占位符密钥。他们甚至可能复制并粘贴在线找到的代码片段,然后忘记更改作为示例提供的硬编码密钥。在这种情况下,攻击者可以轻而易举地使用众所周知的密钥词表来暴力破解服务器的密钥。
①使用hashcat
lab3:通过弱签名密钥绕过 JWT 身份验证
登录后用hashcat拿下面这个字典爆密钥
jwt-secrets/jwt.secrets.list at master · wallarm/jwt-secrets · GitHub
hashcat -a 0 -m 16500 eyJraWQiOiI2MmQzMGZiMC02NTk4LTQzMGYtODBlMi1hY2NkNGYzZTRmMjgiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcyNzg1ODA5Mywic3ViIjoid2llbmVyIn0.463sXDPLcre6npFHccQwSmCGSYgCdKvopKMAXRR9hvQ ./jwt.secrets.list
爆出来密钥是secret1
直接jwt.io改了
带着修改后的jwt删除用户
3.JWT 标头参数注入
根据 JWS 规范,只有alg
标头参数是强制性的。但实际上,JWT 标头(也称为 JOSE 标头)通常包含几个其他参数。以下参数对攻击者特别感兴趣。
-
jwk
(JSON Web 密钥)——提供一个代表密钥的嵌入式 JSON 对象。 -
jku
(JSON Web 密钥集 URL)——提供一个 URL,服务器可以从中获取一组包含正确密钥的密钥。 -
kid
(密钥 ID)- 提供一个 ID,当有多个密钥可供选择时,服务器可以使用它来识别正确的密钥。根据密钥的格式,这可能有一个匹配的kid
参数。
①通过 jwk 参数注入自签名 JWT
JSON Web 签名 (JWS) 规范描述了一个可选的jwk
标头参数,服务器可以使用它来将其公钥以 JWK 格式直接嵌入令牌本身中。
JWK(JSON Web Key)是一种将密钥表示为 JSON 对象的标准化格式。
可以在以下 JWT 标头中看到此示例:
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
理想情况下,服务器应仅使用有限的公钥白名单来验证 JWT 签名。但是,配置错误的服务器有时会使用jwk
参数中嵌入的任何密钥。
您可以使用自己的 RSA 私钥对修改后的 JWT 进行签名,然后将匹配的公钥嵌入到jwk
标头中,从而利用此行为。
jwk
虽然您可以在 Burp 中 手动添加或修改参数,但 JWT Editor 扩展提供了一个有用的功能来帮助您测试此漏洞:
加载扩展后,在 Burp 的主选项卡栏中,转到JWT 编辑器键选项卡。
生成新的 RSA 密钥。
向 Burp Repeater 发送包含 JWT 的请求。
在消息编辑器中,切换到扩展生成的JSON Web Token选项卡并根据需要修改令牌的有效负载。
单击攻击,然后选择嵌入式 JWK。出现提示时,选择新生成的 RSA 密钥。
发送请求来测试服务器如何响应。
您也可以通过自己添加jwk
标头来手动执行此攻击。但是,您可能还需要更新 JWT 的kid
标头参数以匹配kid
嵌入密钥的。扩展的内置攻击会为您完成此步骤。
lab4:通过 jwk 标头注入绕过 JWT 身份验证
先new一个RSA Key
将sub改为administrator
嵌入jwk
选择刚生成的密钥
再请求./admin/delete?username=carlos,成功删除
②通过 jku 参数注入自签名 JWT
一些服务器不直接使用标头参数嵌入公钥jwk
,而是允许您使用jku
(JWK Set URL) 标头参数来引用包含密钥的 JWK Set。验证签名时,服务器会从此 URL 获取相关密钥。
JWK Set 是一个 JSON 对象,其中包含代表不同键的 JWK 数组。您可以在下面看到一个示例。
{ "keys": [ { "kty": "RSA", "e": "AQAB", "kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab", "n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ" }, { "kty": "RSA", "e": "AQAB", "kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA", "n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw" } ] }
此类 JWK 集有时会通过标准端点公开展示,例如/.well-known/jwks.json
。
更安全的网站只会从受信任的域获取密钥,但有时您可以利用 URL 解析差异来绕过这种过滤。我们在SSRF主题中介绍了一些此类示例。
lab5:通过 jku 标头注入绕过 JWT 身份验证
给了一个攻击的服务器
先写入body:
{
"keys" : [
]
}
再新建一个密钥,Copy Public Key as JWK
粘进keys里
{
"keys" : [{
"kty": "RSA",
"e": "AQAB",
"kid": "678de895-1caf-4d6d-bd1a-0c86dc31d294",
"n": "kOwcnjPnbmUvDtzgBwz9xkpwNWnXmyo7KWHZhiJM7ADUTJQl2ES6MuT_iPR-FL3mNl_euTGhpnYCmSUVJ0_cBh77RbaoULETtBEDE_InuXsQaB-CKtgTolXw8WysUq8qFeT35l6E5m9wsSFk5lQdAJTo8CbyOI4mUhQagQ-DMCxEuOmZphbt7IJL3uZSM5bxm8ZaxU5ITDUpBWuY8VA2ddVILHwpYVBUlIokVESnjH_AlPM-3bzt2idN2JhVd_8i4b88_BXjpqfTg0LtjUwFnU_fWj06VkFxDFaWqpzSnMROK0x06H8a73fHaCJalQiMLRQkY_CETXmP1ZKEI4toZQ"
}
]
}
Store后拿到url:https://exploit-0aa200a604c5e3628381148401a8003f.exploit-server.net/exploit
插件里修改sub为administrator,kid为恶意服务器存的kid,jku为恶意服务器地址,最后再用填入的公钥sign一下
带着伪造的jwt成功删除用户
③通过 kid 参数注入自签名 JWT
服务器可能会使用多个加密密钥来签署不同类型的数据,而不仅仅是 JWT。因此,JWT 的标头可能包含kid
(Key ID) 参数,这有助于服务器识别在验证签名时要使用哪个密钥。
验证密钥通常存储为 JWK 集。在这种情况下,服务器可能只是查找与kid
令牌相同的 JWK。但是,JWS 规范并未为此 ID 定义具体的结构 - 它只是开发人员选择的任意字符串。例如,他们可能使用该kid
参数指向数据库中的特定条目,甚至是文件的名称。
如果此参数也容易受到目录遍历的攻击,攻击者可能会强迫服务器使用其文件系统中的任意文件作为验证密钥。
{ "kid": "../../path/to/file", "typ": "JWT", "alg": "HS256", "k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc" }
如果服务器还支持使用对称算法签名的 JWT,则这尤其危险。在这种情况下,攻击者可能会将kid
参数指向可预测的静态文件,然后使用与该文件内容匹配的密钥对 JWT 进行签名。
理论上,你可以对任何文件执行此操作,但最简单的方法之一是使用/dev/null
,它存在于大多数 Linux 系统上。由于这是一个空文件,读取它会返回一个空字符串。因此,使用空字符串对令牌进行签名将产生有效的签名。
如果您使用的是 JWT 编辑器扩展,请注意,这不允许您使用空字符串对令牌进行签名。但是,由于扩展中的一个错误,您可以使用 Base64 编码的空字节来解决这个问题。
lab6:通过 kid 头路径遍历绕过 JWT 身份验证
登录后将拿到的jwt扔到jwt.io中
令kid为../../../../../../../dev/null,改sub为administrator,最后用AA==(base64解码后为空)作为签名密钥
最后带着伪造的jwt删除指定用户
4.算法混淆攻击
算法混淆攻击(也称为密钥混淆攻击)是指攻击者能够强制服务器使用与网站开发人员预期不同的算法来验证 JSON Web 令牌 ( JWT ) 的签名。如果处理不当,攻击者可能无需知道服务器的秘密签名密钥即可伪造包含任意值的有效 JWT。
算法混淆漏洞是如何产生的?
算法混淆漏洞通常是由于 JWT 库的实现存在缺陷而引起的。尽管实际验证过程因所用算法而异,但许多库都提供了一种与算法无关的签名验证方法。这些方法依赖于alg
令牌标头中的参数来确定应执行的验证类型。
以下伪代码展示了此通用verify()
方法在 JWT 库中的声明的简化示例:
function verify(token, secretOrPublicKey){ algorithm = token.getAlgHeader(); if(algorithm == "RS256"){ // Use the provided key as an RSA public key } else if (algorithm == "HS256"){ // Use the provided key as an HMAC secret key } }
当网站开发人员随后使用此方法时,他们会假设该方法将专门处理使用 RS256 等非对称算法签名的 JWT,这时就会出现问题。由于这个错误的假设,他们可能总是将一个固定的公钥传递给该方法,如下所示:
publicKey = <public-key-of-server>; token = request.getCookie("session"); verify(token, publicKey);
在这种情况下,如果服务器收到使用对称算法(如 HS256)签名的令牌,则库的通用verify()
方法会将公钥视为 HMAC 密钥。这意味着攻击者可以使用 HS256 和公钥对令牌进行签名,服务器将使用相同的公钥来验证签名。
①执行算法混淆攻击
lab7:通过算法混淆绕过 JWT 身份验证
访问./jwks.json拿到公钥
复制jwk set内容转为pem格式
Copy Public Key as PEM
将PEM内容base64编码一下作为签名密钥
将登录后拿到的jwt扔到jwt.io中进行伪造
alg改为HS256,sub改为administrator,密钥改为base64编码后的PEM
带着伪造的jwt删除指定用户
②从现有jwt派生公钥
lab8:通过算法混淆且无暴露密钥来绕过 JWT 身份验证
先登录两次分别拿到两个jwt
eyJraWQiOiI0ZjhhNjczOS1kMGIyLTQ4M2UtYmYxNy1mNjg0OWYzOGQ1ZGEiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcyNzg3NjUyNywic3ViIjoid2llbmVyIn0.NOALNzWOAKHKrnp0Rd5DnlCDQCUBRr-KxJsVC_AchALBgyOP9-UELraOADNtwSvfjIKpvfLOladrfEoSaS7UtXtIulSOJzqPjlJzbiFLl3-JN94C0CP8jmbk9Iy3WNHb0_ZorQOvl5Wy-xu4fxDG0FP76p98N7vjbnrcO77xC6QK5J2tMBzi_B9THX8T9YlIZx_U-Zcu5tHhJ5NkZloKcGFjYdFT-qB1Pn7xL8V6GiPzZf8OARIU-HiDPFLBOiBEfGHkWqdElMTkvUlWYV--mWg2nfqt64VMQVWSQhd3p4fsVmc6YgLtn_fQ5NYz96xUqh0W0NSV0a3d_c5uhyP-3A
eyJraWQiOiI0ZjhhNjczOS1kMGIyLTQ4M2UtYmYxNy1mNjg0OWYzOGQ1ZGEiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcyNzg3NjkwNCwic3ViIjoid2llbmVyIn0.nB0ldR1KMOFnuDRNCLEUE94APyQoO0Y0WKbcSHTcD-TT0-ceYiWX8w1vCE3KhoGrjw_753vLMZ2U6n9C74HpHKmVmIoNYSlv1o1qIyuRSYuzcVx1rUA8SPriFWKwqQzBgPoOMBPNSI_57n4ck4AuVMwV7XGVbUkZayaaO5sTixjOheha6DOBsUuCuLQ6TjGpBcMxm1qpPwS3cuSMssrTSMzQ4d672q1QPhDVQz9n9Tkz8bmZKYOC7VPRqLvibXq5BOH5Qs6-33Y97_e-5IQlyLI9w5nh0QpfsaujgZkGsDJFL342qdIpbLPiiKeZ6KiuCXSS5zaeRHI9-y-E9axKfg
爆破服务器公钥
docker run --rm -it portswigger/sig2n eyJraWQiOiI0ZjhhNjczOS1kMGIyLTQ4M2UtYmYxNy1mNjg0OWYzOGQ1ZGEiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcyNzg3NjUyNywic3ViIjoid2llbmVyIn0.NOALNzWOAKHKrnp0Rd5DnlCDQCUBRr-KxJsVC_AchALBgyOP9-UELraOADNtwSvfjIKpvfLOladrfEoSaS7UtXtIulSOJzqPjlJzbiFLl3-JN94C0CP8jmbk9Iy3WNHb0_ZorQOvl5Wy-xu4fxDG0FP76p98N7vjbnrcO77xC6QK5J2tMBzi_B9THX8T9YlIZx_U-Zcu5tHhJ5NkZloKcGFjYdFT-qB1Pn7xL8V6GiPzZf8OARIU-HiDPFLBOiBEfGHkWqdElMTkvUlWYV--mWg2nfqt64VMQVWSQhd3p4fsVmc6YgLtn_fQ5NYz96xUqh0W0NSV0a3d_c5uhyP-3A eyJraWQiOiI0ZjhhNjczOS1kMGIyLTQ4M2UtYmYxNy1mNjg0OWYzOGQ1ZGEiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTcyNzg3NjkwNCwic3ViIjoid2llbmVyIn0.nB0ldR1KMOFnuDRNCLEUE94APyQoO0Y0WKbcSHTcD-TT0-ceYiWX8w1vCE3KhoGrjw_753vLMZ2U6n9C74HpHKmVmIoNYSlv1o1qIyuRSYuzcVx1rUA8SPriFWKwqQzBgPoOMBPNSI_57n4ck4AuVMwV7XGVbUkZayaaO5sTixjOheha6DOBsUuCuLQ6TjGpBcMxm1qpPwS3cuSMssrTSMzQ4d672q1QPhDVQz9n9Tkz8bmZKYOC7VPRqLvibXq5BOH5Qs6-33Y97_e-5IQlyLI9w5nh0QpfsaujgZkGsDJFL342qdIpbLPiiKeZ6KiuCXSS5zaeRHI9-y-E9axKfg
带着第一个Tampered JWT访问主页发现成功登录,说明公钥正确
jwt.io中伪造,alg改为HS256,sub改为administrator,签名密钥改为脚本爆破出的Base64 encoded x509 key
带着伪造的jwt删除指定用户