WEB漏洞系列-JWT攻击手段考究

一、JWT简述

JWT(Json Web Token)是一种标准化格式,用于在系统之间发送经过加密签名的 JSON 数据,常用于系统身份认证、会话管理和访问控制机制。其格式主要由头信息(header)、消息体(payload)、签名(signature)3部分组成。

二、JWT格式

1、格式规则

如下为 JWT 例子,分为三个部分,第一步部分为头信息(header)、第二部分为消息体(payload)、第三部分为签名(signature),数据经过加密处理后通过符号 . 进行拼接后得到完整的JWT Token 值

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

利用 jwt.io 网站进行在线解密获取其信息,如下图:

image-20221022005803698

2、加密规则

第一部分:头信息(header),该部分定义了固定的算法为 “HS256”,token类型为 “JWT”,该部分 Json 数据需经过 base64url 加密后得到第一部分数据,以下为原始数据及加密处理后数据

1
{"alg":"HS256","typ":"JWT"}

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

image-20221022014816164

第二部分:消息体-payload,该部分主要为一些功能点的数据值,这一部分的 JSON 数据也需要进行 base64url 加密处理,以下为原始数据及加密处理后数据

1
{"sub":"1234567890","name":"John Doe","admin":true}

1
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

image-20221022015337482

第三部分:签名(signature),将前面第一、第二部分经过加密处理后的数据利用符号 . 拼接起来,然后再利用 “HS256” 加密算法进行加密得到第三部分加密数据,三部分通过符号 . 拼接后得到完整的 JWT Token 值,以下为原始数据及加密处理后数据

1
2
3
4
5
6
base64url(
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
your-256-bit-secret (秘钥加盐)
)
)

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

三、JWT 危害及攻击点

JWT 通常以伪造任意值创建自己的有效令牌,提升自己的权限或冒充其他用户,从而完全接管这些用户的账户,主要攻击点有以下几部分:

1
2
3
4
5
空算法验证
不验证签名内容分
密钥泄露&暴力破解密钥
密码混淆攻击
篡改 JWT Header,kid 指定攻击

四、靶场学习

在线靶场

1
https://jwt-lab.herokuapp.com/challenges

1、空算法验证

JWT 值 Header 中的 “alg” 参数用于声明加密算法,如果应用程序没有有效验证 “alg” 的值,那么我们可以将其值更改为 “none”,绕过签名验证

进入 jwt-lab 靶场,创建一个账号

image-20221022024849697
image-20221022024927517

点击进入 “None Algorithm” 靶场,利用刚才注册的账号进行登录,Burp 抓包

image-20221022025314222

看到 Cookie 处的 chanllenge 中的值使用的是 JWT 格式

image-20221022025516380

将前面两部分进行解密得加密算法为 “RS256”、用户名为 “woohoo”

image-20221022025952064

将加密算法改为 “none” 然后将 JWT Header 头部分进行 base64url 编码,最后替换原始的 JWT Header 头的第一部分利用 Burp 重放,若依然返回正确页面则存在漏洞,我们把 Result 中 eyJhbGciOiJub25lIn0= , “=” 去掉,这里编码后不应该出现,暂时先不管。

image-20221022031534531

image-20221022031706787

替换为 “none” 仍能正常访问,绕过了签名算法,存在 “空算法验证” 漏洞

image-20221022032053932

实战中可修改第二部分-消息体(payload)中的数据,配合该漏洞进行攻击,将第二部分的 “woohoo” 改为 “admin”,值为 eyJuYW1lIjoiYWRtaW4ifQ ,然后 Burp 放包

image-20221022032851198

修改后登录用户更改为 “admin”

image-20221022033129331

原始为 “woohoo”

image-20221022033318882

2、不验证签名内容

如果服务端没有对 JWT 签名部分进行校验,那么我们就可以直接删除最后的签名部分,使格式变为 {Header.Payload.},此时可以修改 Payload 内容为其他用户信息,伪造用户 token 值。这里直接利用 jwt_tool.py 工具进行验证,若证明存在漏洞再自行修改 payload 部分进行测试,也可直接删掉第三部分,若依然返回正确的页面则存在漏洞,这里没有必要利用这个工具去测试,直接删掉 JWT 值第三部分内容就行,这里为了引出这个工具,叭啦一下

image-20221022055718503

3、密钥泄露&暴力破解密钥

若JWT签名使用了 “HMAC” 算法(对称加密算法),则可对其 Key 进行爆破,若爆破出 key 值则可以伪造用户 token 值进行攻击。测试过程中如果发现存在密钥泄露的情况下则可直接使用工具重新生成 JWT 值,伪造数据,若没有则需要依靠字典,若 key 值为弱密钥则可进行下一步攻击。

进入 “Weak signature” 靶场,利用注册账号登录,获取到 challenge 中的 JWT 值

image-20221022060207246

通过解码得知使用了 “HS256” 算法,”HS256” 为 “HMAC” 算法的其中一种,为对称加密算法,即使用同一个密钥对JWT进行签名和认证,意味着也会对 JWT 进行签名的密钥也用于对其进行验证(注:对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文)

image-20221022060336778

这里使用 jwt_tool.py 工具进行测试 python3 jwt_tool.py jwt值,首先验证是否是可破解的密钥,发现是 “HS256”,可尝试进行 key 爆破

image-20221022061627075

爆破密钥 python3 jwt_tool.py jwt值 -C -d wordlists.txt

image-20221022062515948

利用爆破出来的 key 值对 jwt 进行重新签名

image-20221022062806245

image-20221022063014666

将生成的 jwt 值到 Burp 进行重放

image-20221022063145114


4、密码混淆攻击

当 JWT 签名算法使用 “RSA “ 加密算法时,RSA 是非对称加密算法,需要两个密钥,先用私钥加密生成 JWT ,然后使用其对应的公钥来解密验证。即假设我们能够将签名算法从 “RS256” 更改为 “HS256”,我们就可以强制应用程序仅使用一个密钥来完成加密和解密这两项任务,此时服务端代码将使用公钥作为密钥,然后使用HS256算法验证签名。(注:非对称加密算法需要两个密钥: 公开密钥 (publickey:简称公钥)和私有密钥(privatekey:简称私钥)。 公钥 与 私钥 是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。 因为加密和 解密 使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。)

进入 “Exposed Key” 靶场,利用注册账号登录,获取到 challenge 中的 JWT 值

image-20221022064158310

靶场已提供了公钥信息 “public key”(假装通过技术手段获取到泄露的公钥),将其复制保存为本地文件 public_key (注意:先将公钥复制到 sublime 删掉前面的空格;文件末尾还有一行空行;文件名没有后缀名)

image-20221022064011276

image-20221022071102736

这次就不去解码了,直接利用 jwt_tool.py 进行信息获取,加密方式为 “RS256” ,用户名为 “woohoo”

image-20221022064328897

利用获取到的公钥信息,使用 jwt_tool.py 工具,对 JWT 值使用 “HS256” 算法进行签名 python3 jwt_tool.py -X k -pk public_key jwt值

image-20221022071252972

将重新生成的 JWT 值到 Burp 重放,若返回值与,RS256 算法签名的 JWT 值一样的页面则证明存在漏洞

image-20221022071350414

进一步利用,修改 JWT 第二部分消息体(Payload),伪造 “admin” 用户 token 值

image-20221022071704133

python3 jwt_tool.py -X k -pk public_key eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiYWRtaW4ifQ==.

image-20221022071937771

成功伪造 admin 用户的 token 进行登录

image-20221022071907390

5、篡改 jwt header,kid指定攻击

密钥 ID (kid) 是Header中一个可选标头,即 key ID,用于指示文件系统或数据库中存在的特定密钥,然后使用其内容来验证签名

进入 “Vulnerable Kid “ 靶场,输入账户密码获取 JWT 值。解码后,得知存在 “kid”

image-20221022225037179

也可以直接使用 jwt_tool.py 进行解码

image-20221022225206954

将kid内容修改为 “/dev/null” ,并将 Payload 中 “name” 修改为 “admin” ,然后生成新的 token 。python3 jwt_tool.py jwt值 -I -hc kid -hv "/dev/null" -pc name -pv "admin" -S hs256

image-20221022225653680

生成后的 JWT 解码后为

image-20221022230209305

最后将生成的 token 到 Burp 放包,实现伪造 admin 身份

image-20221022225519054

若 kid 值没有存在过滤,有可能存在其它漏洞,如:任意文件读取、SQL注入、命令执行等,例子如下

1
2
3
4
5
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "../../etc/passwd"
}

1
2
3
4
5
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "admin' || union select 'secretkey' -- "
}

五、总结

经过学习发现,JWT 攻击面大多为伪造令牌从而提升权限或冒充其它用户,进而构建下一步攻击。总体上攻击点还是取决于 JWT 的加密方式、签名验证等是否安全。

参考文章

1
2
3
4
5
6
7
https://www.anquanke.com/post/id/225947
https://mp.weixin.qq.com/s/BP33m2Z5LeNcU2owGTSM2Q
https://mp.weixin.qq.com/s/puYElYPTz6zfEjND0jcm2g
https://simplycalc.com/base64url-encode.php
https://adamc95.medium.com/json-web-token-lab-guide-c402857fa44c
https://jwt.io/
https://base64.us/