Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

$
0
0
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

2017-04-28 10:58:33

阅读:1094次
点赞(0)
收藏




【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

作者:arnow117





【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

翻译:arnow117

预估稿费:300RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


背景介绍

jemalloc的相关研究

argp与huku在2012年在Phrack上发表的:对jemalloc内存分配器的单独利用(做出了基于FreeBSD上libc的POC)。

argp与huku在2012年BlackHat上发表的:在Firefo中玩坏jemalloc的元数据。

argp在2015年INFILTRATE上的jemalloc漏洞利用方法论。

Android堆漏洞利用的相关研究

Hanan Be'er对CVE-2015-3864这个stagefright中整形溢出导致堆破坏的漏洞利用

Aaron Adams的对这个漏洞的又一次利用

Joshua Drake对于stagefright漏洞利用相关工作 向之前的研究者们致谢!(这也是为什么要翻译这一段之必要)

配合jemalloc使用的插件:Shadow

注,因为本文核心是jemalloc与堆漏洞利用,此章节关于对关于插件shadow的历史部分没有翻译。

shadow是CENSUS开发的的一个基于jemalloc的堆漏洞利用框架,开源在Github(传送门)上。用来搭配调试器提供jemalloc分配器的内部结构信息。可以作为插件在GDB,WINDBG,以及lldb中使用。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

这个框架有几个优点:

没有多余的要附加的源文件。

对于Android与Firefox两个平台,使用相同的指令。

简化的调试引擎。

提供堆快照支持。

(gdb)jeparse-f (gdb)jestore/tmp/snapshot1

提供单独的脚本,允许进行非运行时的堆排布分析。单独使用时样例:

$pythonshadow.py/tmp/snapshot1jeruns-c listingcurrentrunsonly [arena00(0x0000007f85680180)][bins36] [run0x7f6ef81468][regionsize08][totalregions512][freeregions250] [run0x7f6e480928][regionsize16][totalregions256][freeregions051] [run0x7f6db81888][regionsize32][totalregions128][freeregions114] ...

提供对于堆中内存排布的解析脚本。作为Python插件包时的使用样例:

//code importjemalloc heap=jemalloc.jemalloc("/tmp/snapshot1") forchunkinheap.chunks: print"chunk@0x%x"%chunk.addr //run $pythonprint_chunks.py chunk@0x7f6d240000 chunk@0x7f6db00000 chunk@0x7f6db40000 chunk@0x7f6db80000 chunk@0x7f6dbc0000 ...

jemalloc

jemalloc的一些特性

jemalloc使用bitmap管理堆分配,而不是通过内存的利用率,这也可能是jemalloc被广泛使用的主要原因。当下FreeBSD libc,Firefox,Android libc,mysql,Redis以及Facebook内部都在用。

设计原则

最小化的元数据开销

基于每个线程进行缓存,避免了同步问题。

避免了连续分配内存的碎片化问题。

简洁高效(所以就可以预判了哦呵呵)

Android中的jemalloc

在Android 6使用的版本实际上是4.0.0,在Android 7 上使用的版本是4.1.0-4-g33184bf69813087bf1885b0993685f9d03320c69

jemalloc在Android源码中的修改通过宏定义开关控制的代码块来实现,同时辅以/* Android change */的注释

#ifdefined(__ANDROID__)/*ANDROIDchange*/ /*…*//*…*/ #endif/*EndANDROIDchange*/

在jemalloc的Android.mk中限制了仅使用两个arenas,并且开启了线程缓存(PS:本文的讨论基于64位的架构)

//partofAndroid.mk jemalloc_common_cflags+=\ -DANDROID_MAX_ARENAS=2\ -DJEMALLOC_TCACHE\ -DANDROID_TCACHE_NSLOTS_SMALL_MAX=8\ -DANDROID_TCACHE_NSLOTS_LARGE=16\

jemalloc内部结构


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

概念:region

调用malloc返回给用户的实际内存

在内存中连续分布

不包含元数据

根据大小不同,划分为三种类型:

Small,最大0x14336字节

Large,最大0x38000字节(Android 6上)

Huge,大于0x38000

可以使用shadow中的jebininfo列出当前线程所有的region,或者jesize列出满足给定size的region相关信息

//jebinfo (gdb)jebininfo [bin00][regionsize008][runsize04096][nregs0512] [bin01][regionsize016][runsize04096][nregs0256] [bin02][regionsize032][runsize04096][nregs0128] [bin03][regionsize048][runsize12288][nregs0256] [bin04][regionsize064][runsize04096][nregs0064] [bin05][regionsize080][runsize20480][nregs0256] [bin06][regionsize096][runsize12288][nregs0128] [bin07][regionsize112][runsize28672][nregs0256] //jesize (gdb)jesize24 [bin02][regionsize032][runsize04096][nregs0128]
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

线程申请memory时,与region的对应关系。

概念:run

存放连续的大小相同的region的容器

一系列连续的页集合

内部存放small/large类型的region

没有元数据

查看给定的地址所属的run中的region信息

(gdb)jerun0x7f931c0628 [region000][used][0x0000007f931cc000][0x0000000070957cf8] [region001][used][0x0000007f931cc008][0x0000000070ea78b0] [region002][used][0x0000007f931cc010][0x0000000070ec2868] [region003][used][0x0000007f931cc018][0x0000000070f0322c] ... (gdb)x/4gx0x7f931cc000 0x7f931cc000:0x0000000070957cf80x0000000070ea78b0 0x7f931cc010:0x0000000070ec28680x0000000070f0322c ...
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

线程申请memory时,与run的对应关系。

概念:chunk

存放run的容器

大小固定相同

操作系统返回的内存被划分到chunk中管理

存储着关于自身以及它管理的run的元数据


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

chunks的结构,与run以及元数据的关系。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS
chunks中的元数据结构,mapbit[0]与mapmisc[0]指向chunk中的第一个run。
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

chunks元数据中mapmisc中的bitmap结构管理着run中的region的分配使用。

变化: 不同Android版本下的jemalloc

Chunk的大小


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

元数据的变化

增加了mapbias与mapbits flags

堆中的jemalloc

root@bullhead/:cat/proc/self/maps|greplibc_malloc 7f81d00000-7f81d80000rw-p0000000000:000[anon:libc_malloc] 7f82600000-7f826c0000rw-p0000000000:000[anon:libc_malloc] 7f827c0000-7f82a80000rw-p0000000000:000[anon:libc_malloc] 7f82dc0000-7f830c0000rw-p0000000000:000[anon:libc_malloc] (gdb)jechunks [shadow][chunk0x0000007f81d00000][arena0x0000007f996800c0] [shadow][chunk0x0000007f81d40000][arena0x0000007f996800c0] [shadow][chunk0x0000007f82600000][arena0x0000007f996800c0] [shadow][chunk0x0000007f82640000][arena0x0000007f996800c0] [shadow][chunk0x0000007f82680000][arena0x0000007f996800c0] [shadow][chunk0x0000007f827c0000][arena0x0000007f996800c0] ...

可以看到maps中0x7f81d00000对应的memory,属于两个chunk,分别位于0x7f81d00000以及0x7f81d40000。

jemalloc的内存排布


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

jemalloc管理下的内存排布


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

溢出了region的示意图


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

如果溢出了run的示意图


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

如果溢出了chunk的示意图,注意到,chunk头部有元数据。

基于jemalloc的堆喷

Hanan Be'er, Aaron Adams, Mark Brand, Joshua Drake都讨论过

region与run都没有元数据

堆喷的时候,chunk的第一个与最后一个页是不可喷的

chunk的地址是可以预测的


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

chunk中可堆喷的区域大小示意图

chunk地址可预测?

Google ProjectZero的Mark Brand曾经有说过

32位上:大chunk,而地址空间较小

mmap()产生多个chunk,而chunk大小固定。

Andorid进程通常加载很多模块

Android 7的chunk更大

同样适用于申请巨大的内存

可预测的chunk地址意味着

可预测的run地址

可预测的region地址

这些可以让我们做更有目的性的,更加精确的堆喷

jemalloc的内存管理

arena

arena内存申请器

用来缓解线程间申请memory时的竞争问题

每一个arena彼此独立,管理各自的chunk

每个线程在第一次malloc时,建立起与各自的arena的联系,一个线程只指向一个arena

每个进程中,arena的数量由jemalloc配置决定,在Android上硬编码为两个。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

在malloc申请内存中,arena与线程缓存的关系。

申请的memory在jemalloc内部实际是通过arena申请的,且在每一个线程中都有一个缓存。

查看进程的arena

//common (gdb)x/2gxarenas 0x7f99680080:0x0000007f997c01800x0000007f996800c0 //usingshadow (gdb)jearenas [jemalloc][arenas02][bins36][runs1408] [arena00(0x0000007f997c0180)][bins36][threads:1,3,5] [arena01(0x0000007f996800c0)][bins36][threads:2,4] arenabin

每个arena都有一个bin数组

每一个bin对应着一种small类型,大小固定的region。

同时bin数组还肩负着用树存储未满的run的职责,并选一个作为当前指向的run

查看arena bin,runcur为对应region所属run的地址

(gdb)jebins [arena00(0x7f997c0180)][bins36] [bin00(0x7f997c0688)][sizeclass08][runcur0x7f83080fe8] [bin01(0x7f997c0768)][sizeclass16][runcur0x7f82941168] [bin02(0x7f997c0848)][sizeclass32][runcur0x7f80ac0808] [bin03(0x7f997c0928)][sizeclass48][runcur0x7f81cc14c8] [bin04(0x7f997c0a08)][sizeclass64][runcur0x7f80ac0448] ...

查看当前run,以及其中region的信息。

(gdb)jeruns-c [arena00(0x7f997c0180)][bins36] [run0x7f83080fe8][regionsize08][totalregions512][freeregions158] [run0x7f82941168][regionsize16][totalregions256][freeregions218] [run0x7f80ac0808][regionsize32][totalregions128][freeregions041] [run0x7f81cc14c8][regionsize48][totalregions256][freeregions093] [run0x7f80ac0448][regionsize64][totalregions064][freeregions007] ...

通过arena申请内存流程


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS
申请size为8字节的memory时,先查bin,发现bin[0]所代表size为8的small region可以装的下,则查找对应存放这个连续region的run,并从中分配一块region返回。

通过arena释放内存流程


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

free与申请类似,查找到存放region的run,然后释放这个region。

arena中的线程缓存

什么是线程缓存(tcache)


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

arena与线程缓存的流程关系。

每一个线程维护着一个对small/large内存申请的缓存

对缓存的操作与栈相似

以申请时间为衡量的增长式“垃圾回收”机制


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS
线程缓存栈以及其指向的run中region示意图,tbins[0]中存储着对应size的region缓存栈,每一种size的tbin中存储着其size下对应的缓存栈。

线程缓存在申请内存时候的作用


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

还是刚才malloc的图,加上了tcache,可以看到,没有直接去通过arena要region,而是先去查对应size的tbin缓存栈avail去了。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

在缓存栈中,弹出一个最近被free“回收”到缓存栈上的内存地址做新malloc的返回地址。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

按照如此大小一直申请,最终栈会弹空。之后arena再通过元数据向run中要对应size的region,申请的数量是lg_fill_div,将返回的内存地址再压入缓存栈。

线程缓存在释放内存时的作用


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

释放与申请类似,只不过变成了将释放的地址压入缓存栈。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

同样,缓存栈满了后arena也会将对应region还回去,但是每次只还一半。申请时间久的先被归还回去。缓存栈的容量在结构体tcache_bin_info中有定义。

tcache中的数据结构

structtcache_s{ ... tcache_bin_ttbins[]; /*cachedallocation pointers(stacks)*/ }; structtcache_bin_s{ ... unsignedlg_fill_div; unsignedncached; void**avail; }; //tcache_bin_s就是tcache_bin_t 以上这些结构体的内存,是通过arenas[0]分配得到的。

每个线程的TSD中也会存着指向这些结构的指针。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

内存中的tbin与其avail指针


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

如何从线程中找到tcache,x0就是线程结构体的地址,其中key_data就是线程特有数据(也叫TSD)的指针,所以这里存放的就包含了tcache的地址。从shadow中可以看到TSD是在size类型为0x80的run中的。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

TSD中存放的tcache与arena的示意。从shadow中可以看到tcache是在size类型为0x1c00的run中的。

如果把tcache溢出了?

这些信息在arenas[0]中存放

tcache在size类型为0x1c00的run里分配,很难去找对并操作

但是这种情况有可能的

需要创建或者销毁线程


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

那如果吧TSD溢出了呢?

TSD在size类型为0x80的run里分配,很难去找对并操作

这种情况有可能,但是也难达到

需要创建或者破坏线程相关信息


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

小结:jemalloc内部结构在堆中的布局

jemalloc中固定的部分有

arena的大小

tcache的大小

arena与线程的关联部分(比如TSD)的大小

结构地址随机化

但是有一点值得注意,线程缓存使得访问相邻的region更加容易


利用shadow搞事情!

基于double free的利用姿势

为什么要用这个呢,是因为之前我们没有在jemalloc里实践过这样的姿势

而且这个姿势在Android和Firefox都有通用的代码模式

可以很通用的使用

在第一次free对象后,控制之后的两次申请

只要申请相同大小就可以进行利用


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

double free的示例代码


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

申请到了0x7f8fed1000,看看此时的tcache。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

0x7f8fed1000压入tcache


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

受我们控制的第二次申请,又拿到了0x7f8fed1000


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

地址还回去,但是指针你留下来。最后我们用这个函数指针跳向我们想去的地方


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

给函数指针赋值。

任意地址free的利用前提

不是简单的原型,通常是有缺陷的清理逻辑(比如对树中节点的移除)。

jemalloc对于free传入的地址没有很好的检查

Android加入的检查可以被绕过

释放后会把地址压入对应的线程缓存栈

释放时候页索引检查代码段:

chunk=(arena_chunk_t*)CHUNK_ADDR2BASE(ptr); if(likely(chunk!=ptr)){ pageind=((uintptr_t)ptr-(uintptr_t)chunk)>>LG_PAGE; #ifdefined(__ANDROID__) /*Verifytheptrisactuallyinthechunk.*/ if(unlikely(pageind<map_bias||pageind>=chunk_npages)){ __libc_fatal_no_abort(...) return; } #endif /*chunksize_mask=chunksize-1*/ #defineLG_PAGE12 #defineCHUNK_ADDR2BASE(a)((void*)((uintptr_t)(a)&~chunksize_mask))

再来看看chunk的排布


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

chunk中对于mapbits的检查

mapbits=arena_mapbits_get(chunk,pageind); assert(arena_mapbits_allocated_get(chunk,pageind)!=0); #ifdefined(__ANDROID__) /*Verifytheptrhasbeenallocated.*/ if(unlikely((mapbits&CHUNK_MAP_ALLOCATED)==0)){ __libc_fatal(...); } #endif if(likely((mapbits&CHUNK_MAP_LARGE)==0)){ /*Smallallocation.*/ /*...*/ #defineCHUNK_MAP_ALLOCATED((size_t)0x1U) #defineCHUNK_MAP_LARGE((size_t)0x2U)

把这两个检查绕过,就可以任意地址进行free了,当然我们就可以传入一个从run中拿到的地址。也就是说,我们可以释放并往tcache里面压栈一个非对齐的region指针,但是有一个字节会被破坏。最后重新申请被free的region就会导致溢出到下一个region,如下图所示。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

利用案例

boot.oat 里面有Android框架层的所有编译的native代码,在启动时候随机化加载。

boot.art 装载着一系列栈初始化类信息,以及相关的对象。

加载地址对每一个设备来说地址固定,由第一次启动时决定

包含着指向boot.oat的指针


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

在boot.art中我们找到一个函数指针0x713b6c40,我们先来分别计算mapbits,以及pagind,可以看到其绕过了这两个检查,注意64bit下的一些常量。

利用流程

1. 把这个在boot.art中指向boot.oat的地址通过free压入缓存栈

2. malloc后从缓存栈中弹出这个地址

3. 把想要控制的PC的值写进新申请的memory里面,覆盖某个当前的函数指针

4. 等风来,调用这个函数指针。

如何找boot.art中的地址

用shadow的jefreecheck找到可以被free的地址

确保这个地址中存储的函数指针会被调用

(gdb)jefreecheck-b0boot.art searchingsystem@framework@boot.art(0x708ce000-0x715c2000) [page0x712cf000] +0x712cf000 +0x712cf028 +0x712cf038 +0x712cf060 +0x712cf070 ...
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

为了举例方便,在这里面我们用gdb直接向malloc得到后的问题地址0x713b6c40写入非法值。可以看到0x713b6c40这个地址存储的是一个函数指针。


【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS

free这个地址后,通过malloc再获得这个地址,然后向这个地址所指向的内存写一些值,比如AAAAA,我们便成功的控制了PC。



【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS
【技术分享】基于jemalloc的Android漏洞利用技巧----CENSUS
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://census-labs.com/media/shadow-infiltrate-2017.pdf

Viewing all articles
Browse latest Browse all 12749

Trending Articles