Switchy – OpenToAll CTF 2015

This is the write-up of the second reverse engineering challenge from the OpenToAll(OTA) team CTF in 2015, worth 150 points. Although I did not manage to play the CTF, I had a brief look and thought I’d give this a go.

For those of you who want to get started playing CTFs, are curious to see what a CTF is, or do not have a team to play with, then I highly recommend joining OTA. I started playing CTFs with them and met some really cool people and learnt some tricks. You can find them under /r/opentoallctfteam or in #opentoall on Freenode IRC.

Preliminaries

Before we can start, some much-needed information gathering: what file are we looking at,

$ file 7fcdb7907692cbd6ea87600ab11377b3
7fcdb7907692cbd6ea87600ab11377b3: ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=ed173f80ccccc36c7c25b5093a93f67e28bc0acc, not stripped

It’s a 32-bit ELF binary, dynamically linked and not stripped, and as such doesn’t make our lives too difficult. Following the dynamic linking information, the binary uses the following libc symbols:

objdump -T 7fcdb7907692cbd6ea87600ab11377b3
7fcdb7907692cbd6ea87600ab11377b3:     file format elf32-i386
<p>DYNAMIC SYMBOL TABLE:
00000000      DF *UND*    00000000  GLIBC_2.0   printf
00000000      DF *UND*    00000000  GLIBC_2.0   fflush
00000000  w   D  *UND*    00000000              __gmon_start__
00000000      DF *UND*    00000000  GLIBC_2.0   __libc_start_main
0804b160 g    DO .bss    00000004  GLIBC_2.0   stdout
08048f4c g    DO .rodata    00000004  Base        _IO_stdin_used

The binary is fairly small (11Kb) and doesn’t import many things. We would expect it to print something:

$ ./7fcdb7907692cbd6ea87600ab11377b3 | hexdump -C
00000000  63 db 7d db 97 ea 4c c9  26 0e 07 b7 0d 69 ae 1f  |c.}...L......i..|
00000010  b7 1f fc db fc b7 1f fc  db fc b7 a6 fc 69 51 0e  |.............iQ.|
00000020  c9 46 0a                                          |.F.|

It prints 35 characters. It could be the flag, but it doesn’t look like it! It’s also worth mentioning that it prints the same 35 characters irrespective of the command line arguments that you pass it.

A closer look

So far we know that we have a small binary that prints something, and somewhere there we have the flag. Opening the file up in IDA gives you the list of functions (NB: I renamed one of the functions to big_switch_statement):

funcs
Figure 1: Summary of functions in the target binary.

The main function, our starting point, looks like this:

main_intro
Figure 2: Part of the main function. The rest of the body is structurally identical to the top part.

A number of noticeable things are going on here. Firstly, there seems to be an awful lot of stack variables; secondly, it looks like characters are being printed out to the stdout stream a character at a time, indicated by the “%c” formatting string to _printf (note that due to the lack of a newline character, printf will not print; lastly, there’s a lot of repetition for printing the aforementioned characters. If you look at the code closely the following fragments of assembly are repeated:

mov ecx, dword_xxxxx
mov [esp], ecx
....
mov [ebp+var_xx], eax
call _fflush

These groups of instructions use the stack variables and call what I, ill advisedly as it turned out, renamed as “big_switch_function”. A closer look at the big_switch_function will reveal a lot of information. From a distance:

big_switch
Figure 3: Overview of the biggest function in the binary. It certainly looks promising.

The entry point of the function along with the negative branch and some of the middle code-blocks are shown in Figure 3 and Figure 4 bellow (for a clearer view):

closeup
Figure 4: Closer view of the function shown in Figure 3.
jmp
Figure 5: Interesting code fragment, used to jump to locations that handle data from the .data section. This is the negative branch depicted in Figure 3.

NB: I renamed the memory location ds:offset_xx to ds:switch_stmt_locations, I will explain later

Some thinking

At this point it is worth to take a step back and thinking about what is going on inside the binary. The main function has a series of local variables (Figure 2), which it passes to the big_switch_statement function, and it prints the value returned from that function. Since the values printed from the main function are not readable, we could assume that the magic is happening inside the odd looking function shown in Figure 3, a view further supported by looking at the rest of the user-defined functions and finding nothing interesting.

If we inspect the big_switch_statement function, we can draw some interesting conclusions. First off, it’s not a big switch statement. The middle blocks are very similar and all have the same exit point, but do not form a cascading flow of comparison statements. They are reached by jumping to their locations and if we trace the function through gdb, we can see that the jump is performed in the negative branch that follows the entry point of this function as shown in Figure 5.

If you haven’t noticed already it seems that the binary is somewhat obfuscated, as the control flow of the program is really unclear in certain places (although admittedly it could be worse). One can see that from the jmp <reg> instruction in Figure 5 and in other places in the binary, the fact that the big_switch_statement function uses one of its arguments to jump to an arbitrary memory address and the fact that variables are being shifted around. As an example, each of the jump targets in Figure 4 could clean up the stack and return, instead of shuffling the result of the XOR eax, ecx operation around registers and the stack.

So far we haven’t looked at all at the data sections of the binary, and these could be useful as they are referenced from within the big_switch_statement function. Figure 6 and Figure 7 show the .data and .rodata sections of the binary and both contain very helpful information!

Figure 6: Data declared by our binary, a mix of printable and non-printable characters.

NB: In Figure 7 switch_stmt_locations was the name that I gave to the structure found in 0x8f50.

Figure 7: The .rodata section contains a list of what looks like memory locations.
Figure 7: The .rodata section contains a list of what looks like memory locations in the .text section.

If we pay attention to the data we found in the .rodata section, shown in Figure 7, then we can see that each of these items is a memory address in big_switch_statement. In turn, each of these code fragments references two bytes in the .data section shown in Figure 6. Combining the information so far with the code fragment from Figure 5 (notice the mov ecx, ds:switch_stmt_locations[eax*4] instruction and remember that in 32-bit Intel integers are 4-bytes wide), we can conclude that the big_switch_statement uses a global array of pointers which contain pointers to the jump-targets of the function. It uses the first argument passed to the function to determine where to jump and it returns the XOR of some characters, which will then get printed out. We can also see that the input to big_switch_statement that determines the jump location is provided by main, and these arguments are main’s list of local variables. As such, we should set a breakpoint on 0x8048497 and then stepping 6 instructions to break on the xor eax,ecx instruction and inspect the contents of eax. In gdb lingo:

$ gdb ./7fcdb7907692cbd6ea87600ab11377b3
(gdb) br *0x8048497
(gdb) r
(gdb) c          #repeat from here 35 times
(gdb) si 6
(gdb) info registers eax
.....

Fin

Finally, after stepping through the code and inspecting the values of the eax register, we get our flag: flag{switch jump pogo pogo bounce}. 150 points, ca-ching 😀

P.S. I apologise for the “special” naming of functions and variable names, appreciate the consistency.

 EDIT

An admittedly cooler way to solve this would be to make the binary tell you the flag! We could find all the locations in the binary where xor eax,ecx happens and turn them into nop instructions. The following code snippet does this!

#!/usr/bin/python
addr = [0x4a7,0x4c1,0x4db,0x4f5,0x50f,0x529,0x543,0x55d,0x577,0x591,0x5ab,0x5c5,0x5df,0x5f9,0x613,0x62d,0x647,0x661,0x67b,0x695,0x6af]
with file('switchy','r+b') as f:
for i in addr:
f.seek(i)
f.write(chr(0x90)+chr(0x90))
f.closed

And this would give us:

$ ./7fcdb7907692cbd6ea87600ab11377b3
flag{switch jump pogo pogo bounce}

🙂

Advertisements
Switchy – OpenToAll CTF 2015

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s