JWT安全漏洞实战:从算法混淆到密钥爆破的靶场通关指南
1. 项目概述从JWT到靶场实战如果你正在学习Web安全尤其是认证与授权相关的漏洞那么JWTJSON Web Token绝对是一个绕不开的核心知识点。它广泛应用于现代Web应用和API的认证流程从单点登录到微服务间的通信随处可见它的身影。但正是这种广泛性使得围绕JWT的安全问题变得至关重要。CTFHub作为国内知名的CTFCapture The Flag技能训练平台其JWT漏洞靶场提供了一个绝佳的实战环境让你能在安全的沙箱里亲手触发、分析和利用各种经典的JWT安全漏洞。这篇教程的目的就是带你从零开始手把手通关这个靶场不仅告诉你“怎么做”更深入剖析每个漏洞背后的“为什么”让你真正理解JWT的安全机制与攻防博弈。这个靶场覆盖了JWT从基础概念到高阶攻击手法的多个层面非常适合有一定Web基础比如了解HTTP协议、Cookie、基本的加密概念并希望深入安全领域的学习者。通过它你能系统性地掌握JWT的结构解析、密钥混淆攻击、签名绕过、密钥爆破、算法操纵如none算法攻击以及基于KIDKey ID和JKUJWK Set URL的参数注入等核心漏洞。我会结合靶场中的每一道题目拆解其原理给出可复现的详细步骤并分享我在实际测试和教学过程中总结的排查技巧与避坑指南。准备好了吗我们直接进入正题。2. JWT核心机制与安全基础解析在动手之前我们必须先打好地基彻底理解JWT是什么以及它为何会引入安全风险。很多初学者一上来就照着Payload打却不知其所以然遇到变种或修复后的环境就束手无策了。2.1 JWT的三段式结构与工作原理一个JWT令牌看起来像这样xxxxx.yyyyy.zzzzz它由三个用点分隔的Base64Url编码部分组成分别是Header头部、Payload载荷和Signature签名。Header通常包含令牌类型typ: “JWT”和所使用的签名算法alg例如HMAC SHA256HS256或RSA SHA256RS256。它指明了如何验证后面的签名。{ “alg”: “HS256”, “typ”: “JWT” }编码后成为第一部分。Payload包含了所谓的“声明”Claims即我们要传递的信息。声明分三种预注册的声明如iss签发者、exp过期时间、公共声明和私有声明。在靶场中我们经常需要篡改的就是这里的user或admin等字段来提升权限。{ “sub”: “1234567890”, “name”: “John Doe”, “admin”: false }编码后成为第二部分。Signature是整个JWT安全性的核心。签名是为了验证消息在传递过程中没有被篡改并且对于拥有密钥的签发者来说是可信的。生成签名的伪代码如下HMACSHA256( base64UrlEncode(header) “.” base64UrlEncode(payload), secret)对于RS256等非对称算法则是使用私钥签名公钥验证。服务器在收到JWT后会使用相同的密钥或对应的公钥重新计算签名并与令牌中的第三部分进行比对。如果一致则信任Payload中的内容。2.2 关键安全假设与常见攻击面JWT的安全建立在几个关键假设之上算法的正确使用、密钥的保密性以及声明验证的完整性。一旦这些环节出现纰漏漏洞便随之产生。算法混淆攻击Algorithm Confusion这是靶场中最常见的题型之一。如果服务器代码在验证签名时依赖于客户端提供的Header中的alg字段来决定使用何种算法和密钥进行验证就可能出问题。例如服务器本应使用RS256非对称用公钥验证但攻击者将alg改为HS256对称并尝试用公开的公钥作为HMAC的密钥去伪造签名。如果服务器错误地使用了公钥作为HMAC密钥去验证攻击就成功了。其根源在于验证逻辑的代码实现不严谨。弱密钥爆破当算法为HS256等对称加密时签名密钥的强度至关重要。如果密钥过于简单如secret、password、123456等攻击者可以通过离线爆破的方式尝试用常见密钥列表重新计算签名直到匹配成功。这完全取决于密钥的复杂度和爆破字典的质量。none算法攻击JWT规范允许alg为none表示不进行签名验证。如果服务器配置不当接受了alg为none的令牌那么攻击者可以任意修改Payload然后去掉签名部分或将第三部分置空即可实现未授权访问。现代库通常默认禁用此算法但历史版本或错误配置中仍可能存在。KID/JKU参数注入kidKey ID是Header中的一个可选参数用于指示验证签名时应使用哪个密钥。如果服务器从不可信的位置如用户可控的kid参数动态加载密钥文件就可能造成路径遍历、SQL注入甚至远程代码执行如果kid指向一个远程URL服务器会去获取该URL内容作为密钥。jkuJWK Set URL则允许指定一个包含公钥集的URL如果该URL可控攻击者可将其指向自己控制的恶意站点提供伪造的公钥。理解这些基础我们再看CTFHub的靶场就会发现每一关都是针对上述某一个或几个攻击面的具体实践。3. 靶场环境准备与工具链搭建工欲善其事必先利其器。针对JWT的测试和攻击我们需要一套顺手的工具。以下是我多年使用下来最稳定高效的组合兼顾了图形化操作的便捷和命令行脚本的灵活。3.1 核心工具推荐与配置Burp Suite JWT Editor插件这是Web安全测试的瑞士军刀。JWT Editor插件可以无缝集成到Burp的Repeater、Intruder和Scanner中。你需要在Burp的BApp Store中安装“JWT Editor”插件。安装后在Burp界面会多出一个“JWT”标签页。它的强大之处在于可以可视化地编辑JWT的各个部分并自动重新计算签名支持多种算法HS/RS/ES/PS等。在测试时拦截到包含JWT的请求直接发送到Repeater然后在“JSON Web Token”选项卡下修改并自动重签效率极高。Python3及pyjwt库用于编写自定义的爆破、签名生成脚本。安装命令pip install pyjwt。在需要精细控制或批量处理时Python脚本是不可或缺的。例如当需要尝试成千上万个密钥进行爆破时用Python写循环比在Burp Intruder中操作更灵活也更容易集成外部字典。jwt_tool一个专为JWT安全测试设计的命令行工具功能非常全面。它支持扫描、篡改、爆破、伪造等多种攻击模式。你可以从GitHub上克隆它git clone https://github.com/ticarpi/jwt_tool.git。它的优势在于一键化操作例如爆破密钥只需一条命令python3 jwt_tool.py JWT_TOKEN -C -d /path/to/wordlist.txt。常用弱密钥字典准备一个高质量的弱密钥字典文件是爆破成功的关键。你可以从SecLists项目中获取如/usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-10000.txt也可以自己积累整理包含常见的secret、password、123456、应用名、公司名等变体。注意所有工具请在授权的测试环境中使用。对于CTFHub靶场这类合法的练习平台可以放心使用。3.2 浏览器环境与代理设置确保你的浏览器推荐Chrome或Firefox已正确配置代理指向Burp Suite默认127.0.0.1:8080并安装了Burp的CA证书以便拦截和解密HTTPS流量。访问CTFHub的JWT靶场地址正常登录或获取初始的JWT Token这是所有测试的起点。4. 靶场关卡实战详解与漏洞复现现在我们进入核心的实战环节。我将按照漏洞类型而非严格关卡顺序来拆解CTFHub JWT靶场的典型题目。这样更有助于你建立知识体系。4.1 第一类弱密钥爆破HS256这是最直接的攻击方式。题目特征通常是你获得了一个JWT算法是HS256但密钥未知。目标是通过爆破找到密钥然后用该密钥伪造一个拥有更高权限如admin: true的Token。实战步骤捕获Token登录靶场对应关卡使用Burp拦截HTTP请求找到包含JWT的请求头通常是Authorization: Bearer token或直接在Cookie中。复制整个Token。使用jwt_tool爆破python3 jwt_tool.py 你的JWT_TOKEN -C -d /path/to/wordlist.txt参数-C代表“Crack”-d指定字典。工具会尝试用字典中的每个密钥重新计算签名并与原Token的签名进行比对。成功后会输出找到的密钥。伪造高权限Token获得密钥假设是secret123后使用jwt_tool或Burp JWT Editor插件来伪造新Token。方法一jwt_tool:python3 jwt_tool.py 原JWT_TOKEN -T -S hs256 -p “secret123” -I -hc “user” -hv “admin”参数解释-T篡改-S指定新算法-p指定密钥-I注入声明-hc指定要修改的声明键-hv指定新值。方法二Burp JWT Editor在Repeater的JWT选项卡将alg改为HS256在Payload里将user改为admin然后在“Sign”标签页选择“Secret”类型填入secret123点击“Sign”生成新Token。替换并提交将请求中的旧Token替换为新伪造的Token发送请求即可成功获取flag。实操心得爆破的成功率高度依赖字典。如果通用字典无效可以尝试针对性的字典比如靶场名称ctfhub、题目提示、常见CTF弱口令等。有时密钥可能是空字符串也要尝试。4.2 第二类算法混淆攻击RS256 - HS256这是经典且重要的攻击。题目场景是服务器使用RS256算法非对称它持有私钥用于签名并应使用公钥验证。但服务器端的验证代码有缺陷它信任客户端传来的alg头。攻击原理我们将alg从RS256改为HS256。然后我们需要一个密钥来生成HS256签名。如果我们能获取到服务器的公钥有时通过/static/key.pub等路径泄露我们就可以尝试将这个公钥的内容作为HMAC算法的密钥secret来生成签名。如果服务器错误地使用了公钥作为HMAC密钥去验证HS256签名那么我们的攻击就成功了。实战步骤信息收集首先尝试寻找公钥文件。常见的路径有/api/key、/static/public.pem、/.well-known/jwks.json等。可以用浏览器访问或用Burp的Intruder配合目录字典进行扫描。获取公钥假设在http://target.com/public.key找到了PEM格式的公钥。复制其全部内容。伪造Token使用Burp JWT Editor在“JSON Web Token”选项卡将Header中的alg改为HS256。修改Payload如admin: false改为true。关键步骤在“Sign”标签页选择“Secret”类型。将刚才复制的整个公钥字符串包括-----BEGIN PUBLIC KEY-----和-----END PUBLIC KEY-----粘贴到密钥框中。点击“Sign”生成新的Token。注意这里是用公钥作为HMAC的密钥进行了签名。发送请求用新Token替换旧Token发送请求。如果服务器存在混淆漏洞就会验证通过。重要提示这种攻击成功的前提是服务器验证逻辑存在缺陷。现代安全的JWT库如java-jwt、pyjwt的最新版本通常会强制指定预期算法从而免疫此类攻击。但CTF和部分老旧系统仍是重灾区。4.3 第三类none算法攻击这种攻击较为简单但很多环境已默认防护。当服务器允许alg为none时任何无签名的Token都会被接受。实战步骤修改Header将原JWT的Header中的alg改为none。移除签名将Token的第三部分签名部分直接删除只保留前两部分加上一个点例如xxxxx.yyyyy.。或者有些实现要求第三部分为空但点仍需保留即xxxxx.yyyyy.也有的要求将签名部分置为Base64编码的空字符串。在Burp JWT Editor中选择“none”算法后它会自动处理签名部分。修改Payload按需提升权限。发送请求替换Token并发送。如果返回成功则漏洞存在。排查技巧如果简单的none攻击失败可以尝试将Header中的alg改为None、NONE、nOnE等大小写变体或者同时将typ字段也删除或修改以绕过一些简单的过滤规则。4.4 第四类KID参数路径遍历与注入kid参数本意是让服务器从本地文件系统或数据库中查找对应的密钥。如果kid的值用户可控且服务器未做安全过滤就可能引发漏洞。常见攻击向量路径遍历如果服务器使用类似/keys/kid.pem的路径拼接来加载密钥文件那么kid参数就可能被操纵。Payload示例{“alg”: “HS256”, “typ”: “JWT”, “kid”: “../../../../etc/passwd”}。这样服务器可能会尝试将/etc/passwd文件内容作为密钥来验证签名。如果我们同时将签名设置为空或任意值并且服务器在密钥加载失败时可能采取默认行为如跳过验证就可能绕过认证。更进一步的利用如果能控制文件内容甚至可以配合其他漏洞如SSRF来让服务器使用一个我们指定的、内容已知的文件作为密钥从而完成签名伪造。SQL注入如果kid被用于数据库查询如SELECT key FROM keys WHERE id ‘$kid’那么就可能存在SQL注入。我们可以构造kid为1 UNION SELECT my_secret_key -- -让数据库查询返回我们已知的密钥my_secret_key然后用这个密钥去签名伪造的Token。命令注入在极少数情况下如果kid被直接用于系统命令如调用openssl可能存在命令注入。靶场实战思路遇到题目JWT的Header中包含kid参数时首先尝试简单的路径遍历Payload观察服务器响应是否有变化或报错信息。同时可以尝试SQL注入的探测Payload如kid1看是否引发数据库错误。4.5 第五类JKU/JWK参数恶意URL利用jkuJWK Set URL和jwksJSON Web Key Set是更“现代化”的攻击面。服务器允许通过一个URL指向一个包含公钥集的JSON文件并使用其中的公钥来验证签名。攻击流程搭建恶意JWKS服务攻击者需要先在一个自己可控的服务器上生成一对RSA密钥对公钥和私钥。然后按照JWKS格式构造一个JSON文件其中包含自己的公钥。{ “keys”: [ { “kty”: “RSA”, “use”: “sig”, “kid”: “malicious-key-1”, “alg”: “RS256”, “n”: “...公钥模数...”, “e”: “AQAB” } ] }伪造Token使用自己生成的私钥以RS256算法对篡改后的Payload进行签名。在JWT的Header中设置jku参数指向自己搭建的恶意JWKS URL如http://attacker.com/malicious_jwks.json同时kid设置为恶意JWKS中对应的kid如malicious-key-1。发送请求当服务器验证Token时会读取Header中的jku去访问该URL获取公钥集并找到kid匹配的公钥来验证签名。由于签名确实是用对应的私钥生成的所以验证通过攻击成功。靶场应对这类题目通常会给你一个可以上传文件或控制某个URL内容的功能点。你需要将恶意JWKS文件上传到该平台并获得一个可访问的URL。然后在伪造JWT时引用这个URL即可。5. 高级技巧与组合拳攻击在实际的CTF比赛或更复杂的漏洞挖掘中往往需要将上述基础手法组合使用并配合其他Web漏洞。5.1 签名校验逻辑缺陷的深度利用有时服务器验证签名的逻辑并非简单的“比对是否相等”而是存在可以被利用的缺陷。时间攻击Timing Attack理论上如果服务器使用字符串比较如来核对签名可能存在细微的时间差异攻击者可以利用这种差异来暴力破解签名。但在Web场景中由于网络延迟远大于比较时间实际利用非常困难。签名部分校验极少数实现可能只校验签名的一部分比如前几个字节或者错误地使用了非恒定时间比较函数。这需要通过分析服务器源码或黑盒测试中的异常行为来推断。密钥文件解析差异在算法混淆攻击中如果公钥文件包含证书信息如-----BEGIN CERTIFICATE-----而服务器在将其作为HMAC密钥使用时可能只截取了特定部分进行解码这需要尝试不同的格式和编码。5.2 配合其他漏洞扩大战果JWT漏洞很少孤立存在它常常是权限提升链条中的关键一环。JWT SSRF如果发现一个服务端请求伪造漏洞可以将其与JWT的jku或kid参数结合。通过SSRF让服务器从内网某个已知文件如/proc/self/environ包含环境变量可能泄露密钥读取内容作为密钥或者访问一个内网的恶意JWKS服务。JWT 文件上传/包含通过文件上传漏洞将恶意密钥文件或JWKS文件上传到服务器获得一个内部的URL路径再在JWT中引用该路径。信息泄露找密钥永远不要忽视信息泄露。.git泄露、备份文件如app.py.bak、配置文件如config.php、错误信息回显、甚至是通过其他无关接口返回的数据都可能包含硬编码的JWT密钥。在开始复杂的攻击前先用目录扫描工具和信息收集手段彻底探查一遍目标。6. 防御措施与安全开发建议作为攻击者我们挖掘漏洞作为开发者我们更应避免漏洞。理解攻击手段后可以从根本上加固你的应用。指定预期算法在验证JWT时永远不要依赖客户端提供的alg头。应该在代码中显式指定期望的算法列表。例如使用pyjwt库时# 错误做法依赖于token中的alg # decoded jwt.decode(token, options{“verify_signature”: False}) # 先不验证获取alg # 正确做法明确指定算法 decoded jwt.decode(token, keypublic_key, algorithms[“RS256”]) # 只接受RS256使用强密钥并安全存储对于HS系列算法密钥必须足够长且随机建议32字节以上并像保护密码一样保护它使用安全的密钥管理服务。绝对不要使用硬编码的弱密钥。彻底禁用none算法确保使用的JWT库默认禁用或明确拒绝alg为none的令牌。在配置中检查此项。安全处理kid、jku、x5u等参数对kid进行严格的白名单校验或将其映射到预定义的、受信任的密钥ID避免用户输入直接参与路径拼接或数据库查询。如果使用jku或x5u必须严格校验URL是否在可信域白名单内并确保能抵御SSRF攻击。最佳实践是根本不要使用动态的JWK URL而是将公钥集内置在应用中或从绝对可信的源获取。验证所有必要的声明不要只验证签名。务必验证exp过期时间、nbf生效时间、iss签发者等声明确保令牌在有效期内且来自可信的签发方。使用最新的安全库始终使用官方维护的最新版本JWT库它们通常修复了已知的安全问题。并定期关注安全公告。7. 实战问题排查与调试技巧在靶场练习或真实测试中你可能会遇到各种意外情况。以下是一些快速排查的思路请求发送后毫无变化首先检查Token是否被正确替换。使用Burp的“Logger”或“Repeater”的历史记录功能对比发送前后的请求数据。确认JWT确实在请求头或Cookie中并且格式正确三个点分隔。返回“Invalid signature”签名无效。检查1密钥是否正确2算法是否匹配3Header或Payload在编码前是否有额外的空格或换行4如果是算法混淆攻击公钥格式是否正确尝试将公钥转换为不同格式如PKCS#1, PKCS#8。返回“Algorithm not allowed”服务器明确拒绝了你的算法。说明目标可能已正确配置了算法白名单。需要尝试其他攻击路径如寻找密钥泄露或利用其他参数。服务器返回500错误这可能是一个好迹象说明你的Payload触发了服务器的异常处理逻辑如路径遍历时文件不存在SQL注入导致语法错误。仔细查看错误信息可能包含数据库类型、文件路径等线索。使用Burp的Comparer进行差异分析如果你有一个正常请求和一个失败请求将它们发送到Comparer进行单词或字节级别的对比能快速定位出服务器敏感的不同之处。开启库的调试日志如果测试自己的应用可以在后端JWT验证代码中开启详细日志查看验证过程每一步的决策这对于理解漏洞原理和调试攻击Payload非常有帮助。通关CTFHub的JWT靶场不仅仅是拿到几个Flag更重要的是建立起对JWT安全体系的立体认知。从基础的编码解码到巧妙的算法混淆再到复杂的参数注入每一步都对应着真实开发中可能踩坑的地方。我建议你在完成基础关卡后尝试用不同的工具、不同的Payload去实现同一目标并思考如果自己是开发者该如何编写代码来防御这些攻击。这种攻防思维的切换才是安全能力提升的关键。最后记得将练习中学到的知识应用到其他靶场如PortSwigger的Web Security Academy也有优秀的JWT实验或合法的漏洞众测项目中不断巩固和深化你的技能树。

相关新闻