内容简介:在学pwn的道路上,我们大多从linux入手,从栈到堆,各种漏洞利用,都和Glibc或多或少打过交道。我的堆入门应该和很多人一样是从libc2.23开始的,之后又经历了各种libc版本的变化,随着现在的pwn题越来越与时俱进,我们会逐渐接触更新的libc版本,因此,我们必须知道,Glibc中堆管理变化了什么,从安全角度,我们的得失又是什么呢?从libc2.27开始,我们聊一聊Glibc中堆管理的漏洞利用的得失。关键字: CTF pwn 新版本libc libc 2.27 libc 2.29 libc 2.3
前言
在学pwn的道路上,我们大多从 linux 入手,从栈到堆,各种漏洞利用,都和Glibc或多或少打过交道。我的堆入门应该和很多人一样是从libc2.23开始的,之后又经历了各种libc版本的变化,随着现在的pwn题越来越与时俱进,我们会逐渐接触更新的libc版本,因此,我们必须知道,Glibc中堆管理变化了什么,从安全角度,我们的得失又是什么呢?从libc2.27开始,我们聊一聊Glibc中堆管理的漏洞利用的得失。
关键字: CTF pwn 新版本libc libc 2.27 libc 2.29 libc 2.30 堆溢出
GLibc2.27
Glibc2.23我就不想多说了,感兴趣的朋友可以学一学pwn相关的堆漏洞利用知识,网上现在总结的也算是比较多。我就不赘述了。从Glibc2.27开始,发生了很多有趣的地方,我们一起聊一聊。
Tcache
Tcache可是说是Glibc2.27中一个大的改变,其实Tcache的引入是从Glibc2.26开始的。但是(以下个人见解)Linux中比较受欢迎的发行版,ubuntu 18.04中的libc版本是2.27,再加上很多发行版都是2.27版本,所以,我们常见的pwn题也就在这种环境下编译开发了,因此,我们直接说说2.27版本,跳过2.26版本。
我认为Tcache使得漏洞利用变简单了,其得失我总结了一下:
漏洞利用最后一哆嗦,特别简单暴力
Tcache的管理结构在堆上,比main_arena好搞一点,毕竟Libc地址一般比堆地址难搞到
Tcache有时候使得泄露Libc地址变得困难
这里我说下Tcache的机制,tcache就是一个为了内存分配速度而存在的机制,当size不大(这个程度后面讲)堆块free后,不会直接进入各种bin,而是进入tcache,如果下次需要该大小内存,直接讲tcache分配出去,是不是感觉和fastbin蛮像的,但是其size的范围比fastbin大多了,他有64个bin链数组,也就是(64+1)*size_sz*2,在64位系统中就是0×410大小,有图有真相:
也就是说,在64位情况下,tcache可以接受0×20~0×410大小的堆块。
Tcache poisoning
那么Tcache对漏洞利用来说,不像fastbin attack一样,需要寻找合适的size了, 在2.27的环境下是可以直接做到任意地址写的 ,这一点非常nice, 这种利用方法,在也被叫做 tcache poisoning 。同时,在double free领域,Tcache可以直接double free,而不需要像fastbin那样,需要和链上上一个堆块不一样,也就是下面这个样子。
/* heap0 ----> heap1 ----> heap0 (fastbin YES) heap0 ----> heap0 (fastbin NO) heap0 ----> heap0 (Tcahce YES) */
还有一点不同,就是在Tcache中, fd指向的并不是堆头,而是堆内容 ,这一点也是需要我们注意的。
leak libc地址
单纯在堆中leak libc地址,一般是使用size大于fastbin范围的堆块,而在有tcache的情况下,这个变得相较之前困难,我将我目前用的比较多的方法总结如下:
1、申请8个大堆块,释放8个,这里堆块大小,大于fastbin范围,就是填满tcache。
2、有double free的情况下,连续free 8次同一个堆块,这里堆块大小,大于fastbin范围。
3、申请大堆块,大于0×410。
4、修改堆上的Tcache管理结构
大致就是以上几种方法,如果还有其他的想法,欢迎交流。
Tcache Stashing Unlink Attack
网上有很多人在分析这一漏洞的时候,都是基于libc2.29分析的,其实在libc2.27中,这一漏洞就已经存在了。
这里简单讲下,这是small bin 中的检查,即:__glibc_unlikely(bck->fd != victim)
// 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin;
我们来看看Tcache中的情况:
#if USE_TCACHE /* While we're here, if we see other chunks of the same size, stash them in the tcache. */ size_t tc_idx = csize2tidx (nb); if (tcache && tc_idx < mp_.tcache_bins) { mchunkptr tc_victim; /* While bin not empty and tcache not full, copy chunks over. */ while (tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = last (bin)) != bin) { if (tc_victim != 0) { bck = tc_victim->bk; set_inuse_bit_at_offset (tc_victim, nb); if (av != &main_arena) set_non_main_arena (tc_victim); bin->bk = bck; bck->fd = bin;//0 tcache_put (tc_victim, tc_idx);//1 } } } #endif
那么,我们需要注意2个地方,就是我在源码中标注的0和1。那么这两个地方由于没有任何检查,导致了两个问题,1、任意地址写libc地址,2、将任意地址放入tcache。
那么这段的逻辑是什么呢,简单来说,当我们从smallbin中申请了一个chunk后,会将此大小的tcache用smallbin里的堆块填满。
我们来看看什么时候,终止填入呢,两个条件:tcache->counts[tc_idx] >= mp_.tcache_count || (tc_victim = last (bin)) == bin就是上述while循环中的相反的条件。也就是说,如果smallbin里没heap了或者tcache填满了,就不需要继续填充了,但是由于我们期望漏洞利用,所以需要改掉bck,这就导致(tc_victim = last (bin)) == bin这个条件是很难达到的。所以,我们需要控制tcache中的数量,但是,这里又出现了一个矛盾,那就是如果Tcache不为空,就不会从smallbin中取出堆块。
所以,综上所述,只有绕过tcache的calloc能够符合这样的要求,那么,如果,我们想要任意地址写libc,就在tcache中留一个空间,如果期望任意地址放入tcache,就在tcache中留两个空间,同时,我们需要清楚,动手脚的small bin 应该是倒数第二个smallbin。
画个图示意一下:
将Chunk1的bk指向目标地址,再calloc一个0xa0大小的chunk,参照上述的目的,确定自己需要在Tcache中留几个heap。
Tcache结构破坏
这个其实没什么好说的,只是一个tips吧,tcache的管理结构在堆上,再加上tcache宽松的检查条件,其实有时候搞一搞这里还是蛮有意思的。
libc2.27中的东西基本就讲这些了,接下来就是libc2.29了
Glibc2.29
在2.27的基础上,我们看看2.29做了哪些改变:
Tcache的double free防护
首先是一个对漏洞利用者较为遗憾的改动,就是在tcache的结构体上,加了一个key。
在官方注释上,这一增加是为了检测tcache的double free,在2.27的libc中,tcache为了速度,几乎没有什么安全保护,这一机制会缓解部分漏洞利用。那么,这一增加如何作用呢,我们可以看到,在tcache_put中,对这一结构体进行了赋值,赋值的内容就是定义的tcache_perthread_struct结构体tcache的地址,tcache就是通过这一函数来判断当前的heap是否在tcache中,当然,在tcache_get中,也会将其清理。同时在free中加了这么一段。
if (__glibc_unlikely (e->key == tcache)) { tcache_entry *tmp; LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx); for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next) if (tmp == e) malloc_printerr ("free(): double free detected in tcache 2"); /* If we get here, it was a coincidence. We've wasted a few cycles, but don't abort. */ }
也就是说在free时,如果当前的chunk的bk位置是tcache这一地址,那么就会循环检测当前大小的tcache的链表,查看链表中是否存在当前的chunk。所以,想要double free前,记得先改一下bk。
unlink前操作
在free的时候,unlink前新加了一个检查,这个不太致命,注意绕过即可。
/* consolidate backward */ if (!prev_inuse(p)) { prevsize = prev_size (p); size += prevsize; p = chunk_at_offset(p, -((long) prevsize)); if (__glibc_unlikely (chunksize(p) != prevsize))//add malloc_printerr ("corrupted size vs. prev_size while consolidating");//add unlink_chunk (av, p); }
unsortbin保护
不说了,unsortbin attack我先不用了,总可以了吧(含泪)。
if (__glibc_unlikely (size <= 2 * SIZE_SZ) || __glibc_unlikely (size > av->system_mem)) malloc_printerr ("malloc(): invalid size (unsorted)"); if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ) || __glibc_unlikely (chunksize_nomask (next) > av->system_mem)) malloc_printerr ("malloc(): invalid next size (unsorted)"); if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size)) malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)"); if (__glibc_unlikely (bck->fd != victim) || __glibc_unlikely (victim->fd != unsorted_chunks (av))) malloc_printerr ("malloc(): unsorted double linked list corrupted"); if (__glibc_unlikely (prev_inuse (next))) malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)"); ...... ...... /* remove from unsorted list */ if (__glibc_unlikely (bck->fd != victim)) malloc_printerr ("malloc(): corrupted unsorted chunks 3"); unsorted_chunks (av)->bk = bck; bck->fd = unsorted_chunks (av);
libc 2.30
那么到了libc2.30其实增加的东西也是不多了。
largebin attack
在largebin 中,加了这个,刚好对largbin的bk和bk_nextsize做出了限制。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 技术讨论 | 自动化Web渗透Payload提取技术
- 技术讨论 | 如何编写一段内存蠕虫?
- 技术讨论 | Largebin attack漏洞利用分析
- 技术讨论 | 简谈渗透测试的些许基础
- 技术讨论 | 基于基站定位APP开发实录
- 技术讨论 | 记一次XSS蠕虫渗透实验
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP 5完全攻略
杜江 / 2010-5 / 79.00元
《PHP 5完全攻略(畅销书升级版)》是目前第一本真正介绍PHP 5及MySQL 5新增语法与功能的中文版本权威宝典!《PHP 5完全攻略(畅销书升级版)》本着精、全、要三宗旨,从理论中延伸,从实践中深入,翔实并完善地描述了PHP 5的开发特性与MySQL 5数据库。《PHP 5完全攻略(畅销书升级版)》分为两大部分,第1部分主要阐述PHP开发的基础知识,如PHP数组与表单处理、PHP 5面向对象......一起来看看 《PHP 5完全攻略》 这本书的介绍吧!