CSAW CTF 2014 — Reverse 500 – Aerosol can

by Shubham Bansal [iN3O@SegFault]

This year’s CSAW CTF [2014] was fun, especially Pwning and Reversing sections.
Here,I am going to explain my solution of — Aerosol Can — Reverse 500 — although I didn’t do it in time and i haven’t found any writeup for this problem online so here we go…

1. Start

We are given a 32-bit executable.

04:48 AM iN3O:Aerosol_Can-500>file aerosol_can
aerosol_can: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x366b4f02c3b5f4196a54512332f79396bc20ef2e, stripped

2. Understand

Lets open this in IDA.
1_cropped
In IDA, we see the main_func function which checks for the number of arguments passed to the binary and prints “Must have at least one argument\n” string if its less then 2 otherwise it passes the second argument to the function call starter(0x8053460) which checks for the correctness of the 2nd argument we passed, which in this case, is our flag string. If we passed the correct flag then function starter returns the 0 otherwise any other negative value.
lets go inside starter function…
2_cropped
What this function does is :

  • Calls getrlimit function to get the stack limit(size) for the program.
  • Calls mmap function to map virtual stack of the same size as above.(this will later be used extensively)
  • Call function DEVIL(0x8048660) to do some processing on our input string.
  • Again Call getrlimit function to get the stack limit(size) of the program.
  • Call munmap to unmap the previously mapped stack.

3. Obfuscation

DEVIL function is passed some address(esp+0x11c-0x1A4) of the original stack, but before that it puts an address(base address of new stack – 0x38) at [esp+0x11c-0x18C] which is at an offset 0x18 of the address which is passed in DEVIL function.It also puts the address of the input string at(base address of new stack – 0x34) which is just below the address we put at [esp+0x11c-0x18C] above. So lets see the DEVIL function…
4_cropped

Its a thousands of lines of code which is quite an Obfuscation. And It took me lot a of time to figure out what to do with this binary.
First, let me tell you what the obfuscation is and then we will rip this binary apart 😛
Actually, the obfuscation is a play between Original Stack, New Stack and local variables of DEVIL function on stack. DEVIL function allocates 0x1DC size memory for its local variables on the stack in it prologue, then it takes the argument we passed, takes random values from different offset of it and puts it in random address on new stack and local variables to obfuscate the code. I could have done dead code analysis on the binary but it does use those dead code(not so dead actually :P) to pass the arguments to the functions and for the control flow checks.

What I did to figure out a way out of this mind f**king Obfuscation is:

  1. follow the [ebx+18h] value(which contains some address of the new stack and is used to get reference to our input string ) in the code
  2. 2. follow the function calls and what their arguments and what they are returning ( i tried to understand the functions but they are obfuscated too 😦 )
  3. 3. follow the control flow checks, what check leads to which path.

See their are many different solution possible for this , and each solution leads to return 0, and , you can follow your own path to make it return 0. Following is just my way of doing that.

4. Functions

Lets start getting the flag. I will reduce my explanation to just checks only ,otherwise it will take soo much time and space…

I will explain the different function call used during the checks in DEVIL function.

  1. str_to_hex(0x8051bd0)                takes string address as argument                 convert/decode string like “abcd” into 0xabcd , “efgh” into 0xef00 (using the following table for conversion)
  2. hex_to_1char(0x80512e0)           takes input a character address                     convert/decode that character according to above table
  3. sum_natural_num(0x8050030)   takes an integer(n) as argument                    returns the sum of first n natural numbers (1 + 2 + 3…. n)
  4. divide_(0x08053540)                     takes 4 integer as argument(a,b,c,d)             returns a/(b*c*d)
  5. sum_4hexchar(0x0804DAB9)      takes (esp+0x11c-0x1A4)(a) as argument     returns sum of 8bit values from [a] to [a+8]

TABLE :

  • 0x20 – 0x2f                                         ->                                         0x0 – 0xf
  • 0x3A – 0x46                                      ->                                          0x3 – 0xf
  • 0x30 – 0x39                                       ->                                          0x0 – 0x9
  • 0x47 – 0x57                                       ->                                          0x0 – 0xf
  • 0x58 – 0x66                                       ->                                          0x0 – 0xf
  • 0x66 onward                                    ->                                            0x0

 

 

5. Flag Checks

1. Code first checks for the length of the string we passed

 .text:0804866A                 mov     ebx, [esp+1ECh+arg_0]    ; new stack address
 .text:080486A4                 mov     ebp, [ebx+18h]
 .text:080489D5                 mov     eax, [ebp+4]    ; input string
 .text:080489DE                 mov     [esp+1ECh+s], eax ; s
 .text:080489E1                 call    _strlen
 .text:080489E6                 mov     [ebp-8Ch], eax  ; string length
 .text:08048A96                 cmp     eax, 26h
 

It must be equal to 0x26.

2. Then it checks for the first 5 characters of the string flag[0:5]

 .text:08048A9F                 mov     eax, [ebp+4]
 .text:08048AA2                 mov     ecx, 0FFFFFFFEh
 .text:08048AA7                 cmp     byte ptr [eax], 'f'
 .text:08048AAA                 jnz     loc_80492FF
 .text:08048AB0                 cmp     byte ptr [eax+1], 'l'
 .text:08048AB4                 jnz     loc_80492FF
 .text:08048ABA                 cmp     byte ptr [eax+2], 'a'
 .text:08048ABE                 jnz     loc_80492FF
 .text:08048AC4                 cmp     byte ptr [eax+3], 'g'
 .text:08048AC8                 jnz     loc_80492FF
 .text:08048AE0                 cmp     byte ptr [eax+4], '{'
 .text:08048AE4                 jnz     loc_80492FF

 

first 5 chars must be flag[0:5] = “flag{“

3. Check for next 4 char

 .text:08048B02                 add     ecx, 5          ; string pointer added 5 for flag{
 .text:08048B1A                 mov     [ebx], ecx
 .text:08048ECD                 mov     [esp+1ECh+s], ebx        ;  input_string[5:9]
 .text:08048ED0                 call    str_to_hex
 .text:0804921F                 mov     ecx, [esp+1ECh+var_174] ; str_to_hex returned value
 .text:0804922A                 movzx   eax, word ptr [edx-9Ch]    ; constant 0x0E607
 .text:08049235                 xor     ecx, eax
 .text:080492DC                 cmp     cx, 0FEEDh

 

I choosed flag[5:9] = “1_ea”

4.Check for 6th character

 .text:08049AF1                 mov     [esp+1ECh+s], ebx        ; input_string[6]
 .text:08049AF4                 call    hex_to_1char
 .text:0804A270                 mov     [esp+1ECh+s], ebx        ; return value from above hex_to_1char
 .text:0804A273                 call    sum_natural_num
 .text:0804A278                 sub     esp, 4
 .text:0804A40C                 mov     al, [eax+ecx+1]        ; ecx is address if input string , eax is the return value of sum_natural_num
 .text:0804A66B                 cmp     byte ptr [esp+1ECh+var_E8], '}'
 

flag[0x25] = “}”

5. Check for 9th char


.text:0804A69D                 movsx   eax, byte ptr [eax+9]    ; flag[9]
 .text:0804A6A1                 mov     cl, al
 .text:0804A6A3                 and     cl, 0Fh
 .text:0804A6A6                 cmp     cl, 6            ; check for last 4 bits of flag[9]
 .text:0804A6AD                 jnz     loc_804A756
 .text:0804A6B3                 mov     ecx, eax
 .text:0804A6B5                 and     ecx, 0FCh
 .text:0804A6BB                 cmp     ecx, 34h        ; check for first 4 bits of flag[9]

 

I choosed flag[9] = “a”(0x36)

6. Check for 10,11,12th chars


.text:0804AB6B                 call    hex_to_1char    ; input_string[10] (b)
 .text:0804B2DD                 call    hex_to_1char    ; input_string[11] (c)
 .text:0804BA56                 call    hex_to_1char    ; input_string[12] (d)
 .text:0804BDC5                 call    divide_            ; 144 , b,c,d
 .text:0804BE76                 cmp     eax, 6

 

I choosed flag[10:13] = “lm6”

7. Here I tried to get as many character possible from the given string, but only 3 char was possible… :/
6_cropped

I chossed flag[13:16] = “aba”

and so on… their are many checks further but i think you got the idea 🙂

One more thing i found is that in the following 3 blocks at the end of DEVIL function, we can only return 0 if we can get to the block loc_804F4ED.
7_cropped

NOTE : If anybody found any better solution or any method to de-obfuscate it using code, please tell me 🙂 I did all of it manually.

NOTE: I haven’t been able to confirm the key as nobody is replying on #csaw irc channel. but I am getting “You win!” as output 🙂

UPDATE 1 : I confirmed with admin. Flag i got is correct 🙂

Thanks to Artem for this amazing problem. I don’t think i solved it by the way he was expecting 😦 but still I got the flag 😛

5. Flag

Flag I got is:

flag{1_ea6lm6aba0faad77703151f6666667}

Pwnium CTF 2014 — Reverse 150 [ Kernel Land ]

Hey Pwners, this is the second writeup of the day …

Now lets get to this MotherF***er 😉

Reverse 150 [ Kernel Land ] is an simple reversing challenge . All it requires is a — CTF thinking
I don’t know why this challenge is alloted 150 points and why Rev100 only 100 pts  , only organizers can tell us.
So lets Start…We are given an 32-bit binary file. file command gives us
11
but when i tried to run it , I got Segmentation Fault.
12
What the hell ? Then i got curious as i was expecting a good challenge as compared to REV100 , i thought may be i am running it on wrong processor .
I tried to use string command to see wats up with this binary? Generally you see different library strings ( like libc.so.6 , GLIBC_2.0 ) but this binary had nothing .
13
So , next move is well know , “RELEASE THE IDA“. Well , as you know its an 32-bit binary , I just decompiled it using IDA hex-ray decompiler plugin ( and it came out to be really really small kernel ).
So lets move to main function.
1
As you can see main has couple of function call . Lets start with monitor_clear()
2
Eww…looks messy. If you look closely , its very simple (compiler is the culprit here)…

void monitor_clear()
{
for(int a=0;a<80;a++)
{
b = a%256;
for(c=b;c<25;c++)
{
monitor_put_char_at(a,c,32);
}
}
}
view raw monitor_clear.c hosted with ❤ by GitHub

okay now look at monitor_put_char_at()
3
its some address calculation ( v1 calculation ) and some value calculation ( v2 calculation ) to be stored at v1. Doesn’t look like a string creation , so moving on…
Next , we will see dump_mboot()
4
Well, looks like some memory calculation and some dumping . Not looks interesting. Moving on…
Next stop is gdt_init() [Global Description table initialisation]…
5
Well, at the first look i think this function is completely useless to me…moving on…
Next stop idt_init() [ Interrupt descriptor table initialisation]…
6
Useless…moving on…
Next Step irq_init()
7
Interrupt request initialisation…hmm…moving on…
Next and last interesting function timer_init()…lets jump in…
8
It calls an another function irq_install_handler to install an interrupt handler i.e timer_trick [ the main culprit ]
9
hmm…interesting function. Looks like its xoring some value in the bss section (i.e memory) with the tick count. IDA made it look little bit confusing but its very simple. It takes a string from memory location 0x103060 and performs some xor operation with tick variable and then again store it at the same position .Looks easy right ? but their is one trick. Each time we enter this handler we increase the value of tick by one. So we can’t figure out the flag until we know which value of tick gives us the flag. Now Good thing about this tick variable is that , its value matters till it reaches 255[0xff] as we are doing mod operation with 256.
So, Lets fuck it,
I wrote a very simple c code to brute force the tick value and compute output string for each tick value.
#include<stdio.h>
int main()
{
char encoded_flag[]={"Itofrjxb2`..c.2.6031]g6b1gg0^)b11cb^^-]z"},ch;
int tick,x;
for(tick=1;tick<256;tick++)
{
printf("tick:%d\n",tick-1);
int i = 0;
for(i=0;i<0x28;i++)
{
x = encoded_flag[i]^tick ;
x++;
encoded_flag[i]=(char )x;
}
puts(encoded_flag);
}
return 0;
}

And , for tick = 2 we got our Flag.

Flag –> Pwnium{e5c11b1519328df9e8ff3a0e88beaa4d}

Pwnium CTF 2014 — Reverse 100 [ Old School ]

Hey pwners , this is my first CTF writeup so if i make mistakes, be nice :).

Now lets get to this MotherF***er 😉

Reverse 100 [ Old School ] was a good challenge written by m4r00n. Although, it requires some previous knowledge of VM ( and how it works ) and also one very important tool which is must in CTF — Intelligent Guessing — it was a fun challenge.
Reverse 100 [Old School] is an atari xl emulator which is emulating 6502 processor opcodes and uses SDL graphic API. It runs as a game that we have to play like an old nokia mobile’s Snake game but as you know this is CTF — nothing is straight here –.
12
Initially, Keys to use was not clear , so, i started to press random key ( trying to find the useful keys ) and eventually found out the keys required to start and play the game .

  • Alt                         –>          FIRE Key
  • Up arrow key       –>          Up
  • Down arrow key  –>          Down
  • Right arrow key   –>          Right
  • Left arrow key     –>           left
  • F1                        –>              Help

Problems with the Game is that , the length of the Snake in the game increases contuniously and makes a cage like structure which is annoying and when we take a point/coin , the taken point doesn’t disappear and make many more points available to be taken on the screen. We can’t win this ( atleast i can’t.. ). Here comes the mind of an reverse engenieer. I fired the IDA and started looking at the code .
3
Nothing interesting except an exceptional handler..moving on…
5
hmm…call at 0x004010F8 …lets go inside..

6
Some Command line argument parsing and some initialization, nothing interesting here…

7
lets look into function call at 0x004090E6 to 0X402A98 .
8
looks like the main program code.Some initialization,setting videobuffer and screen resolution. It Sets the screen buffer length at [esp+0x18] i.e 0x8AF0. I followed the code and reached here
9
looking for something interesting…
At 0x00402C29 their is a call to function 0x402964 [ Buffer_Handler ].
Buffer_Handler function is dangerous looking . First i didn’t wanted to understand the function and wanted to put this function on hold for understanding in future but I didn’t find anything interesting other than this and one another function at 0x00402BB7 ( we will look into it shortly ) . So , wat the hell? screw it.. we are going in…
10
In Buffer_Handler function we are taking argument from main function like buffer size,buffer address. You can say Buffer_Handler is a function who handles what buffer screen will show. This VM has its own VM_RAM from where it copies the screen buffer to be shown . I didn’t wanted to go more deep for a 100 pts problem.So I left it their understanding only basic structure of the function.
11
Now at main function, we see SDL_Flip which copies src screen buffer to dest screen buffer i.e Main screen buffer.
13
Then we see SDL_PollEvent which checks , if their is some pending event . If their is some pending event ,we will eventually reach 0x00402BB7 where we have a call to [Culprit] function ( why culprit? because here is where we will find our solution ).
14

We are going in Culprit

27
HolyShit… What the hell is that ? Now Buffer_Handler function looks much nicer to me 😛
15
Actually , This function is an Event handler. Whenever their is an event to handle like arrow key pressing , Function key pressing and shift key pressing , this function is called. This function identifies which key is pressed and jumps to appropriate address ( using switch cases ) for that key and handles it accordingly.
1617

1819

2021

22
Looks like every key pressing is denoted by doing some operation at fixed position in Memory of VM and that memory address is stored at an particular offset from eax = [ edi+ 10h ] .
For the important key below is the what handlers do.

  • UP                —-            #or    [eax + 0xD300],1 #and    [eax + 0xD300],0xFE
  • DOWN            —-            #or    [eax + 0xD300],2 #and    [eax + 0xD300],0xFD
  • LEFT            —-            #or    [eax + 0xD300],4 #and    [eax + 0xD300],0xFB
  • RIGHT            —-            #or    [eax + 0xD300],8 #and    [eax + 0xD300],0xF7

“OR” and “AND” operation happens at different time not sequencially.First AND operation is done to the initial value stored at [eax+0xD300] which is 0xff , therefore after the operation, value at [eax+0xD300] becomes 0XFE/0XFD/0xF7/0XFB according to the key pressed . And, after storing that value in ARRAY, Culprit OR that value with its negation to make it 0xff again ( default value ).

23

25

26

Culprit stores each OR’ed value at address [denoted by value at [eax+0xd300] ] in ARRAY and then checks for some sequence of value present in buffer [ which represent the key sequence ]. If it finds that sequence in ARRAY , it jump to 0x004074EC . Hmmm… wait a second, is it ? is this binary expecting some key sequence [ this was an intelligent guess after wasting soo much time ]. I took a chance and noted the sequence, binary is expecting.
Binary is expecting 0xFE,0xFE,0xFB,0xFE,0xFD,0xF7,0xFD,0xFB,0xF7 sequence in ARRAY , which is ( as converted according to table above )

  • UP
  • UP
  • LEFT
  • UP
  • DOWN
  • RIGHT
  • DOWN
  • LEFT
  • RIGHT .

I pressed those keys in this sequence and waited for the key to pop up , and it did pop up at Game ending .
Well, it wasn’t easy . was it?

KEY -> GO0D_J0B!