Monday, May 18, 2015

Defcon CTF 2015 - Cat western - 1 Point Coding Challenge

Defcon was finally here and I was so totally pumped for it. When it dropped, so did my jaw, it wasn't going to be easy. That's for sure. Defcon had some of the evilest, trickiest challenges I've ever faced in a CTF. And it was super fun because of it.

This challenge itself was a break from all the pwnables which I found really irritating in their multilayered complexity. This was simple, get in, work on data, get the flag. Single step stuff.

The challenge gives only the following clue:


meow 9999

Upon connecting to that host with netcat we see the following:

# nc 9999
****Initial Register State****
****Send Solution In The Same Format****
About to send 81 bytes: 
I �H��)o�zL)�I��� �^M!�I��M �H��M��I��H��I��

Hrmm, whats this even about?

Given that it's talking about registers, my first thought is that maybe the binary data represents a memory dump of registers that we're supposed to unpack and send back. But after some thought, that was not much of a challenge so I looked for other vectors.

Next I thought about the terminology used in the server response. "Initial Register State"? "Send solution in same format"? Hmm. Well what operates on registers? Machine code I suppose. I wrote a quick Python client to grab just the binary data from the host into a file called "data.bin".

I then wrote a quick C program to act as a host for my test:

root@mankrik:~/defcon/cat# cat hw.c

void main(void) {
 printf("Hello world!\n");

Next I used GDB to inject my data.bin file into this process:

root@mankrik:~/defcon/cat# gcc hw.c -o hw
root@mankrik:~/defcon/cat# gdb ./hw
GNU gdb (GDB) 7.4.1-debian
(no debugging symbols found)...done.
gdb-peda$ br main
Breakpoint 1 at 0x400510
gdb-peda$ r

Breakpoint 1, 0x0000000000400510 in main ()
gdb-peda$ restore data.bin binary $pc
Restoring binary file data.bin into memory (0x400510 to 0x40055b)
gdb-peda$ x/20i $pc
=> 0x400510 <main+4>: nop
   0x400511 <main+5>: inc    rdx
   0x400514 <main+8>: shrd   r13,r11,0x6
   0x400519 <main+13>: shrd   r9,r11,cl
   0x40051d: mul    rdi
   0x400520 <__libc_csu_fini>: adc    rbx,0x5b4087d2
   0x400527: adc    rbx,r13
   0x40052a: shld   rax,r11,0x1
   0x40052f: push   r8
   0x400531 <__libc_csu_init+1>: shld   rdi,rax,0xd
   0x400536 <__libc_csu_init+6>: add    rbx,r13
   0x400539 <__libc_csu_init+9>: adc    r13,r12
   0x40053c <__libc_csu_init+12>: and    r15,0x7fac5ea2
   0x400543 <__libc_csu_init+19>: xor    rdi,rdx
   0x400546 <__libc_csu_init+22>: xor    r14,r11
   0x400549 <__libc_csu_init+25>: sbb    r9,r14
   0x40054c <__libc_csu_init+28>: xor    rax,0x586fd268
   0x400552 <__libc_csu_init+34>: imul   r15,r15,0x7053ef41
   0x400559 <__libc_csu_init+41>: pop    rax
   0x40055a <__libc_csu_init+42>: ret    

As I suspected, the binary blob is x86 instructions that operate on registers and then returns. Cool. Now, how to programatically do this?

Since my host binary is working so well in hosting this parasite, I decided to make GDB do all the heavy lifting in this exploit and simply script it to do what I wanted.

In Python I grab the binary data and initial states from the network, write a GDB script to load the binary data into the main() function of a hello world C program. Execute the code, crash on the "ret" instruction, examine the final state registers and send them to the server.

The output looks like this:

[+] Opening connection to on port 9999: Done
[+] Getting register states.
[+] Received binary data of size 75:
[+] Building GDB script ...
[+] Executing code...
[+] Parsing registers...
[+] Uploading final state registers...
[>>] rax=0xc4dd04450d8a82e2
[>>] rbx=0xecec03e91686ddeb
[>>] rcx=0xbc71ad0689c1b33d
[>>] rdx=0xc53b9356b687985d
[>>] rsi=0x4997cd7d0cbedd1d
[>>] rdi=0x1bcddcc53dbff749
[>>] r8=0xc4dd04450d8a82e2
[>>] r9=0xc4af29843ee93987
[>>] r10=0x5eb67a469067a63d
[>>] r11=0x554004b2b7283702
[>>] r12=0x677751cce192919e
[>>] r13=0x711711cc7f408fec
[>>] r14=0xe550fc117a587e8f
[>>] r15=0x2748e3d257567b22
[+] Recieving all data: Done (66B)
[*] Closed connection to port 9999
[+] Result: The flag is: Cats with frickin lazer beamz on top of their heads!

And here's the code!


from pwn import *
import subprocess
import re
import os


conn = remote(HOST,PORT)

print "[+] Getting register states."
regstates = conn.recvuntil("****Send")

# Store the states in a list
initials = []
for register in regstates.splitlines():
 if '****' in register:
size = int(conn.recvline().split(" ")[3],10)
data = conn.recvn(size)
print "[+] Received binary data of size " + str(size)
with open('data.bin','wb') as f:

print "[+] Building GDB script ..."
with open('gdbscript.txt','wb') as f:
 f.write("pset option ansicolor off\nbr main\nr\n")
 a = 0
 while a < len(initials):
  f.write("set $" + initials[a] + "=" + initials[a+1]+"\n")
  a += 2
 f.write("info reg\nrestore data.bin binary $pc\nc\ninfo reg\nquit\n")

print "[+] Executing code..."
with open(os.devnull) as DEVNULL:
 gdbout = subprocess.check_output(['gdb','-x','gdbscript.txt','./hw'], stderr=DEVNULL)
print "[+] Parsing registers..."

foundsegv = 0
finals = []
for line in gdbout.splitlines():
 if 'Stopped reason: SIGSEGV' in line: # the ret instruction causes segv
  foundsegv = 1
 i = 0
 while i < len(initials):
  if initials[i] in line and foundsegv > 0:
   finalval = re.split('\s+',line)[1]

print "[+] Uploading final state registers..."
i = 0
while i < len(finals):
 payload = finals[i] + "=" + finals[i+1]
 print "[>>] " + payload
 i += 2
result = conn.recvall()
print "[+] Result: " + result