JWT垂直越权漏洞实战:从原理到Polar靶场攻防演练

发布时间:2026/7/7 20:47:25
JWT垂直越权漏洞实战:从原理到Polar靶场攻防演练 1. 项目概述与核心思路最近在Polar靶场里折腾一个关于JWT垂直越权的实战场景感觉挺有意思的。这个靶场模拟了一个典型的Web应用核心目标是从一个普通用户user的权限通过分析和篡改JWT令牌最终拿到管理员admin的权限从而访问后台获取Flag。整个过程就像一次精细的“权限升级手术”考验的是对JWT机制的理解和漏洞利用的实操能力。如果你正在学习Web安全尤其是身份认证和授权相关的漏洞这个案例能帮你把JWT的原理、常见错误配置以及如何利用这些配置失误串起来形成一个完整的攻击链。简单来说JWTJSON Web Token是现代Web应用里非常流行的一种无状态身份认证机制。它由三部分组成头部Header、载荷Payload和签名Signature。服务端签发令牌后客户端在后续请求中携带这个令牌服务端通过验证签名来确认令牌的合法性和完整性。听起来很安全对吧但问题往往出在实现细节上。在这个Polar靶场的案例里漏洞的根源就在于服务端对JWT的验证存在缺陷可能没有严格校验签名或者签名密钥过于简单可被爆破导致攻击者能够篡改载荷中的关键信息比如用户角色标识从而实现垂直越权。这个实战过程会涉及几个关键步骤首先是信息收集和初始访问找到普通用户的登录凭证然后是分析当前会话的JWT令牌结构接着是识别漏洞点可能是弱密钥、无签名验证或者算法混淆最后是构造一个拥有管理员权限的新令牌并替换完成越权访问。下面我就结合Polar靶场的具体环境把每一步的操作、背后的原理以及我踩过的坑详细拆解一遍。2. JWT垂直越权漏洞原理深度剖析在动手之前我们必须把JWT垂直越权的原理吃透这样才能知道每一步操作在“打”什么以及为什么能成功。很多人一上来就照着步骤做做完还是云里雾里遇到变种场景就又不会了。2.1 JWT令牌的结构与安全基石一个标准的JWT令牌看起来像这样eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyIn0.xxx。它由三个用点号分隔的Base64Url编码字符串组成。第一部分是头部Header通常包含令牌类型typ: JWT和签名算法alg比如HS256HMAC SHA-256。这个部分经过Base64Url编码但本身没有加密任何人都可以解码查看。在Polar靶场的例子里解码后我们能看到{alg:HS256}这告诉我们服务端使用HS256算法来生成和验证签名。第二部分是载荷Payload包含了所谓的“声明”Claims。声明有三种类型注册声明预定义的一些标准字段如iss签发者、exp过期时间、sub主题通常放用户名。公共声明可以自定义但为避免冲突应使用抗冲突的名字。私有声明开发者为了业务需要自定义的字段比如这个靶场里的login_user_key用来标识当前登录的用户。载荷同样只是Base64Url编码没有加密。这意味着只要拿到令牌任何人都能轻松解码并看到里面的内容。攻击者篡改权限主要就是针对这个部分下手。第三部分是签名Signature。这是JWT安全性的核心。签名的生成方式依赖于头部中声明的算法。对于HS256签名是这样生成的HMACSHA256(base64UrlEncode(header) . base64UrlEncode(payload), secret_key)服务端用同样的密钥和算法对收到的Header和Payload重新计算一次签名如果计算结果与令牌中的Signature部分一致就证明令牌在传输过程中没有被篡改并且确实是使用正确的密钥签发的。2.2 漏洞产生的常见场景垂直越权漏洞的产生本质上是因为JWT的安全机制在某个环节被绕过或削弱了。结合Polar靶场和常见情况主要有以下几种签名验证缺失或逻辑错误这是最致命的一种。服务端在验证JWT时可能因为开发人员疏忽只解码了Payload读取用户信息却没有验证Signature或者验证逻辑存在缺陷比如验证失败后仍然信任Payload。在这种情况下攻击者可以直接修改Payload如把sub: user改成sub: admin然后重新组装一个令牌因为签名不对但服务端不检查就能轻松实现越权。Polar靶场的情况更典型一些属于下面第二种。弱密钥或密钥泄露当使用HMAC如HS256这类对称加密算法时签名和验证使用同一个密钥secret_key。如果这个密钥设置得过于简单比如123456、secret、password攻击者就可以通过暴力破解或字典攻击的方式猜解出来。一旦密钥泄露攻击者就可以用正确的算法和密钥为任何他想要的Payload生成合法的签名从而签发一个完全受控的、服务端会认可的高权限令牌。Polar靶场最终就是爆破出了弱密钥。算法混淆攻击JWT头部中的alg字段本应指定签名算法。但如果服务端代码存在缺陷可能会信任客户端提供的alg值。攻击者可以将alg改为none如果服务端配置允许“none”算法或者从非对称算法如RS256改为对称算法如HS256。在后者的情况下攻击者可以利用服务端用于验证RS256签名的公钥作为HS256的密钥来伪造一个有效的签名。这种攻击对代码逻辑的要求更高。敏感信息泄露与逻辑缺陷Payload中的自定义字段如果包含了权限标识如role: admin,isAdmin: true而服务端完全依赖这个字段做权限判断那么一旦签名被绕过这个字段就成了攻击的直接目标。此外如果密钥硬编码在客户端代码中也可能被逆向工程获取。核心心法JWT的安全完全建立在签名验证的可靠性上。任何导致签名验证失效或密钥可控的环节都是潜在的突破口。我们的攻击思路永远是围绕“如何让服务端接受一个我们篡改过Payload的令牌”来展开的。3. Polar靶场实战从普通用户到管理员的完整攻防链理论清楚了我们进入实战环节。我会模拟在Polar靶场中的完整攻击流程并穿插讲解每个步骤的意图和可能遇到的变数。3.1 环境探测与初始凭证获取首先我们访问靶场提供的Web应用地址。通常是一个登录界面。尝试使用常见的弱口令admin/admin, user/user等进行登录大概率会失败但这有助于我们了解登录接口的请求格式。根据参考的实战记录靶场可能存在一个默认用户user但密码未知。这时我们需要进行密码爆破。抓取登录请求包在浏览器中打开开发者工具F12切换到Network网络标签页勾选“Preserve log”保留日志。在登录框输入用户名user和一个任意密码如test点击登录。在网络请求列表中找到向/login或类似路径发送的POST请求。分析请求结构查看该请求的详情通常请求体是JSON格式如{username:user,password:test}。同时注意观察响应。如果密码错误响应可能是{code: 400, message: 密码错误}如果用户不存在可能是{code: 404, message: 用户不存在}。区分这两种响应对于精准爆破至关重要。实施密码爆破将捕获到的登录请求包发送到Burp Suite的Intruder模块。在Positions标签页清除所有自动标记只将password字段的值test标记为攻击载荷位置。在Payloads标签页加载一个常用的弱口令字典如rockyou.txt的简化版或自定义的123456、password、123123等。在Options标签页根据之前观察的响应设置Grep - Match规则来识别成功的登录。例如如果成功登录的响应中包含success: true或跳转到了/dashboard就以此作为判断依据。获取有效凭证启动攻击Intruder会遍历字典尝试所有密码。很快我们就会发现当密码为123456时响应状态码或内容与其他尝试不同例如返回了200 OK和一个JSON Web Token。至此我们获得了初始凭证user / 123456。实操心得在实际测试中登录失败和成功的响应差异可能很微妙。有时成功登录后服务端会设置一个CookieJWT而失败不会。因此在Burp Intruder的Grep - Extract功能中可以尝试提取响应中Set-Cookie头部的值如果提取到内容则很可能代表登录成功。这是比单纯看HTTP状态码更可靠的方法。3.2 JWT令牌捕获与结构分析使用获取到的凭证user:123456成功登录系统。登录后应用通常会跳转到用户主页。此时我们再次打开开发者工具的Network标签页刷新页面查看任意一个后续请求比如获取用户信息的API请求。关键的一步来了找到身份凭证。在请求的Headers中寻找Authorization头其值通常为Bearer token或者Cookie头。在这个Polar靶场案例中身份令牌是放在Cookie里的。你会看到一个名为token、auth或类似名称的Cookie其值就是一长串看起来像eyJ...的JWT字符串。把它复制出来。接下来我们需要解码并分析这个令牌。你可以使用在线工具如 jwt.io 但更推荐使用Burp Suite的扩展如JWT Editor或本地命令行工具避免敏感信息外泄。以在线工具jwt.io为例将完整的JWT令牌粘贴到左侧的“Encoded”输入框。工具会自动在右侧的“Decoded”部分将其解析为Header和Payload。查看解析结果Header:{alg: HS256, typ: JWT}。这确认了签名算法是HS256。Payload:{login_user_key: user, iat: 163xxxxxx, exp: 163xxxxxx}。这里包含了关键信息login_user_key字段的值为user这很可能就是服务端用来识别当前用户的字段。此外还有令牌签发时间iat和过期时间exp。这个结构清晰地告诉我们只要我们能生成一个签名有效、且login_user_key字段值为admin的JWT服务端就可能把我们当作管理员。现在的挑战是如何生成一个有效的签名3.3 密钥爆破与令牌伪造由于算法是HS256我们需要知道签名密钥secret才能伪造签名。如果密钥强度足够长度长、随机性强爆破几乎不可能。但现实中开发人员可能设置弱密钥。Polar靶场就模拟了这种情况。我们有几种方法来尝试获取或破解这个密钥使用专用工具爆破这是最直接的方法。我们可以使用hashcat或john这类密码破解工具配合强大的字典如rockyou.txt来爆破。命令示例# 将JWT令牌保存到文件 jwt.txt echo eyJhbGciOiJIUzI1NiJ9.eyJsb2dpbl91c2VyX2tleSI6InVzZXIifQ.IKiUadJl6hUuSCnD38CVuIsl-BnGEkSEEECoDpHtFpU jwt.txt # 使用hashcat模式16500对应JWT hashcat -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt如果密钥在字典中hashcat会很快跑出来。利用Burp Suite扩展安装Burp Suite的“JWT Editor”扩展。在Proxy - HTTP history中找到携带JWT的请求右键选择“JWT Editor” - “Attack” - “Brute-force secret”。扩展会使用内置的常见弱密钥列表进行爆破速度很快。在Polar靶场中用这个方法几乎瞬间就能爆出密钥是123456。基于已知信息的猜测在CTF或一些简单的靶场中密钥可能与应用名、公司名、secret、password、123456等相关。可以手动尝试几个。一旦成功爆破出密钥123456我们就拥有了伪造令牌的全部条件。伪造管理员令牌在jwt.io或JWT Editor中保持Header不变{alg: HS256, typ: JWT}。修改Payload将login_user_key的值从user改为admin。注意不要修改iat和exp除非你想模拟一个“新签发”的令牌可能需要调整时间戳。在“VERIFY SIGNATURE”区域输入我们爆破得到的密钥123456。工具会自动使用这个密钥和HS256算法为新的Header和Payload生成一个合法的签名。左侧的Encoded JWT会实时更新为一串新的令牌字符串。现在我们得到了一个“合法”的管理员令牌。它的Payload声明我们是admin并且签名经得起服务端的验证。3.4 权限提升与Flag获取最后一步就是用我们伪造的令牌去“欺骗”服务端。替换令牌回到浏览器打开开发者工具进入Application应用或Storage存储标签页找到Cookies。将当前站点的对应JWT Cookie的值修改为我们刚刚生成的新令牌字符串。访问特权功能刷新页面。如果漏洞存在页面可能会发生明显变化用户昵称变成了“admin”侧边栏出现了原本不可见的“系统管理”、“用户管理”等管理员菜单。寻找Flag根据提示“Flag在管理员后台”或“在admin用户的备注里”我们进入用户管理页面。在用户列表中找到admin用户点击编辑或查看详情。在详情页的“备注”或“描述”字段中就能找到最终的Flag通常是一串由字母数字组成的字符串格式如flag{xxxx-xxxx-xxxx}或polar{...}。至此一次完整的JWT垂直越权攻击就完成了。我们从信息收集开始通过密码爆破获得低权限账户分析其JWT令牌结构爆破弱密钥最终伪造高权限令牌成功实现了从user到admin的权限跃迁。4. 漏洞的深度利用与高级技巧上面的流程是基础版本。在实际的安全测试或更复杂的靶场中可能会遇到一些变种和加固场景。掌握以下技巧能让你更从容。4.1 应对签名验证的几种情况服务端对JWT的验证逻辑可能不同我们需要灵活应对情况一服务端完全不验证签名。这是最低级的错误。我们甚至不需要爆破密钥直接修改Payload后把签名部分删除或者改成任意字符串服务端可能都会接受。在Burp Repeater中尝试发送修改后的令牌如果依然返回成功就证实了这一点。情况二服务端使用非对称算法如RS256。这时签名密钥私钥和验证密钥公钥是不同的。我们很难拿到私钥。但可以尝试“算法混淆攻击”将Header中的alg改为HS256然后尝试用已知的公钥有时会暴露在/jwks.json端点作为HMAC的密钥来伪造签名。这需要服务端存在相应的逻辑漏洞。情况三密钥不是固定值而是动态的或与用户相关。有些设计会用用户的密码哈希的一部分作为JWT的密钥。这种情况下即使你爆破出了user的密码并登录也无法直接伪造admin的令牌因为不知道admin的密码。这时可能需要结合其他漏洞如SQL注入先获取管理员密码哈希。4.2 工具链的熟练使用工欲善其事必先利其器。除了Burp Suite以下工具能极大提升效率jwt_tool一个功能强大的命令行JWT测试工具。它可以解码、验证、伪造、爆破JWT支持多种攻击方式如CVE-2015-9235、CVE-2016-10555等。# 解码令牌 python3 jwt_tool.py eyJhbGciOiJIUzI1NiJ9.eyJsb2dpbl91c2VyX2tleSI6InVzZXIifQ.IKiUadJl6hUuSCnD38CVuIsl-BnGEkSEEECoDpHtFpU # 爆破密钥 python3 jwt_tool.py eyJ... -C -d /path/to/wordlist.txt # 伪造令牌如果已知密钥 python3 jwt_tool.py eyJ... -S hs256 -p 123456 -TBurp Suite JWT Editor扩展如前所述它集成在Burp中方便在拦截的请求上直接进行解码、编辑、签名和攻击无需在多个工具间切换。浏览器插件如JWT Debugger可以方便地在浏览器中实时解码和编辑Cookie中的JWT对于快速测试非常有用。4.3 自动化攻击脚本编写对于需要批量测试或集成到自动化扫描流程中的场景可以编写简单的Python脚本。import jwt import requests import base64 import json import sys def decode_jwt(token): 解码JWT不验证签名 try: # 分割令牌 parts token.split(.) if len(parts) ! 3: print(无效的JWT格式) return None, None # 解码头部和载荷 header json.loads(base64.urlsafe_b64decode(parts[0] ).decode(utf-8)) payload json.loads(base64.urlsafe_b64decode(parts[1] ).decode(utf-8)) return header, payload except Exception as e: print(f解码失败: {e}) return None, None def brute_force_secret(token, wordlist_path): 暴力破解JWT密钥HS256 header, payload decode_jwt(token) if not header or header.get(alg) ! HS256: print(非HS256算法或不支持的令牌) return None with open(wordlist_path, r, encodingutf-8, errorsignore) as f: for line in f: secret line.strip() try: # 尝试用当前密钥验证签名 decoded jwt.decode(token, secret, algorithms[HS256]) print(f[] 密钥破解成功: {secret}) return secret except jwt.InvalidSignatureError: continue except Exception as e: # 其他错误如过期但签名验证通过了也说明密钥正确 if signature in str(e).lower(): continue else: print(f[*] 使用密钥 {secret} 时出现其他错误: {e}) return secret print([-] 密钥破解失败) return None def forge_admin_token(original_token, secret, admin_usernameadmin): 伪造管理员令牌 header, payload decode_jwt(original_token) if not header or not payload: return None # 修改载荷中的用户标识字段这里假设字段名是login_user_key根据实际情况修改 payload[login_user_key] admin_username # 可选移除或更新过期时间避免立即过期 # if exp in payload: # payload[exp] int(time.time()) 3600 # 设置为1小时后过期 try: new_token jwt.encode(payload, secret, algorithmheader[alg], headersheader) return new_token except Exception as e: print(f令牌伪造失败: {e}) return None # 使用示例 if __name__ __main__: # 替换为你的靶场令牌和字典路径 target_token eyJhbGciOiJIUzI1NiJ9.eyJsb2dpbl91c2VyX2tleSI6InVzZXIifQ.IKiUadJl6hUuSCnD38CVuIsl-BnGEkSEEECoDpHtFpU wordlist common_secrets.txt print([*] 开始破解密钥...) found_secret brute_force_secret(target_token, wordlist) if found_secret: print(f[*] 发现密钥: {found_secret}) print([*] 尝试伪造管理员令牌...) admin_token forge_admin_token(target_token, found_secret) if admin_token: print(f[] 伪造的管理员令牌:\n{admin_token}) # 这里可以添加自动替换Cookie并访问管理员页面的逻辑 # session requests.Session() # session.cookies.set(auth_token, admin_token, domaintarget.com) # resp session.get(https://target.com/admin) # print(resp.text) else: print([-] 令牌伪造失败) else: print([-] 未找到有效密钥)这个脚本提供了基本的解码、爆破和伪造功能。在实际使用中需要根据靶场的具体JWT字段名不一定是login_user_key进行调整。5. 防御方案与开发安全建议作为攻击者我们研究漏洞作为开发者或安全人员我们更要懂得如何防御。针对JWT垂直越权以下是一些核心的防御措施使用强密钥并安全存储对于HS256等对称算法密钥必须足够长建议至少32字节、随机且复杂。绝对禁止使用123456、secret等弱密钥。密钥应作为机密信息存储在环境变量或安全的密钥管理服务中绝不能硬编码在客户端代码或配置文件中。强制验证签名且逻辑严谨在服务端验证JWT时必须强制校验签名。任何签名无效或验证失败的令牌必须立即拒绝并返回401 Unauthorized。验证逻辑要放在所有业务逻辑之前。采用非对称算法如RS256在生产环境中优先使用RS256等非对称算法。私钥用于签发令牌并严格保护在服务端公钥用于验证令牌可以安全地分发给需要验证的组件。这样即使公钥泄露攻击者也无法伪造签名。避免在Payload中存放敏感权限信息JWT的Payload是Base64编码并非加密。不要将用户的角色如role: super_admin、权限列表等敏感信息直接放在Payload里。权限信息应在服务端数据库或缓存中关联用户ID进行查询。Payload中只存放最低必要信息如用户IDsub和过期时间。设置合理的令牌有效期使用expExpiration Time声明为JWT设置一个较短的有效期如15-30分钟。结合Refresh Token机制来获取新的Access Token减少令牌泄露后的风险窗口。实施令牌黑名单/白名单对于关键操作或需要立即撤销权限的场景如用户登出、密码修改可以实现一个轻量级的令牌黑名单机制存储已撤销但未过期的令牌IDjti或者在验证时检查令牌是否在有效白名单中。对算法进行显式声明和限制在验证JWT时不要依赖客户端提供的alg头。代码中应显式指定期望的算法列表如只允许RS256防止算法混淆攻击。进行安全的代码审查和依赖管理定期审查身份认证相关的代码。确保使用的JWT库是最新版本避免使用存在已知漏洞的旧版本库。6. 常见问题排查与实战避坑指南在实际操作中你可能会遇到一些意料之外的情况。这里记录了几个我踩过的坑和解决方法问题1替换了伪造的JWT Cookie但页面没变化或者提示“登录已过期”。可能原因APayload字段名不对。服务端可能不是通过login_user_key来识别用户而是username、sub、user_id等其他字段。你需要仔细分析原始低权限用户的JWT Payload找到那个唯一标识用户的字段名。可能原因B令牌过期。检查原始令牌的Payload是否有exp字段。如果有并且时间已经过期那么即使签名正确服务端也会拒绝。在伪造令牌时需要将exp字段的值更新为一个未来的时间戳Unix时间戳单位秒。可能原因C签名密钥错误。确认你爆破出的密钥确实是正确的。可以用这个密钥尝试重新为原始令牌未修改的计算签名看是否能得到完全一样的原始签名。如果不能说明密钥不对。可能原因D存在额外的验证。服务端可能不仅验证JWT还会在数据库或会话中检查用户状态。仅仅修改JWT可能不够。问题2爆破密钥时字典跑完了也没出结果。对策尝试更大的字典如rockyou.txt。考虑密钥可能不是单词而是与项目名、域名、公司名相关的字符串或者是简单的数字序列。也可以尝试使用hashcat的掩码攻击模式如果怀疑密钥是6位数字可以用hashcat -m 16500 jwt.txt -a 3 ?d?d?d?d?d?d。问题3JWT被放在HTTP请求头的Authorization字段里格式是Bearer token如何方便地修改和测试对策Burp Suite的Repeater模块可以完美处理。在Proxy拦截到请求后发送到Repeater。在Repeater的请求框中直接修改Authorization: Bearer eyJ...后面的令牌字符串即可。结合JWT Editor扩展可以一键解码、编辑、重新编码并更新请求头。问题4在真实渗透测试中如何高效地发现JWT漏洞点侦察阶段登录应用后检查所有Cookie和Authorization头寻找eyJ开头的字符串。使用浏览器插件或Burp扩展快速解码观察Payload结构。测试阶段签名删除测试将JWT的签名部分第三个点号后的部分删除或者改成任意字符如aaa发送请求看服务端是否接受。算法置空测试将Header中的alg改为none或None同时清空签名部分发送请求。弱密钥测试使用工具和常见弱密钥字典进行爆破。Payload篡改测试在已知密钥或签名验证缺失的情况下尝试修改Payload中的用户ID、角色字段。最后想说的是JWT本身是一个优秀的规范绝大多数安全问题都源于不正确的实现和配置。这个Polar靶场的实战就像一把钥匙帮你打开了理解Web身份认证漏洞的一扇门。真正掌握它不仅要会利用更要懂原理和防御这样才能在安全这条路上走得更稳更远。下次遇到JWT不妨多花几分钟想想它的签名是否真的牢不可破。