Page 1

Defcon CTF Quals 2014 – 100lines | zepvn


Search MAY 18, 2014

Defcon CTF Quals 2014 – 100lines By admin

Challenge description:

It’s not broken, you just need more RAM. This is the second 64-bit binary file that I deal with in this CTF. It has 4 main functions called calc , loop , getByte , and of course main . A quick pseudo-code to describe what main does: Generate 38 number (32-bit each) as OTP, store to (long long) OTP[38] Calculate (long long) seed variable based on a constant (named __randpad_len in the binary) Generate a (char*) static_buffer based on static buffer (named __randpad in the binary) Print all generated numbers to STDOUT. Read 8 chars from STDIN. set counter = 0 For each input char: Do a check with very long condition which involves a lot of calls to getByte() function. If the check returns True: counter += 1 if counter > 6: Read file 'flag' and store to (char*) flag. Loop i from 0 to 37: (char) c = getByte(OTP[i], seed, static_buffer) ^ flag[i] Princ c to STDOUT in hex format.

The condition that is used to check looks like this:


That looks pretty scary to me. However if we look at it closely, the check is basically a comparison between our input character and an expression of OTP , seed , and static_buffer which are all known. So by evaluating that expression with the first 8 numbers from OTP we could get the expected input characters. But it’s actually impossible to do so with the current code base as it always end up calling malloc with a very huge number in getByte function:

char getByte(unsigned long long index, unsigned long long seed, unsigned char* static_buffer) { int size = (seed – 0×20) * ((seed << 2) - 80); (char*) new_buffer = malloc(size); if (new_buffer == null) { exit(0); } else { loop(seed, static_buffer, new_buffer) free(new_buffer); return new_buffer[index]; } }

Notice that the value of seed is 0xf81000 that makes size equal to 1,057,159,935,887,872 , that means it's trying to malloc about 961 Terabytes here! So we definitely need to rewrite this function. We can see that:


Defcon CTF Quals 2014 â&#x20AC;&#x201C; 100lines | zepvn


loop function does something to assign values to new_buffer getByte function only cares about the value of new_buffer[index]

Let's reverse the loop function:

function loop(unsigned long long seed, unsigned char* static_buffer, unsigned char* new_buffer) { int N = seed - 0x20; var_40 = 0x0; rax = var_40; for (int i=0; i<N; i++) { int K = 0; for (int j=0; j<=3; j++) { K = calc(K, static_buffer, i, j); } for (int j=0; j<N; j++) { int H = 0; for (int k=0; k<=3; k++) { H = calc(H, static_buffer, j, k); } H = H ^ K for (int k=0; k<=3; k++) { new_buffer[(i + j * N) * 4 + k] = H >> ((-k << 3) + 0x18); } } } }

The value of each byte in new_buffer is independent to each other and it rather depends on i , j , k , N , and H . We could easily calculate those variable then rewrite loop function as below:

function new_loop(unsigned long long seed, unsigned char* static_buffer, unsigned long long my_index) { unsigned long long N = seed - 0x20; unsigned long long k = my_index % 4; unsigned long long i = (my_index / 4) / N; unsigned long long j = (my_index / 4) - k*N; int K = 0; for (int l=0; l<=3; l++) { K = calc(K, static_buffer, i, l); } int H = 0; for (int l=0; l<=3; l++) { H = calc(H, static_buffer, j, l); } return (H ^ K) >> ((-k << 3) + 0x18); }

I remove the new_buffer param as we no longer need it, the param my_index is added so that it only calculates and returns the value of that index. getByte function also needs to be rewritten now:

char getByte(unsigned long long index, unsigned long long seed, unsigned char* static_buffer) { return new_loop(seed, static_buffer, my_index) }

Simple huh ? How about the calc function ? Well, Hopper does a pretty good job on decompiling it so I would keep it as is:

function calc(unsigned int, unsigned char*, unsigned long long, unsigned long long) { var_m4 = LODWORD(rdi); var_m16 = rsi; var_m24 = rdx; var_m32 = rcx; var_m4 = var_m4 | LODWORD(LODWORD(LOBYTE(LODWORD(SAR(LODWORD(LOBYTE(*(int8_t *)(var_m16 + 0x1 + var_m32 + (var_m24 >> 0x3)) & 0xff) & 0xff), LOBYTE(LOD LODWORD(rax) = var_m4;


Defcon CTF Quals 2014 â&#x20AC;&#x201C; 100lines | zepvn


return rax; }

So I got the final code to receive 38 OPT numbers from STDIN and print out ASCII values of expected input characters: defcon-2014-quals-100lines-bruteforce. In this version, I was too lazy to break down the expression when it checks the input character, so I kept the condition as it is and made a variable looping from 0 -> 255 to test whether that condition is met (ie. the expression returns True) Simply compile the code by $ g++ -o bruteforce bruteforce.c

Since I don't feel like writing socket code in C, let's write some Python script to communicate with the real service and execute the bruteforce code that we just got.

import socket from subprocess import Popen, PIPE, STDOUT s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(('', 20689)) data = s.recv(2048) data += s.recv(2048) data = data.split("\n")[1] print "OTP:" print data arr = [str(eval(_)) for _ in data.split()] p = Popen(['./bruteforce'], stdout=PIPE, stdin=PIPE, stderr=PIPE) stdout_data = p.communicate(input="\n".join(arr))[0] result = "".join(chr(int(_)) for _ in stdout_data.split("\n")[:-1]) print "Sending back: ",repr(result) s.send(result) print "Response:" print s.recv(2048) print s.recv(2048)

One of the sample output:

OTP: 0x0003477c0b80aa0f 0x00008985a385bd8e 0x000371f572962c37 0x0001bec6d1d1f7a6 0x000060f32eb8e2d1 0x00017c9388cc842b 0x00032d90a11d5d21 0x000251507644d55f 0x00017c68d057 Sending back: 'U<{v!&Vm' Response:

0xc6, 0x11,0x3e,0x93,0x67,0x6a,0xf2,0xcd,0xfe,0x29,0x0d,0x4d,0xf2,0x8a,0x87,0x48,0x2e,0x81,0x39,0xb7,0x20,0x88,0xc3,0x98,0x21,0x20,0xfb,0x51,0xdc,0xb4,0x2a,0x03,0x7f,0xb7,0x79,0xe

Okay, we are really close now. We got back 38 bytes and that must be some kind of encrypted form of the flag (or some text that contains flag). We continue reversing the last part of main function and get this:

... for (int i = 0; i<38; i++) { int c = flag[i] ^ getByte(OTP[i], seed, static_buffer); printf("0x%02x", c); } ...

It's pretty obvious now. I wrote another C code which is quite similar to the bruteforce one except that OTP is now hardcoded together with the 38 encrypted bytes: defcon-2014-quals-100lines-bruteforce.cpp. Compile, run it and enjoy the flag: $ g++ -o get_flag get_flag.cpp $ ./get_flag The flag is:#RadicalSpaceOptimization!


Defcon CTF Quals 2014 – 100lines | zepvn


Post a Comment or leave a trackback: Trackback URL.

Leave a Reply Name (required) Mail (will not be published) (required) Website

Post Comment Current ye@r * 21

Recent Posts Defcon CTF Quals 2014 – 100lines Defcon CTF Quals 2014 – byhd NAKL – Yet another Vietnamese input keyboard [CTF] Codegate 2012 [Pidgin Plugin] Yahoo Messenger – Buzz blocker HDFS over Webdav for Hadoop 0.20.1 [Hadoop] Mount HDFS using built-in fuse library [git] Change your previous commit [Hadoop] “Too many fetch-failures” or “reducer stucks” issue [Scribe] Another approach to supporting HDFS

Archives May 2014 June 2012 March 2012 September 2010 October 2009 September 2009 August 2009 July 2009 March 2009

Tags 2014 block buzz



defcon disable distributed computing emoticons facebook fetch-failures fuse git Hadoop hdfs linux pidgin plugin Programming reversing scribe vietnamese-input-keyboard Webdav yahoo

log collector mac-os memcached nakl nginx module objective-c pidgin


Defcon CTF Quals 2014 – 100lines | zepvn


© zepvn, 2014. All Rights Reserved


Defcon ctf quals 2014 – 100lines zepvn  
Defcon ctf quals 2014 – 100lines zepvn