郑重声明:文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,如果您不同意请关闭该页面!任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

前言

缓冲了2个月重新捡起来,有些地方还是没太懂 看来还是二次搁浅了

image-20210517140400619

关于几个知识点

我们在进行堆学习的时候那么就是主要针对的是Linux的动态链接库也就是glibc,但是由于一个系统只存在一个glibc,我们要是进行切换的话会很麻烦,所以下面记几个知识点

libc-database

有一些PWN题故意不给libc文件,但是有泄露地址,libc database可以利用泄露的地址来确定服务器使用的libc。

git clone https://github.com/niklasb/libc-database
cd libc-database
./get all #下载全部libc文件
./get Ubuntu #下载Ubuntu所有libc文件

程序会自动在ubuntu网站上下载相关的libc文件,存储到./db文件夹下

glibc-all-in-one

里面有对libc的库,官方文档听清楚的我就不概述了

git clone https://github.com/matrix1001/glibc-all-in-one
cd glibc-all-in-one
./update_list

patchelf

最快的安装方式apt install patchelf,然后替换libc文件

patchelf --set-interpreter /home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/ld-2.26.so --replace-needed libc.so.6 /home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so  ELF
  • –set-interpreter:设置动态库解析器
  • –replace-needed:替换旧的动态库为新的

TCache

由于glibc-2.26(ubuntu 17.10)加入了TCache机制而有了较大的变化(see commit),TCache全名为Thread Local Caching,它为每一个线程创建一个缓存,里面包含了一些小堆块,无须对arena上锁即可使用,这种无锁的分配算法有不错的性能提升。

  • 每个线程默认使用64个单链表结构的bins,每个bins最多放7个chunk
  • chunk在64机器上已16字节递增,从24到1032字节
  • chunk在32机器上已8字节递增,从12到512字节
  • TCache bin只能存放non-large(非大型)的chunk

相关结构体

tcache引入了两个新的结构体,tcache_entrytcache_perthread_struct

tcache_entry

//https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#tcache_entry
/* We overlay this structure on the user-data portion of a chunk when
the chunk is stored in the per-thread cache. */
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;

tcache_entry 用于链接空闲的 chunk 结构体,其中的 next 指针指向下一个 chunk。

tcache_perthread_struct

//https://code.woboq.org/userspace/glibc/malloc/malloc.c.html#tcache_perthread_struct
/* There is one of these for each thread, which contains the
per-thread cache (hence "tcache_perthread_struct"). Keeping
overall size low is mildly important. Note that COUNTS and ENTRIES
are redundant (we could have just counted the linked list each
time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

# define TCACHE_MAX_BINS 64

static __thread tcache_perthread_struct *tcache = NULL;

该数据结构位于堆开头的位置,这说明它本身就是一个堆块,大小为0x250

CVE-2017-17426

在libc-2.26中由于__libc__malloc()使用request2size()来请求大小转换为实际大小,该函数不会进行整数溢出检查,所以如果请求一个很大的块(接近SIZE_MAX)那么就会导致整数溢出,从而导致malloc错误的返回了tcache bin里的堆块

//CVE-2017-17426.c
#include <stdio.h>
#include <stdlib.h>

int main() {
void *x = malloc(10);
printf("malloc(10): %p\n", x);
free(x);

void *y = malloc(((size_t)~0) - 2); // overflow allocation (size_t.max-2)
printf("malloc(((size_t)~0) - 2): %p\n", y);
}
//gcc CVE-2017-17426.c -o CVE-2017-17426

使用上面的小知识点进行libc-2.26替换

$ patchelf --set-interpreter /home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/ld-2.26.so --replace-needed libc.so.6 /home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so  CVE-2017-17426
$ ldd CVE-2017-17426
linux-vdso.so.1 (0x00007fff8017b000)
/home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/libc-2.26.so (0x00007f8359065000)
/home/ascotbe/Desktop/PWN/glibc-all-in-one/libs/2.26-0ubuntu2_amd64/ld-2.26.so => /lib64/ld-linux-x86-64.so.2 (0x00007f8359453000)
$ ./CVE-2017-17426
malloc(10): 0x55f81f481260
malloc(((size_t)~0) - 2): 0x55f81f481260

接着替换为libc-2.27

$ ./CVE-2017-17426 
malloc(10): 0x564e19e352a0
malloc(((size_t)~0) - 2): (nil)

tcache poisoning

参考文章

https://ctf-wiki.org/pwn/linux/glibc-heap/implementation/tcache/