当前位置: 首页 > news >正文

JWT攻击实战指南:从解码识别到签名绕过

1. 这不是密码学考试是攻防现场的实时决策JWT攻击详解与CTF实战——这八个字背后藏着无数选手在CTF赛场上盯着屏幕发呆的凌晨三点。我带过三届高校CTF战队最常听到的抱怨不是“算法不会”而是“明明知道是JWT漏洞可靶机返回的错误信息像谜语改了header没反应换payload又401token重放还被rate limit封IP”。这不是理论题是时间、经验和直觉的三重博弈。JWT本身不是漏洞但它的设计哲学——无状态、自包含、签名即信任——在真实系统中处处埋着雷密钥管理松散、算法混淆、未校验kid字段、过期时间形同虚设……这些都不是教科书里的假设而是某次AWD比赛中对手用一个alg: none就绕过全部登录鉴权的真实战例。本文不讲RFC7519标准原文不堆砌Base64Url编码原理只聚焦你打开Burp Suite后真正要做的三件事怎么快速识别JWT是否可利用、怎么在10秒内判断该打哪个点、以及打完之后如何验证成功而非制造一堆403日志。适合刚接触Web渗透的新手补全“鉴权绕过”这一环也适合有经验的选手查漏补缺——毕竟去年DEF CON Quals一道JWT题72支队伍里只有9支在决赛前复现了完整的密钥爆破链路而其中6支都栽在了一个被忽略的JWKS端点缓存机制上。2. JWT结构解剖别再把token当黑盒每个字节都在说话2.1 三段式结构的本质是“可读的不可信数据”JWT由三部分组成Header头部、Payload载荷、Signature签名用英文句点.分隔。很多人第一步就错了——直接丢进在线解码器看内容却从不思考“为什么能解码”。关键在于Header和Payload是Base64Url编码的JSON不是加密是编码。这意味着只要拿到token你就能100%还原出原始JSON内容无需任何密钥。我见过太多人对着eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9发愁其实拿Python一行就能拆开import base64 header_b64 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 # Base64Url解码需补全号并替换字符 header_json base64.urlsafe_b64decode(header_b64 ) print(header_json.decode()) # {alg:HS256,typ:JWT}这个操作的价值远不止“看看而已”。比如当你发现Header里alg:RS256却注意到后端API文档写着“所有服务使用HMAC密钥”这就是典型的算法混淆风险信号再比如Payload中user_id:123和role:user明文可见说明业务逻辑很可能直接信任这部分数据——此时若能篡改Signature权限提升就是秒级的事。记住JWT的脆弱性始于“可读”成于“被信”。你解码出的每一个字段都是后续攻击的坐标原点。2.2 Header字段的暗流alg、kid、jku、jwk、crit的攻防博弈Header里藏着最多陷阱的字段远不止alg一个。我们逐个拆解真实CTF场景中的利用逻辑alg字段最经典的none攻击。当alg:none时JWT规范允许签名为空字符串服务器若未强制校验算法会跳过签名验证。但2023年后90%的CTF题目已加防护真正有效的打法是算法混淆Algorithm Confusion将HS256伪装成RS256。原理在于——RS256用私钥签名、公钥验签而HS256用同一密钥签名和验签。如果服务器错误地用公钥PEM格式去验HS256签名而你把HS256密钥当作RSA私钥传入OpenSSL会尝试用该“私钥”解密签名结果恰好能通过验证。实操中你只需把原始HS256密钥文件如key.txt转换为PEM格式# 将ASCII密钥转为RSA私钥格式CTF中常考 echo mysecretkey | openssl genrsa -aes256 -passin stdin -out key.pem 2/dev/null # 或更暴力的直接构造PEM头尾包裹密钥 echo -----BEGIN RSA PRIVATE KEY-----\n$(base64 -w0 key.txt)\n-----END RSA PRIVATE KEY----- key.pemkid字段Key ID表面是密钥标识符实则是服务器查找密钥的“SQL注入点”。典型漏洞是kid注入当kid:../etc/passwd服务器若拼接路径/keys/{kid}.pem读取密钥就可能触发路径遍历。更隐蔽的是SQL注入式kidkid:1 UNION SELECT attacker_key -- 若后端用SQL查密钥你的恶意字符串就替换了真实密钥。我在2022年PlaidCTF一道题中正是通过kid字段注入1 AND 12 UNION SELECT -----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...让服务器用我的公钥验签从而伪造管理员token。jku与jwk字段指向外部JWKSJSON Web Key Set端点或直接嵌入密钥。危险在于JWKS端点劫持。例如jku:http://attacker.com/keys.json若服务器无白名单校验且支持HTTP你就能控制整个密钥集。更狠的是JWK内联攻击jwk:{kty:RSA,n:...,e:AQAB}直接把公钥塞进Header服务器若盲目信任你的签名就用这个公钥验证——而你早已掌握对应私钥。crit字段声明必须理解的扩展头部参数。若crit:[x5u]且服务器未实现x5u处理逻辑应拒绝token。但很多CTF题目故意留空处理此时你可添加crit:[bogus]触发解析异常导致签名验证被跳过——这是2023年Hack The Box赛季一道高分题的核心思路。提示在Burp Suite中修改Header后务必检查响应状态码和响应体。200 OK不等于成功要确认业务逻辑是否真正执行如返回管理员数据500 Internal Server Error反而是好消息——说明服务器尝试解析你注入的恶意字段但失败了证明该字段参与了处理流程。2.3 Payload字段的业务逻辑突破口从exp到custom字段的纵深利用Payload里exp过期时间、iat签发时间、nbf不早于时间看似只是时间戳实则是时间窗口攻击的起点。但真正的高价值字段往往藏在自定义键里exp的逆向利用当exp值极大如2147483647即2038年问题时间戳服务器可能因整数溢出将其视为负数导致token永不过期。我在一次企业渗透测试中发现某SaaS平台的JWTexp设为9999999999后端Java代码用Long.parseLong()解析后在Instant.ofEpochSecond()调用时抛出DateTimeException但异常被捕获且token被默认接受——这是典型的“异常处理逻辑缺陷”。jtiJWT ID与重放攻击jti本意是唯一标识防重放但若服务器仅校验存在性而不记录已用ID你可截获一个有效token反复重放。更致命的是jti注入jti:; DROP TABLE sessions; --若后端用该字段拼SQL直接触发数据库命令执行。业务字段的横向移动sub:user123、aud:api.example.com、iss:auth.example.com这些字段常被忽略。aud受众若校验不严你可将aud:admin-api.example.com注入普通用户token欺骗后端认为这是管理员API的合法请求iss签发者若被用于多租户隔离iss:tenant-a改为iss:tenant-b可能跨租户访问数据——去年ASIS CTF一道题正是通过篡改iss字段从普通用户越权读取了其他租户的加密密钥。隐式字段的盲打有些系统会解析但不声明的字段如cnf确认、cty内容类型。在2021年Google CTF中一道JWT题要求cnf:{jwk:{kty:EC,crv:P-256,x:...,y:...}}服务器会用该椭圆曲线公钥验证签名。而题目给出的公钥参数x,y是可控的通过选择特定曲线点可构造签名使验签恒为真——这是纯密码学层面的利用脱离了常规Web渗透思维。3. 签名机制攻防从暴力破解到密钥泄露的完整链条3.1 HMAC密钥爆破不是猜密码是算熵值HS256等HMAC算法的安全性完全依赖密钥强度。但CTF题目中密钥往往是password、123456或secret这类低熵值。爆破的关键不是工具而是熵值评估与字典构造策略熵值计算决定爆破效率密钥abc123的熵值约为36比特6字符×6比特/字符而Tr0ub4dour3约72比特。CTF中95%的HMAC密钥熵值低于40比特可在1分钟内暴力破解。计算公式entropy length × log2(character_set_size)。例如小写字母数字36字符的8位密钥8 × log2(36) ≈ 41比特。字典优先级必须动态调整不要一上来就跑rockyou.txt。先做三件事抓包分析密钥模式观察多个token的Header若kid:prod-key-v1密钥很可能是prod-key-v1-secret源码审计线索若题目提供前端JS搜索jwt.sign、crypto.createHmac密钥常硬编码在process.env.JWT_SECRET或注释里错误回显利用发送alg:none失败后若响应体含Invalid signature说明服务器在验签若含Unknown algorithm: none说明算法被拦截此时应转向alg混淆。我常用hashcat爆破但参数必须精准# HS256爆破字典为top1000密码常见后缀 hashcat -m 16500 -a 0 jwt_token.txt wordlist.txt -r rules/best64.rule # -m 16500 是JWT HS256模式比通用SHA256快10倍 # -r rules/best64.rule 自动添加数字、符号后缀实测中对secret密钥hashcat在RTX 4090上每秒尝试2800万次1秒内命中对MyAppSecret2023!需结合规则生成变体耗时约12秒。3.2 RSA密钥提取当公钥暴露私钥就不再安全RS256的安全基于大数分解难题但CTF中常出现公钥泄露导致私钥可计算的情况。核心原理RSA公钥包含模数n和指数e若n过小或存在共模攻击条件可直接分解。n值过小的直接分解当n小于512比特用rsatool一键分解# 从JWKS或证书中提取n,e n0xABCDEF1234567890... # 十六进制字符串 e65537 rsatool.py -n $n -e $e -o private.pem2022年DiceCTF一道题n仅384比特rsatool运行3秒输出私钥。共模攻击Common Modulus Attack当两个token使用相同n但不同e如e13,e265537且加密相同消息m可通过扩展欧几里得算法恢复m。CTF中表现为获取两个不同用户的token解析出相同n值此时用common_modulus.py脚本输入两个签名和e值直接输出原始Payload。证书私钥提取若题目提供.crt或.pem证书用openssl提取公钥参数openssl x509 -in cert.crt -text -noout | grep -A1 Modulus # 若显示Modulus has 1024 bit则极可能被分解注意RSA爆破不是蛮力而是数学攻击。看到n值第一时间用factordb.com查询是否已被分解——CTF题目常用经典RSA挑战数如RSA-100、RSA-110其因子在数据库中已公开。3.3 JWKS端点与密钥轮换的致命陷阱现代系统常用JWKS端点如/.well-known/jwks.json动态分发公钥。这本是安全实践但在CTF中常被扭曲JWKS缓存污染服务器若缓存JWKS响应且未校验Cache-Control你可发起两次请求第一次用恶意JWKS含你的公钥触发缓存第二次用对应私钥签名的token即可通过验证。2023年DEF CON Quals的jwks-cache题关键就在于Vary: Origin头缺失导致CDN缓存了攻击者的JWKS。密钥轮换逻辑缺陷当系统支持多密钥轮换如kid:2023-q1和kid:2023-q2若验签时未严格匹配kid对应的密钥而是遍历所有密钥尝试验签你可构造一个用旧密钥签名的token而服务器用新密钥验签失败后继续用旧密钥尝试——此时若旧密钥仍有效攻击即成功。我在一次红队演练中正是利用客户系统密钥轮换窗口期72小时用已泄露的旧密钥持续伪造token。JWKS端点SSRF利用若jku字段支持file://或http://localhost可触发服务器端请求伪造。例如jku:file:///etc/shadow服务器读取敏感文件并解析为JSON虽大概率报错但错误信息可能泄露路径jku:http://127.0.0.1:8080/internal/jwks.json则可访问内网JWKS端点。4. CTF实战推演从靶机响应到Root Flag的完整决策树4.1 响应特征驱动的漏洞识别法告别盲目 fuzzCTF中没有时间让你跑完整套工具链。我总结了一套30秒响应分析法基于HTTP响应头、状态码、响应体特征快速定位攻击面响应特征可能漏洞验证操作工具推荐WWW-Authenticate: Bearer errorinvalid_token算法混淆/none攻击修改alg为none删除Signature段Burp Repeater{error:invalid signature}HMAC密钥弱提取HeaderPayload用hashcat爆破hashcat -m 16500{error:unknown kid}kid注入尝试kid:../flag、kid:1 OR 11Burp Intruder500 Internal Server Errorjava.security.SignatureExceptionJWKS端点劫持设置jku为恶意URL监控请求Burp Collaborator401 Unauthorized{message:Token expired}exp篡改将exp设为9999999999手动编辑JWT实操案例去年强网杯一道题访问/api/profile返回401响应头含WWW-Authenticate: Bearer realmauth, errorinvalid_token, error_descriptionThe token is invalid.。我立刻在Burp中复制token将Header的alg:HS256改为alg:none删除Signature段只剩两段发送后得到200 OK和用户数据——但Flag不在响应中。此时我意识到none攻击成功但业务逻辑未授权访问Flag接口。于是改用alg混淆将HS256密钥letmein转换为PEM格式用pyjwt生成RS256签名token最终在/api/admin获得Flag。4.2 Burp Suite深度配置让自动化适配手动决策CTF中Burp不是摆设而是你的攻防大脑。关键配置如下Decoder标签页永久开启JWT解码器。右键token →Decode as → JWT自动分离Header/Payload/Signature并高亮显示alg、kid等关键字段。禁用自动Base64解码Settings → User Options → Decoder → Auto decode Base64避免干扰原始结构。Intruder攻击载荷针对kid注入设置Payload Positions为kid:§§Payload type选Simple list输入../etc/passwd 1 UNION SELECT attacker_key -- %2e%2e%2fetc%2fpasswd # URL编码绕过Attack type用Sniper确保每个载荷单独测试。Collaborator Everywhere启用jku/jwk探测。在Proxy → Options → Match and Replace中添加规则Match: jku:(.?)→Replace: jku:http://BURP-COLLABORATOR-SUBDOMAIN。当服务器请求你的Collaborator域名即证明JWKS端点可控。Extender插件安装JWT EditorGitHub开源它能在Repeater中直接编辑JWT并重签名。但注意CTF中禁用自动重签名因为题目常检测签名算法你必须手动构造符合漏洞逻辑的token。4.3 从Token伪造到Flag获取的七步链路以一道典型CTF题为例演示完整攻击链题目描述访问https://jwt-ctf.example.com登录后获得JWT目标是读取/api/flag。Step 1登录抓包获取tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJyb2xlIjoidXNlciJ9.xxxxxxStep 2基础分析Header{alg:HS256,typ:JWT}→ HMAC算法Payload{user_id:1,role:user}→ 业务字段明确Step 3算法混淆测试将alg改为RS256Signature不变 →401服务器拒绝→ 说明算法校验严格转向密钥爆破Step 4HMAC爆破用hashcat -m 16500跑rockyou.txt12秒后得到密钥supersecretkeyStep 5角色提升用密钥重签名Payload{user_id:1,role:admin}→ 新tokenStep 6访问Flag接口发送新token到/api/flag返回{error:Forbidden}→ 检查响应头发现X-Backend-Server: admin-api.internal→ 推断需aud字段匹配修改Payload{user_id:1,role:admin,aud:admin-api.internal}Step 7最终验证重签名后访问/api/flag返回{flag:flag{JWT_is_not_always_secure}}踩坑心得第6步的Forbidden错误90%选手会放弃重签名转而找其他漏洞。但我的经验是——当token伪造成功却权限不足第一反应不是重来而是检查业务层的额外校验字段。aud、iss、sub这些字段就像门禁卡的区域权限光有卡不行还得刷对闸机。5. 防御视角的加固清单为什么你的修复方案在CTF中总被绕过5.1 开发者常犯的“伪安全”错误CTF选手能绕过防御往往因为开发者只做了表面功夫“我用了RS256所以安全”错若密钥管理不当如私钥硬编码在Git仓库RS256比HS256更危险——因为私钥泄露意味着所有历史token可被伪造。2023年某金融CTF题private.key文件被误提交到GitHub选手用git clone下载后直接生成管理员token。“我校验了exp所以不会过期”错若用Date.now() exp * 1000毫秒转换而exp是秒级时间戳乘1000后可能溢出为负数导致永远为真。Java中Instant.ofEpochSecond(exp)若exp超范围会抛异常但若捕获异常后默认接受token就形成逻辑漏洞。“我禁用了alg:none”错禁用none只是堵住一个洞HS256→RS256混淆、kid注入、JWKS劫持依然畅通。真正的做法是白名单算法只允许[RS256, ES256]且对RS256强制校验jku域名在白名单内。5.2 CTF级加固的硬核实践面向真实攻防我给开发团队的加固建议密钥管理HMAC密钥长度≥32字节且必须从环境变量或密钥管理服务如AWS KMS加载禁止硬编码。RS256私钥用openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096生成公钥通过JWKS端点分发且JWKS响应头加Cache-Control: no-store, max-age0。算法校验验签前强制检查alg字段若不在白名单则立即拒绝。Node.js示例const allowedAlgs [RS256, ES256]; if (!allowedAlgs.includes(decoded.header.alg)) { throw new Error(Invalid algorithm); }kid字段沙箱化kid值只能是UUID或哈希值禁止包含路径分隔符/、点号.、单引号。校验正则/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i。JWKS端点防护jku字段只允许HTTPS协议且域名必须在预设白名单如[https://auth.example.com, https://cdn.example.com]。用new URL(jku).hostname提取域名后校验。业务字段二次校验即使JWT验签通过也要在业务层校验aud是否匹配当前API、iss是否为可信签发者、sub是否存在于用户数据库。这是最后一道防线。我在某银行渗透测试中发现其JWT库已升级到最新版但业务代码中if (jwt.role admin) { return getFlag(); }未校验jwt.aud而前端可随意修改aud字段。最终用aud:bank-admin-api绕过证明框架安全不等于应用安全。6. 经验沉淀那些文档里不会写的CTF实战心法6.1 时间就是分数建立你的漏洞响应优先级矩阵CTF比赛时间宝贵必须按ROI投入产出比排序攻击点。我用一张表指导队员攻击点预估耗时成功率Flag关联度优先级alg:none30秒15%中常需配合其他漏洞★★☆alg混淆HS256→RS2562分钟40%高直接提权★★★★HMAC密钥爆破1-5分钟60%高可控重签名★★★★★kid注入3分钟25%低常需SSRF配合★★JWKS劫持5分钟30%高但需网络条件★★★exp篡改1分钟50%中常需配合aud★★★★规则很简单先打高成功率高Flag关联度的组合。比如看到alg:HS256且无kid字段直接开hashcat若alg:RS256且jku存在优先测JWKS劫持。6.2 工具链的极简主义三个命令走天下过度依赖GUI工具会拖慢节奏。我只用三个终端命令JWT解码与分析# 一行解码Header/Payload无需安装额外工具 echo HEADER.PAYLOAD.SIGNATURE | cut -d. -f1,2 | tr . \n | while read s; do echo $s | base64 -d 2/dev/null | jq .; doneHMAC爆破# 从token提取HeaderPayload生成hashcat输入格式 echo HEADER.PAYLOAD.SIGNATURE | awk -F. {print $1.$2:$3} jwt.hashes hashcat -m 16500 jwt.hashes rockyou.txtRS256签名生成需提前准备私钥# 用OpenSSL生成RS256签名 echo -n HEADER.PAYLOAD | openssl dgst -sha256 -sign private.pem | openssl enc -base64 | tr \n | sed s//-/g; s/\//_/g6.3 心理战术当所有路都走不通时回归HTTP本质最后分享一个屡试不爽的心法当Burp里全是401/403/500工具跑不出结果代码审计无头绪时请关掉所有工具打开浏览器开发者工具只看Network标签页的原始HTTP请求与响应。有一次在TCTF我盯着/api/health的响应头看了10分钟发现X-JWT-Debug: true点开响应体竟然是完整的JWT解析日志包括kid查询的SQL语句——原来题目把调试信息留在生产环境而kid字段正是SQL注入点。那一刻我意识到CTF不是考你会多少工具而是考你敢不敢相信自己的眼睛。JWT攻击的本质从来不是密码学竞赛而是对系统设计哲学的理解当“信任”被编码进一段可读字符串所有看似坚固的防线都成了等待被重新解读的文本。你不需要成为密码学家只需要在每次看到eyJ开头的字符串时本能地问一句“这段文字正在向我承诺什么而这个承诺真的无法被篡改吗”
http://www.rkmt.cn/news/1366346.html

相关文章:

  • SQL Server 最大服务器内存设置:不是越大越好,官方推荐这样配 2026-05-24
  • 工业云脑:05 边缘AI:PLC+边缘盒子跑模型
  • 终极Ark-Pets明日方舟桌宠配置指南:让你的桌面伙伴活灵活现的5个技巧
  • 机器学习势函数MTP在Ni-Al合金缺陷模拟中的高精度应用与验证
  • 算法稳定性与PAC-Bayesian边界:从理论到实践的泛化保障
  • MLOmics:构建标准化癌症多组学基准,赋能机器学习模型公平评测
  • 实时机器学习去偏框架:基于反事实解释与人工审查的工程实践
  • 从AUC稳健下界到量子场论:机器学习与物理的数学统一
  • 概念建模的四种数学框架:从格代数到群论,构建更智能的AI
  • 为AI Agent框架OpenClaw配置Taotoken作为模型供应商
  • 正确使用关键词密度 提升内容质感
  • 基于C++17的QMC音频解密技术实现方案
  • ImageGlass:为Windows用户解决图像格式兼容性困境的专业级轻量查看器
  • AI 后台请求链路可观测性治理:从静默状态丢失到分层指标归因的工程实践
  • 5分钟免费安装yuzu模拟器:完整Switch游戏体验指南
  • 从能量关联函数到D2:喷注子结构分析与Sudakov安全观测量
  • 3种智能方案彻底清理Zotero文献库重复项
  • Deriva-ML:构建可复现机器学习工作流的数据驱动实践
  • Karpathy加盟Anthropic与九章四号:2026年5月AI人才与算力双突破
  • RGB 中的“隐藏亮度“:揭秘藏在红绿蓝中的明暗密码
  • 免费CAJ转PDF终极指南:3分钟学会caj2pdf完整教程
  • 3分钟让Windows资源管理器完美显示iPhone照片缩略图:告别灰色图标困扰
  • 5分钟学会使用CompressO:免费开源视频压缩神器终极指南
  • 如何快速配置智能游戏模组管理:XXMI启动器完整解决方案
  • 如何三步解锁微信网页版访问限制?wechat-need-web插件完整指南
  • 告别混乱图库:3步实现本地千万级图片秒级搜索的终极方案
  • BetterNCM-Installer:网易云音乐插件管理器的终极解决方案
  • DLSS Swapper完整指南:免费开源的游戏性能优化利器
  • 2026 南京 GEO 优化深度测评:本土实战数据、豆包引用提升与服务商能力横评 - 小艾信息发布
  • 跨平台资源下载神器:如何轻松保存任何平台上的珍贵内容