web渗透之jwt 安全问题
前言
JWT 全称 JSON Web Token,是一种标准化格式,用于在系统之间发送加密签名的 JSON 数据。原始的 Token 只是一个 uuid,没有任何意义。
JWT 包含了部分业务信息,减少了 Token 验证等交互操作,效率更高
JWT组成
JWT 由三部分组成,分别为:
Header — 头部
Payload — 负载
Signature — 签名
它们之间由三个 . 分隔,由 Base64 加密而来。我们以下面这一个 JWT 作为例子进行说明
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9 .eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm8sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ .SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA
Header 部分
Header 部分就像是货车的标志,告诉了我们这辆车上面装了什么易爆品啊,还是易燃品。
在 JWT 中 Header 部分存储的是 Token 类型和令牌的加密算法
{
"alg": "HS256", // 也可以是 HS512, RS256 等等
"typ": "JWT"
}
Payload 部分
Payload 这个词中文意思为负载,也就是我们大货车上面的货物。Payload 里面存具体的业务数据比如用户 id 等等。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature 部分
Signature 部分意为签名,这批货送到了,需要对方签收一下,这就是签名。Signature 是具体的数字签名密文信息,这部分的密文信息是手动设置的,一般在 Java 开发的配置文件当中设置。像这样
JWT 基础安全问题
JWT 所产生的安全问题,是设计上与逻辑上不恰当的设计所造成的。
其中,如果服务器未对签名进行校验,则可以任意修改数据,若进行了校验,则无法修改,需要我们通过其他手段进行辅助攻击。
1. 未对签名进行验证
前文我们说到,JWT 需要开发者提供一个 Signature — 签名,如果我们不对签名进行验证,那么就可能存在对令牌的其余部分内容进行任意更改的风险。
假设选择存在一个 JWT 如下
{
"username": "carlos",
"isAdmin": false
}
如果服务器基于此识别会话username,又未对签名进行认证,修改其值可能会使攻击者模拟其他登录用户。同样,如果isAdmin值用于控制是否为管理员,修改其值为true可能到导垂直越权。
PortSwigger 靶场
本实验室使用基于jwt的机制来处理会话。由于实现缺陷,服务器不会验证它接收到的任何jwt的签名。靶场要求:修改会话令牌以访问/admin的管理面板,然后删除用户carlos。
首先访问靶场: bypass-via-unverified-signature,点击右上角my account,使用提供的账号密码登录 wiener:peter
成功登录后返回了jwt认证信息
接下来每次请求都会携带认证信息
上面我们介绍了jwt中的第二部分payload存储了具体的业务数据如用户id等,我们直接在burp中选择jwt第二部分的数据然后进行base64解码查看,或者使用在线工具解码:https://jwt.io/。发现sub键的值为当然用户名wiener
那我将其改为administrator用户会怎么样了?如下修改后点击apply changes,然后cookie信息会被修改
对修改后的数据包重放,发现此时用户变为administrator,垂直越权成功
我们可以将修改后的jwt认证信息复制,然后使用“Cookie Quick Manager”插件将当前用户wiener的cookie修改为administrator的cookie,最后点击保存
然后刷新浏览器,页面显示为administrator,接着进入到管理员面板
最后删除carlos用户
2. 接受没有签名的令牌
其中,JWT头包含一个alg参数。这告诉服务器使用哪种算法对令牌进行签名,从而服务器也使用这种算法对签名进行验证。
{
"alg": "HS256",
"typ": "JWT"
}
jwt可以使用一系列不同的算法进行签名,但也可以不进行签名。通过设置 alg 为 none,绕过验证,从而实现攻击。
PortSwigger 靶场
地址:signature-verification
本实验室使用基于jwt的机制来处理会话。服务器被不安全地配置为接受无符号jwt。要解决这个问题,请修改会话令牌以访问/admin的管理面板,然后删除用户carlos。您可以使用以下凭证登录您自己的帐户:wiener:peter
首先像上一关一样尝试直接将sub值修改为administrator,发现无法认证成功
此时在将sub值修改为administrator的基础上,再将alg的值修改为none,并且删除jwt的第三部分但要保留引号,让其成为 JWT 的形式,因为 Signature 是通过 alg 算法生成的,修改了alg值 Signature也会变所以要将其删除。
此时已经是administrator用户了,然后去完成删除用户的操作
3. jwt秘钥爆破
一些签名算法,如HS256 (HMAC + SHA-256),使用简单的的字符串作为密钥,就很容易被暴力破解。
在jwt中的Signature部分,使用秘钥secret加密生成Signature,当通过暴力破解获取到了secret值,就能重新加密生成任意的用户的jwt
PortSwigger 靶场
地址:web-security-academy.net
登录后获取jwt,然后使用jwt_tool进行 Secret 爆破,字典:jwt.secrets.list
jwt_tool.py jwt -C -d 字典
#或者使用hashcat
hashcat -a 0 -m 16500 <jwt> <wordlist>
获取到了 Secret 为 "secret1",接着我们去到 jwt.io 下伪造administrator身份。如下填入,然后复制jwt替换原本的jwt
实现越权操作
4. jwt标头注入
根据JWS规范,只有alg标头参数是必需的。然而,在实践中,JWT报头(也称为何塞报头)通常包含几个其他参数。以下是攻击者特别感兴趣的。
jwk(JSON Web Key)——提供一个表示密钥的嵌入式JSON对象。
jku(JSON Web密钥集URL) -提供一个URL,服务器可以从该URL获取一组包含正确密钥的密钥。
kid(密钥ID) -提供一个ID,在有多个密钥可供选择的情况下,服务器可以使用该ID来识别正确的密钥。根据密钥的格式,这可能有匹配的kid参数。
正如您所看到的,这些用户可控制的参数都在告诉接收方服务器在验证签名时需使用哪个密钥。所以接下来将学习如何使用您自己的任意密钥去注入修改jwt签名而不是使用默认的服务器秘钥。
通过jwk头注入绕过JWT认证
jwk header中提供了一个可选的jwk参数,服务器可以使用该参数以JWK格式将其公钥直接嵌入令牌本身。JWK (JSON Web Key)是将键表示为JSON对象的标准化格式。
{
"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标题,从而生成新的jwt,实现jwt认证绕过。
先安装 Burpsuite 的 JWT Editor Keys插件,burp商店中自带。靶场地址:bypass-via-jwk-header-injection
点击我们安装的 JWT Editor Keys插件中的 New RSA Key,生成 默认2048 长度的 JWK 私钥,点击ok保存。
回到repeatter模块中,将sub值改为administrator,然后点击Attack,点击“EMbedded JWK”,选择新生成的RSA秘钥,点击ok
在JWT的标题中,可以看到jwk已添加包含您的公钥的参数,发送请求,服务器会使用jwk中的公钥进行解密,从而绕过认证。成功变为administrator用户
还有其他的一些注入,后续补充
JWT 安全问题的防护
使用最新的 JWT 库,虽然最新版本的稳定性有待商榷,但是安全性都是较高的。
对 jku 标头进行严格的白名单设置。
确保 kid 标头不容易受到通过 header 参数进行目录遍历或 SQL 注入的攻击。
参考:
https://www.freebuf.com/articles/web/337347.html
https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-jku-header-injection