Tuesday 1 January 2013

SmashTheStack IO Level 1

The first level is pretty straightforward. Connect to the smashthestack.org server using an SSH Client. For level01, connect to level01@io.smashthestack.org and port 2224. Get into the "levels" folder using the "cd levels" command. Now, let's gather some information about the program first. 

Source Code available: No

For the sake of understanding, I have mentioned the program logic below. We will see how this can be derived later.

Program Logic:

Argument passed at command line is compared with an internal string. At the assembly level, each character of the input and the internal pwd is compared one by one.

GDB Commands we will be using:

display/i $pc: Turns on automatic display of the program counter. After every step instruction, the next intruction to be executed will be displayed.
ni: Next instruction (Jumps functions/calls)
si: Step instruction (Does not Jump functions/calls)
x/c <addr>: Prints the character at the specidied address (Shows the hex value as well)
break: Breaks the program execution at the specified function.
break *<addr>:  Breaks the program execution at the specified address (after the asterisk).
strings file: Displays all the hardcorded strings in a file

Approach:

Start by disassembling the main function.

Dump of assembler code for function main:
0x08048596 <main+0>:    push   %ebp
0x08048597 <main+1>:    mov    %esp,%ebp
0x08048599 <main+3>:    sub    $0x18,%esp
0x0804859c <main+6>:    and    $0xfffffff0,%esp
0x0804859f <main+9>:    mov    $0x0,%eax
0x080485a4 <main+14>:   sub    %eax,%esp
0x080485a6 <main+16>:   cmpl   $0x2,0x8(%ebp)
0x080485aa <main+20>:   je     0x80485ca <main+52>
0x080485ac <main+22>:   mov    0xc(%ebp),%eax
0x080485af <main+25>:   mov    (%eax),%eax
0x080485b1 <main+27>:   mov    %eax,0x4(%esp)
0x080485b5 <main+31>:   movl   $0x8048760,(%esp)
0x080485bc <main+38>:   call   0x80483b8 <printf@plt>
0x080485c1 <main+43>:   movl   $0x0,-0x4(%ebp)
0x080485c8 <main+50>:   jmp    0x8048618 <main+130>
0x080485ca <main+52>:   call   0x804852d <pass>
0x080485cf <main+57>:   movl   $0x64,0x8(%esp)
0x080485d7 <main+65>:   mov    0xc(%ebp),%eax
0x080485da <main+68>:   add    $0x4,%eax
0x080485dd <main+71>:   mov    (%eax),%eax
0x080485df <main+73>:   mov    %eax,0x4(%esp)
0x080485e3 <main+77>:   movl   $0x80491a0,(%esp)
0x080485ea <main+84>:   call   0x80483a8 <mbstowcs@plt>
0x080485ef <main+89>:   movl   $0x8049140,0x4(%esp)
0x080485f7 <main+97>:   movl   $0x80491a0,(%esp)
0x080485fe <main+104>:  call   0x80483d8 <wcscmp@plt>
0x08048603 <main+109>:  test   %eax,%eax
0x08048605 <main+111>:  jne    0x804860c <main+118>
0x08048607 <main+113>:  call   0x80484b4 <win>
0x0804860c <main+118>:  movl   $0x8048795,(%esp)
0x08048613 <main+125>:  call   0x80483e8 <puts@plt>
0x08048618 <main+130>:  mov    -0x4(%ebp),%eax
0x0804861b <main+133>:  leave
0x0804861c <main+134>:  ret
End of assembler dump.
Looking through the assembly quickly. The last function called before <win> is "wcscmp". It is likely that this function is where the comparison takes place. Also, we need %eax to be 0 to move on to the call <win> function.

test %eax, %eax will set the ZF = 1 if %eax =0. (test ANDs the 2 operands and sets ZF=1 if the AND is 0)

Disassemble the function:

Dump of assembler code for function wcscmp:
0x00b8b4d0 <wcscmp+0>:  push   %ebp
0x00b8b4d1 <wcscmp+1>:  xor    %edx,%edx
0x00b8b4d3 <wcscmp+3>:  mov    %esp,%ebp
0x00b8b4d5 <wcscmp+5>:  push   %edi
0x00b8b4d6 <wcscmp+6>:  push   %esi
0x00b8b4d7 <wcscmp+7>:  mov    0x8(%ebp),%edi
0x00b8b4da <wcscmp+10>: mov    0xc(%ebp),%esi
0x00b8b4dd <wcscmp+13>: lea    0x0(%esi),%esi
0x00b8b4e0 <wcscmp+16>: mov    (%edi,%edx,1),%eax
0x00b8b4e3 <wcscmp+19>: mov    (%esi,%edx,1),%ecx
0x00b8b4e6 <wcscmp+22>: test   %eax,%eax
0x00b8b4e8 <wcscmp+24>: je     0xb8b4f8 <wcscmp+40>
0x00b8b4ea <wcscmp+26>: add    $0x4,%edx
0x00b8b4ed <wcscmp+29>: cmp    %ecx,%eax
0x00b8b4ef <wcscmp+31>: je     0xb8b4e0 <wcscmp+16>
0x00b8b4f1 <wcscmp+33>: sub    %ecx,%eax
0x00b8b4f3 <wcscmp+35>: pop    %esi
0x00b8b4f4 <wcscmp+36>: pop    %edi
0x00b8b4f5 <wcscmp+37>: pop    %ebp
0x00b8b4f6 <wcscmp+38>: ret
0x00b8b4f7 <wcscmp+39>: nop
0x00b8b4f8 <wcscmp+40>: mov    %ecx,%eax
0x00b8b4fa <wcscmp+42>: neg    %eax
0x00b8b4fc <wcscmp+44>: pop    %esi
0x00b8b4fd <wcscmp+45>: pop    %edi
0x00b8b4fe <wcscmp+46>: pop    %ebp
0x00b8b4ff <wcscmp+47>: ret
End of assembler dump.
Assembly interpretation:
0x00b8b4d0 <wcscmp+0>:  push   %ebp
0x00b8b4d1 <wcscmp+1>:  xor    %edx,%edx
0x00b8b4d3 <wcscmp+3>:  mov    %esp,%ebp
0x00b8b4d5 <wcscmp+5>:  push   %edi
0x00b8b4d6 <wcscmp+6>:  push   %esi
xor %edx,%edx makes the %edx register = 0. Later, the register is used as a counter.
Main's %edi and %esi are pushed because the current function i.e. "wcscmp" will be using them later on for the comparison.
0x00b8b4d7 <wcscmp+7>:  mov    0x8(%ebp),%edi
0x00b8b4da <wcscmp+10>: mov    0xc(%ebp),%esi
0x00b8b4dd <wcscmp+13>: lea    0x0(%esi),%esi
The arguments passed by the main function are loaded into %edi and %esi. The argument-passing instructions in the main function are the two instructions above the call to wcscmp:
0x080485ef <main+89>:   movl   $0x8049140,0x4(%esp)
0x080485f7 <main+97>:   movl   $0x80491a0,(%esp)
0x080485fe <main+104>:  call   0x80483d8 <wcscmp@plt> 
Next sequence of instructions:
0x00b8b4e0 <wcscmp+16>: mov    (%edi,%edx,1),%eax
0x00b8b4e3 <wcscmp+19>: mov    (%esi,%edx,1),%ecx
0x00b8b4e6 <wcscmp+22>: test   %eax,%eax
0x00b8b4e8 <wcscmp+24>: je     0xb8b4f8 <wcscmp+40>
The first characters of the input and the internal pass string are loaded into %eax and %ecx. Note that %edx=0 since it was XORed with itself at the start of the function. If %eax=0 i.e. it contains a null character, the program jumps to:
0x00b8b4f8 <wcscmp+40>: mov    %ecx,%eax
0x00b8b4fa <wcscmp+42>: neg    %eax
0x00b8b4fc <wcscmp+44>: pop    %esi
0x00b8b4fd <wcscmp+45>: pop    %edi
0x00b8b4fe <wcscmp+46>: pop    %ebp
0x00b8b4ff <wcscmp+47>: ret
%eax will be set to a non-zero integer. Then the function returns after the cleanup. If this happens, the test %eax,%eax in the main function will fail and the "win" function will not be called.

The remaining piece of instructions is the most interesting:
0x00b8b4ea <wcscmp+26>: add    $0x4,%edx
0x00b8b4ed <wcscmp+29>: cmp    %ecx,%eax
0x00b8b4ef <wcscmp+31>: je     0xb8b4e0 <wcscmp+16>
0x00b8b4f1 <wcscmp+33>: sub    %ecx,%eax
0x00b8b4f3 <wcscmp+35>: pop    %esi
0x00b8b4f4 <wcscmp+36>: pop    %edi
0x00b8b4f5 <wcscmp+37>: pop    %ebp
0x00b8b4f6 <wcscmp+38>: ret
0x00b8b4f7 <wcscmp+39>: nop
Counter is increased (%edx+4) so that the next character will be fetched in the next round. Both the characters are compared by the instruction cmp %ecx,%eax and if they are equal the program jumps back to fetch the next character and compare them and so on. Once the strings end, the jump will not be taken and %eax will be set to 0 by subtracting it with %ecx. After restoring main's registers, the program returns with %eax=0. The jump is not taken in the main function and the "win" function is called.

0 Comments:

Post a Comment