Hacking Tube

DEFCON CTF 2015 Quals -- catwestern

| Comments

Category: Coding Challenge
Points: 1

catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me 9999

Interesting challenge. First we connect to the service, it will send us the following message:

****Initial Register State****
****Send Solution In The Same Format****
About to send 74 bytes: 

So it seem like the service gave us the registers' inital value, and a sequence of machine code. Apparently, they want us to execute the machine code and send them all the register values after the execution.

After some thinking and researching, I decide to solve the challenge by using the following method:

First, get all the registers' initial value and construct the machine code, start with mov [reg], [val]. Then, receive the machine code from the server, and append it to the current machine code. So the final machine code will be like this:

; if we convert it to assembly
mov rax, XXX
mov rbx, XXX
mov rcx, XXX
mov r15, XXX
[server's machine code]

Now all we have to do is to execute the machine code and get all the registers' value. To achieve this requirement, I prepared a C code:

char code[] = machine ;

int main(int argc, char **argv)
    int (*func)();
    func = (int (*)()) code;
    (int)(*func)(); // execute the machine code
    int a = 0; // line 8, our break point to check the register values
    return 0;

"machine" will be replace by the machine code I have (by using the sed command). After we finish building the C file, we can compile it by using the gcc -g -z execstack -o real real.c command.

Now we'll just need to excute the binary file we have compiled. I decide to execute it with gdb, since it'll make me more easier to get the register values. So now the problem is how do we get the output from gdb by using python? Fortunately, with the help of the internet, I found this link, which is pretty useful for me to solve the challenge. All I need to do is call gdb by using Popen, send 3 command to it (b 8, r & i r), then parse the result and send the answer to the server.

Here's the final script:

from pwn import *
from subprocess import Popen , PIPE
from time import sleep
import sys
import os

HOST = "catwestern_631d7907670909fc4df2defc13f2057c.quals.shallweplayaga.me"
PORT = 9999

context.arch = "x86_64"
context.os = 'linux'
context.endian = 'big'

r = remote(HOST, PORT)

def getoutput(proc):
    print "the output"
    s = ""
    while True:
        out = proc.stdout.read(1)
        if out == '' and proc.poll() != None:
            s += out
            # recv until the end of the color code(generate by gdb-peda)

            if '20011b5b306d02'.decode('hex') in s:
                return s

def get_ans():
    proc = Popen( ['gdb' , './real'] , bufsize=1 ,stdin=PIPE, stdout=PIPE )
    s = getoutput(proc)

    print "sending b 8" # break at line 8

    proc.stdin.write('b 8\n')
    s = getoutput(proc)

    print "sending r" # start running

    s = getoutput(proc)

    print "sending i r" # get the register values

    proc.stdin.write('i r\n')
    s = getoutput(proc)
    # parse the result

    ans = ""
    all_register = s.split('\n')
    for c in all_register:
        temp = c.split(' ')
        if temp[0] in register:
            ans += temp[0].strip() + "=" + temp[len(temp)-1].split('\t')[0].strip()
            ans += '\n'

    print ans
    print r.recv(1024)

# recv registers' state

r.recvuntil("****Initial Register State****")
s = r.recvuntil("****Send Solution In The Same Format****")
print s
temp = s.split("\n")
register = dict()

# setting registers' dict

for index, c in enumerate(temp):
    if "=" in c:
        name = c.split("=")[0]
        val = c.split("=")[1]
        register[name] = int(val, 16)

# adding mov [reg], [val]

machine_code = ""
for key, val in register.items():
    s = "mov "+str(key)+", "+hex(val)
    machine_code += asm(s).encode('hex')

# receiving machine code

print r.recvuntil("bytes: \n")
s = r.recv(1024)

# appending machine code

machine_code += s.encode('hex')
machine = "\""
for c in machine_code.decode('hex'):
    machine += "\\\\x"+c.encode('hex')
machine += "\""

# contructing real.c

cmd = "sed \'s|machine|"+machine+"|g' < shell.c > real.c"
print disasm(machine_code.decode('hex'))
os.system('gcc -g -z execstack -o real real.c')


Notice the line context.endian = 'big'. At first I use the little endian to solve the challenge, which gave me "Invalid solution" everytime I send the answer. Just before I was about to give up, teammate bletchley suggest me to change the endianness into big endian. And guess what? The flag appears on the screen right after I change the word "little" into "big"! What an end!

Flag: Cats with frickin lazer beamz on top of their heads!


comments powered by Disqus