攻击JWT使用X509证书
1.1 设置
为了开始此攻击,我们需要一个易受攻击的应用程序进行测试。我为此在Flask中创建了一个易受攻击的API。
https://github.com/Tsynack/JWT_X509_Re-Signer/tree/main/Example%20API
图1 - 运行示例API
为了使测试更容易,我还构建了一个自定义的Burp扩展,允许修改头和声明。该扩展还可用于重新签名令牌。您可以在此处找到扩展:
https://github.com/Tsynack/JWT_X509_Re-Signer/tree/main/Burp%20Extension
注意: 该扩展依赖于在Burp的扩展设置中设置Jython。
要安装扩展,导航到Extensions > Add > Extension Type: Python > 并选择JWT_x509_Re-Sign.py文件。
图2 - 加载JWT重新签名器
一旦扩展加载,任何包含JWS的Burp HTTP历史或Repeater中的请求都应有一个标记为“重新签名JWT”的选项卡。
图3 - 扩展已安装
为了执行此攻击,我们最后需要的是一个X509私钥用于签名令牌,以及相关的证书,以便服务器可以验证签名。使用OpenSSL,可以通过以下命令完成:
|
|
1.2 攻击剖析
快速回顾一下,JWS包含三个部分:
- 头(Header) - 包含关于签名如何生成的详细信息,并可能包含JWS内容的详细信息
- 声明(Claims) - 包含关于发生的认证过程的详细信息。一些示例声明包括:
- Iat(签发时间) - JWS生成时的纪元时间
- Exp(过期时间) - 服务器不应再接受令牌的纪元时间
- Sub(主题) – 与令牌关联的用户
- 签名(Signature) - 声明与头中列出的算法结合生成签名。这确保令牌在服务器处理请求之前未被篡改。
图4 - 示例令牌结构
图5 - 示例头
图6 - 示例声明
在JWS的RFC中,概述了两个可选的头参数:
- x5c - 包含X509公钥证书或证书链
- x5u - 指向X509公钥证书或证书链的URI
公钥是服务器验证令牌签名并确定声明是否被篡改的方式。如果服务器使用这些参数的值来验证签名,我们有机会修改声明并用我们自己的密钥签名令牌。
1.3 概念验证
使用前面提到的示例API,我们将发送几个请求到Burp的Repeater以篡改令牌。在示例API文件夹中,我包含了一个API_example_requests.txt,其中包含用于生成登录和验证请求的curl命令。您应该能够复制并粘贴登录命令,并在响应中接收令牌。令牌需要复制到验证命令中。
图7 - API登录
图8 - API验证请求
如果一切正常,Burp的代理历史中应记录两个请求。右键单击/verify请求并将其发送到Repeater。
在Repeater中,导航到“重新签名JWT”选项卡。按下解码按钮将对令牌头和声明进行base64解码。对这些值的任何修改将在令牌重新签名后反映。首先将sub声明修改为root。
图9 - 解码和修改声明
对于第一次攻击,我们将公钥证书直接嵌入到x5c参数中。为此,您需要导入之前使用OpenSSL生成的X509私钥和证书。
图10 - 导入私钥和证书
由于我们将证书嵌入到令牌中,选择使用x5c头重新签名令牌并点击Attack!请求中的令牌将自动更新为新签名的令牌。发送请求应返回200 OK,表示更新后的令牌已被服务器接受。
图11 - 重新签名的令牌被服务器接受
如果您做到了这一步,恭喜您通过用自己的私钥签名令牌成功利用了漏洞!
1.4 攻击x5u
x5u头不是将证书内容直接嵌入到令牌中,而是将服务器指向可以找到内容的URI。为了利用这一点,我将cert.pem托管在我的Web服务器上,并将在扩展中的相应字段添加URL。
确保将重新签名的方法更新为使用x5u头,并点击Attack!当请求发送时,您托管证书的Web服务器应收到对cert.pem的请求,表明API在验证签名时已外部交互。
图12 - 访问日志显示外部交互
API响应应显示200 OK,表示签名和声明已被接受。
图13 - 利用x5u头
1.5 结论
对于每个已识别的头,问题是服务器信任提供的证书来验证令牌签名。作为攻击者,我们可以控制头的值,因此可以提供我们自己的证书。最终结果是一个被破坏的令牌,允许任何人更改声明中的值以冒充其他用户或执行权限提升。
作为开发者,检查您的令牌验证流程。即使您在签发令牌时未显式使用这些头,服务器是否会接受头值并根据提供的证书验证签名?实施代码以确保仅使用预期的证书来验证令牌签名。