Reverse engineering the HITB binary 100 CTF challenge
Disclaimer for legal people: “I” and “me” are nicknames in this blog post. They refer to a person who may or may not be me, myself, or I.
During the HITB conference (Hack In The Box) in Amsterdam last week, a Capture The Flag challenge was organised. Six categories were available of which you could solve challenges: Web, Binary, Network, Crypto, Misc and Special. Together with Kinine and Flunk, team hDs secured a 7th place in the CTF ranking.
Binary analysis is not exactly the field I feel most comfortable at right now, but it has certainly captured my interest lately. The binary for the first challenge we were confronted with (bin100), simply outputted a lyric line once every second. After a certain amount of time (too long), the binary will eventually display the solution.
Download the binary: bin100.elf
Analysing the binary
Here’s an overview of a strings run on the binary. It may not seem like many lyrics, but they’re just repeated over and over again. The line “OK YOU WIN. HERE’S YOUR FLAG: “, is not actually followed by the solution we’re looking for (would be a bit too easy, wouldn’t it). So how do we get the actual key?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
/lib64/ld-linux-x86-64.so.2
__gmon_start__
libc.so.6
__printf_chk
srand
time
__stack_chk_fail
putchar
sleep
__ctype_toupper_loc
__ctype_tolower_loc
__libc_start_main
GLIBC_2.3.4
GLIBC_2.4
GLIBC_2.2.5
GLIBC_2.3
AVAUATUSH
|$4H
D<Yi
D<YI
[]A\A]A^A_
l$ L
t$(L
|$0H
%80s
KEY:
%02x
OK YOU WIN. HERE'S YOUR FLAG:
,7"rmn~uJ
im the defacto leader of a movement
screaming "hack the planet" back in 99
hacktivism in its prime globalHell had the .mil rooted
alphabet soup and their troops in the suits kid
kicking down doors and seizing my equipment
blocking all my shipments sitting on their hitlist
0day radical emphatic beat addict
and that stab hit i envelope the game call me rabbit
hop to hop i run the internet equivocally
bitch i be hit em with the bytestyle symmetry
digi g digital gangster repping till im dead
steady grep apache logs when im looking for the feds
fast forward now the internet anonymous
and captains of the lulzboat raise the mast prominent
dominant hacks - antisec on that new new
dropping tables in mysql like it was some poo poo
pound antisec - pounding through your speakers
pound antisec - pound it to the bleachers
pound antisec - if youre sitting below deck
in the lulzboat salute bitch and show some respect
lulzsec bitch they fold ya hacked sony
got that md5 we rocked ya macaroni
cook coke crack then boil like a noodle
while hbgary stay toast like a streudel
rootshell on ya bootstrap - now whos that?
botnet mjoin and drop your whole c class
see class? it is evident we flossing
ion cannon in the proc list ddossing
lean back bitch we be sending an injection
magic quotes off JOIN TABLE intersection
put it up on pastebin it wont get erased then
20 million hits to your dome like some cavemen
ask cnn - you want a interview?
send a PRIVMSG to the nickname sabu
on irc - man we convening
this some 99 throwback shit that im screaming
;*3$"
|
Time is of the essence, so let’s get going (literally, you’ll see). First, we ran the binary through IDA Pro to see which function names are in there. Here you see an overview of .symtab and some externally imported functions from GLIBC:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# nm bin100.elf | egrep " T "
0000000000400a34 T _fini
0000000000400600 T _init
0000000000400a30 T __libc_csu_fini
00000000004009a0 T __libc_csu_init
00000000004006c0 T main
0000000000400890 T _start
# objdump -T bin100.elf | awk '{print $1, $NF}'
bin100.elf: elf64-x86-64
DYNAMIC TABLE:
0000000000000000 __ctype_toupper_loc
0000000000000000 putchar
0000000000000000 __stack_chk_fail
0000000000000000 __libc_start_main
0000000000000000 srand
0000000000000000 __gmon_start__
0000000000000000 time
0000000000000000 __printf_chk
0000000000000000 sleep
0000000000000000 rand
0000000000000000 __ctype_tolower_loc
|
Not much interesting. It seems like the whole program’s functionality has been compiled into the single main() function. Some functions from the standard library are being called (time, srand, putchar, sleep, rand). The main function consists of a gazillion branches.
GDB jumping
Here you see an excerpt of two locations that were of specific interest: 0x4007E8 and 0x400803.
The first (0x4007E8) seems to display a key, while the second (0x400803) seems to output our flag. Let’s try jumping to those addresses using GDB:
It was a long shot, but the outputted flag is still garbage when we jump to 0x4007E8. Jumping to 0x400803 immediately segfaults. Not what we want.
Overriding time() and sleep()
We then proceeded by attaching a debugger and follow the program flow dynamically. You can do as such using gdbserver and remotely attaching to the process with IDA’s Remote GDB Debugger tool. Doing that, revealed that the lyric lines were being looped (no surprise there). At the end of each iteration the line is printf’ed, followed by a call to the sleep() function. The special characters you see in the screenshot are the music symbols.
At first thought you would want to NOP the sleep() function, or replace the MOV edi, 1 by MOV edi, 0 to disable the sleep function. This is not possible because the program keeps track of the elapsed time, and the needed key to output the flag relies on this time (correct me if I’m wrong). Since time is limited and my knowledge of assembly isn’t all that great, another approach was taken rather than trying to analyse the file any further.
What if you could fake both the sleep calls and elapsed time, tricking the program into thinking it has actually slept for the required number of seconds? You can do that by overriding GLIBC’s sleep() and time() functions very easily: create a C file that contains the functions you want to override, and make them have the same signature. Just increment the time with one second on every sleep() call, without invoking the underlying sleep() function. Then compile it into a shared object and preload it using the LD_PRELOAD trick. When you’d run the binary, the sleep function will be NOP’ed, while it still increases the elapsed time.
In the above screenshot, you can see the C file being compiled into a shared object and then preloaded. It does all the necessary iterations, followed by the printout of our flag, p4ul_1z_d34d_1z_wh4t_th3_r3c0rd_s4ys!
NOP’ing the printf statements
For the attentive readers: you don’t see any lyric lines because we NOP’ed the aforementioned foregoing printf calls (printing all the lines takes a lot of time).
This can be done in IDA (although definitely not its best developed function), by first undefining the instruction call ___printf_chk in 0x7B7 (0x4007B7 if you like). You can then use the patch function (Edit > Patch > Change byte), and replace the call with 0x90, which is a NOP. Newer IDA versions don’t display the Patch submenu at this moment, but you can bring it back by editing idagui.cfg and setting DISPLAY_PATCH_SUBMENU = YES. Then produce a DIF file (File > Produce file > DIF), and patch your binary with it. It doesn’t work with the Unix patch utility, but you can use this gist.
That seems rather difficult for applying a simple patch. Surely there are better solutions (comments are welcome). You could just as well use a hex editor. P.S. A nice tool that can also help you getting the correct assembly for your instruction, is radare2.
Download:
original bin100.elf binary, printf patch file, patched bin100.elf binary, time.c preload
Recent Comments