目录
<1> 算法混淆攻击
当攻击者能够强制服务器使用与网站开发人员预期不同的算法来验证 JSON Web 令牌 (JWT)的签名时,就会发生算法混淆攻击(也称为密钥混淆攻击) 。如果这种情况处理不当,这可能使攻击者能够伪造包含任意值的有效 JWT,而无需知道服务器的秘密签名密钥
<2> 对称与非对称算法
JWT 可以使用一系列不同的算法进行签名。其中一些,例如 HS256 (HMAC + SHA-256) 使用“对称”密钥。这意味着服务器使用单个密钥来签署和验证令牌。显然,这需要保密,就像密码一样
其他算法,例如 RS256 (RSA + SHA-256) 使用“非对称”密钥对。这包括服务器用来签署令牌的私钥和可用于验证签名的数学相关公钥
顾名思义,私钥必须保密,但公钥通常是共享的,以便任何人都可以验证服务器发布的令牌的签名
<3> 算法混淆漏洞是如何产生的?
算法混淆漏洞通常是由于 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 和公钥对令牌进行签名,服务器将使用相同的公钥来验证签名.
<4> 执行算法混淆攻击
算法混淆攻击通常涉及以下高级步骤:
(1) 获取服务器的公钥
/jwks.json
例如,服务器有时会通过映射到or 的标准端点将其公钥公开为 JSON Web Key (JWK) 对象/.well-known/jwks.json
。这些可以存储在名为keys
. 这称为 JWK set
{
"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"
}
]
}
即使密钥没有公开公开,也可以从一对现有的里面提取
(2) 将公钥转换为合适的格式
尽管服务器可能会以 JWK 格式公开其公钥,但在验证令牌的签名时,它将使用其本地文件系统或数据库中自己的密钥副本。这可以以不同的格式存储。
为了使攻击起作用,您用于签署 JWT 的密钥版本必须与服务器的本地副本相同。除了格式相同之外,每个字节都必须匹配,包括任何非打印字符。
出于本示例的目的,假设我们需要 X.509 PEM 格式的密钥 , 我们可以使用burp中的 JWT 扩展将 JWK 转换为 PEM ,如下所示:
加载扩展后,在 Burp 的主选项卡栏中,转到JWT Editor Keys选项卡。
单击新建 RSA密钥。在对话框中,粘贴您之前获得的 JWK。
选择PEM单选按钮并复制生成的 PEM 密钥。
转到解码器选项卡并对 PEM 进行 Base64 编码。
返回JWT Editor Keys选项卡并单击New Symmetric Key。
在对话框中,单击生成以生成 JWK 格式的新密钥。
将生成的
k
参数值替换为您刚刚复制的 Base64 编码的 PEM 密钥。保存密钥。
(3) 修改您的 JWT
获得合适格式的公钥后,您可以随意修改JWT
(4) 使用公钥签署 JWT
使用 HS256 算法以 RSA 公钥作为secret 对令牌 Sign
Lab1 :通过算法混淆绕过 JWT 身份验证
一、获取服务器公钥
首先wiener:peter 登录,/my-account 抓一个包
转到标准端点/jwks.json
并观察服务器公开了一个包含单个公钥的 JWK set
从keys
数组内部复制 JWK 对象。确保您不会意外复制周围数组中的任何字符
{"kty":"RSA","e":"AQAB","use":"sig","kid":"89ec1811-fa1c-4808-96fa-8000670491e1","alg":"RS256","n":"_MvB0ckOd82gjns4Kq6Zk-Vw8L67p0AM69yQEwGqFrLFdklFwOOjEAXmSLNFbwX5--tpP5C1EjAH_21-RgDOYXjusrznz3FE3ca01g6G-L-YkdfOKYjxweoJps4IXyaXt0wOxs9Ip2vFHftoUZChz6WGR2LD1_HQOjhRSxqY2-88fdyLE9WhCmlL65zdPpi6nAjIK0DQsLWydRPaEbNmg3dyNbVeoX89Aw9NVkE9Z-1QW6xHBwZiPhkxD8SC194cib8Kr26T_p_ekNW2TVBvRZn1NTakYX9nHZmkzHVCjJWGEPNobN9c2v4VVFQKTWAIWOxWpSyC6DDiIGDg_jU65w"}
二、生成恶意签名密钥
去到JWT editor选项卡,点击New RSA Key 复制JWK set内容 保存
之后再右键我们新建的Key Copy Public Key as Pem
去Decoder选项卡对这个 PEM 密钥进行 Base64 编码,然后复制生成的字符串
再次回到Burp 主选项卡栏中 的JWT Editor Keys选项卡,点击New Symmetric Key Generate
将 k 属性的生成值替换为PEM Base64编码
三、修改并签署令牌
在 JWT 的标头中,将alg
参数的值更改为HS256
.
在有效负载中,将sub
声明的值更改为administrator
在选项卡底部,单击Sign,然后选择您在上一部分中生成的对称密钥 选择 Dont modify header
会发现点击ok 没有反应。
从别的视频评论区得知:linux 上对其进行了测试,linux下的burp可以,windows下的不行。。。。。
但是我们还可以用 jwt.io 来进行补全签名,将上面得到的base64编码后的pem 当作secret
构造好的JWT 放入cookie中,访问/admin 发现变为admin用户
访问/admin/delete?username=carlos 发包 删除carlos 完成任务
<5> 从现有令牌派生公钥
在公钥不可用的情况下,您仍然可以通过从一对现有 JWT 派生密钥来测试算法混淆。使用诸如jwt_forgery.py
同时还有
此工具的简化版本,您可以将其作为单个命令运行:
docker run --rm -it portswigger/sig2n <token1> <token2>
这使用您提供的 JWT 来计算n
. 不要太担心这意味着什么——您只需要知道其中只有一个与n
服务器密钥所使用的值匹配。对于每个潜在值,我们的脚本输出:
X.509 和 PKCS1 格式的 Base64 编码 PEM 密钥。
使用这些密钥中的每一个签名的伪造 JWT。
要识别正确的密钥,请使用 Burp Repeater 发送包含每个伪造 JWT 的请求。服务器只接受其中一个。然后,您可以使用匹配的密钥来构造算法混淆攻击
Lab2:通过没有暴露密钥的算法混淆绕过 JWT 身份验证
本实验使用基于 JWT 的机制来处理会话。它使用强大的 RSA 密钥对来签署和验证令牌。但是,由于实现上的缺陷,这种机制很容易受到算法混淆攻击。
解决实验室,首先获取服务器的公钥。使用此密钥签署修改后的会话令牌,使您可以访问管理面板
/admin
,然后删除用户carlos
hint:
服务器将其公钥存储为 X.509 PEM 文件
工具地址:GitHub - silentsignal/rsa_sign2n: Deriving RSA public keys from message-signature pairs
一、获取服务器生成的两个 JWT
登录wiener帐户并 登录后GET /my-account
请求 抓包发送到 Burp Repeater
复制您的 JWT 会话 cookie 并将其保存在某个地方以备后用
注销并重新登录,再次复制新的 JWT 会话 cookie 并保存。您现在有两个由服务器生成的有效 JWT
二、暴力破解服务器的公钥
在终端中,运行以下命令,将两个 JWT 作为参数传入
docker run --rm -it portswigger/sig2n <token1> <token2>
输出包含 的一个或多个计算值n
。这些中的每一个在数学上都是可能的,但只有一个与服务器使用的值匹配。在每种情况下,输出还提供以下内容:
- X.509 和 PKCS1 格式的 Base64 编码公钥
- 使用这些密钥中的每一个签名的篡改 JWT
从第一个 X.509 条目中复制被篡改的 JWT(您可能只有一个)
然后用这个新的 JWT 替换会话 cookie,然后发送请求。
- 如果您收到 200 响应并成功访问您的帐户页面,那么这是正确的 X.509 密钥
- 如果您收到 302 响应,将您重定向到
/login
并删除您的会话 cookie,那么这是错误的 X.509 密钥。在这种情况下,对脚本输出的每个 X.509 密钥使用篡改的 JWT 重复此步骤
三、生成恶意签名密钥
在您的终端窗口中,复制您在上一节中确定为正确的 Base64 编码的 X.509 密钥。请注意,您需要选择密钥,而不是您在上一节中使用的被篡改的 JWT
去 JWT editor中 New Symmetric Key 更改k为正确的 Base64 编码的 X.509 密钥。
四、修改并签署令牌
回到repeater中用插件打开,更改sub为administrator 选中我们新生成的对称密钥,Sign
:由于是windows环境下的burp,点击ok没有反应 我们可以用JWT.io 网站来帮助进行签名
把构造好的JWT放到cookie中,访问/admin 成功改为admin身份
再次访问 /admin/delete?username=carlos 删除carlos用户 完成任务
至此 portswigger JWT关卡结束。经过这几天的学习对JWT相关知识增加很多,基本上是构造签名,越权访问,操作。但是感觉自己对密钥算法这方面原理还不是很清晰,后面遇见相应的情况继续学习。