利用basee64特性 -- BYUCTF 2025 JWTF
题目信息: Unfortunately one of our JWTs was compromised by attackers, so we created a JWT Revocation List to ensure they can’t use it anymore.
代码量很少
# 导入必要的模块
# 从 flask 模块导入 Flask, request, redirect, make_response, jsonify 类和函数
from flask import Flask, request, redirect, make_response, jsonify
# 导入 jwt 模块用于处理 JSON Web Token,导入 os 模块用于与操作系统进行交互
import jwt, os# 初始化 Flask 应用
app = Flask(__name__)
# 从 flag.txt 文件中读取内容并赋值给 FLAG 变量
FLAG = open('flag.txt', 'r').read()
# 生成一个 32 字节的随机字节串,并将其转换为十六进制字符串作为应用的密钥
APP_SECRET = os.urandom(32).hex()
# 生成一个 32 字节的随机字节串,并将其转换为十六进制字符串作为管理员的密钥
ADMIN_SECRET = os.urandom(32).hex()
# 打印管理员密钥,方便调试
print(f'ADMIN_SECRET: {ADMIN_SECRET}')# JRL - JWT 吊销列表,用于存储需要吊销的 JWT 令牌
jrl = [# 编码一个包含管理员权限和用户 ID 的 JWT 令牌并添加到吊销列表中jwt.encode({"admin": True, "uid": '1337'}, APP_SECRET, algorithm="HS256")
]# 定义根路由,处理 GET 请求
@app.route('/', methods=['GET'])
def main():# 创建一个响应对象,内容为 'Hello World!'resp = make_response('Hello World!')# 设置一个名为 'session' 的 cookie,值为一个包含非管理员权限的 JWT 令牌resp.set_cookie('session', jwt.encode({"admin": False}, APP_SECRET, algorithm="HS256"))return resp# 定义 /get_admin_cookie 路由,处理 GET 请求,用于在知道管理员密钥时获取管理员 cookie
@app.route('/get_admin_cookie', methods=['GET'])
def get_admin_cookie():# 从请求参数中获取管理员密钥secret = request.args.get('adminsecret', None)# 从请求参数中获取用户 IDuid = request.args.get('uid', None)# 如果管理员密钥、用户 ID 为空或者用户 ID 为 '1337',则重定向到根路由if secret is None or uid is None or uid == '1337':return redirect('/')# 如果管理员密钥与预设的管理员密钥相等if secret == ADMIN_SECRET:# 创建一个响应对象,内容为 'Cookie has been set.'resp = make_response('Cookie has been set.')# 设置一个名为 'session' 的 cookie,值为一个包含管理员权限和用户 ID 的 JWT 令牌resp.set_cookie('session', jwt.encode({"admin": True, "uid": uid}, APP_SECRET, algorithm="HS256"))return resp# 定义 /flag 路由,处理 GET 请求,用于在用户为管理员时获取标志信息
@app.route('/flag', methods=['GET'])
def flag():# 从请求的 cookie 中获取名为 'session' 的值,并去除首尾空格和等号session = request.cookies.get('session', None).strip().replace('=','')# 如果 session 为空,则重定向到根路由if session is None:return redirect('/')# 检查 session 是否在 JWT 吊销列表中,如果在则重定向到根路由if session in jrl:return redirect('/')try:# 尝试解码 session 中的 JWT 令牌payload = jwt.decode(session, APP_SECRET, algorithms=["HS256"])# 如果解码后的令牌中 admin 字段为 True,则返回标志信息if payload['admin'] == True:return FLAGelse:# 否则重定向到根路由return redirect('/')except:# 解码失败则重定向到根路由return redirect('/')# 定义 /jrl 路由,处理 GET 请求,用于获取 JWT 吊销列表
@app.route('/jrl', methods=['GET'])
def jrl_endpoint():# 将 JWT 吊销列表以 JSON 格式返回return jsonify(jrl)# 如果该脚本作为主程序运行,则启动 Flask 应用
if __name__ == "__main__":app.run(host='0.0.0.0', port=1337, threaded=True)
我们先尝试获取被吊销的JWT
["eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6dHJ1ZSwidWlkIjoiMTMzNyJ9.NPRE_IxezKiALwrpSQkHy2DxuJEXmHblTYtvGUo-1Gw"]
JWT(JSON Web Token)由三个部分组成
- 头部(Header)
- 载荷(Payload)
- 签名(Signature)
每部分使用 Base64 编码,并由句点 .
分隔,形成如下格式:
Header.Payload.Signature
签名部分是
NPRE_IxezKiALwrpSQkHy2DxuJEXmHblTYtvGUo-1Gw
其解码后hex为
34 f4 44 23 17 b3 2a 20 0b c2 ba 52 42 41 f2 d8 3c 6e 24 45 e6 1d b9 53 62 db c6 52 8d 46
但是我们无论如何修改最后一位base64,其结果均相同
NPRE_IxezKiALwrpSQkHy2DxuJEXmHblTYtvGUo-1Gx
NPRE_IxezKiALwrpSQkHy2DxuJEXmHblTYtvGUo-1Ga