title: CVE-2015-3864漏洞利用分析(exploit_from_google)

author: hac425

tags:

  • CVE-2015-3864
  • 文件格式漏洞

    categories:
  • 安卓安全

    date: 2017-11-21 23:17:00

前言

接下来学习安卓的漏洞利用相关的知识,网上搜了搜,有大神推荐 stagefright 系列的漏洞。于是开干,本文分析的是 googleexploit. 本文介绍的漏洞是 CVE-2015-3864 , 在 google的博客上也有对该 exploit 的研究。

我之前下载下来了:

pdf版本 的链接:在这里

exploit 的链接: https://www.exploit-db.com/exploits/38226/

分析环境:

  1. Android 5.1 nexus4

正文

这个漏洞是一个文件格式相关漏洞,是由 mediaserver 在处理 MPEG4 文件时所产生的漏洞,漏洞的代码位于 libstagefright.so 这个库里面。

要理解并且利用 文件格式 类漏洞,我们就必须要非常清楚的了解目标文件的具体格式规范。

Part 1 文件格式学习

先来一张总体的格式图

mp4 文件由 box 组成,图中那些 free, stsc等都是box, box 里也可以包含 box ,这种 box 就叫 containerbox .

  • 每个 box 前四个字节为 boxsize

  • 第二个四字节为 boxtypebox typeftyp,moov,trak 等等好多种,moovcontainerbox ,包含 mvhdtrakbox

还有一些要注意的点。

  • box 中存储数据采用大端字节序存储
  • size 域为 0时,表示这是文件最后一个 box
  • size 为1 时,表示这是一个 large box ,在 type 域后面的 8 字节 作为该 box 的长度。

下面来看两个实例。

实例一

  • size 域为 00000014,所以该 box长度为 0x14 字节。
  • type 域为 66 74 79 70 所以 typefytp
  • 剩下的一些信息是一些与多媒体播放相关的一些信息。与漏洞利用无关,就不说了。

实例二

  • size 域为1,表示从该 box 开头偏移8字节开始的8字节为 size 字段, 所以该 box 的大小为 0xFFFFFFFFFFFFFF88
  • typetx3g

现在我们对该文件的格式已经有了一个大概的了解,这对于漏洞利用来说还不够,接下来我们要去看具体的解析该文件格式的代码是怎么实现的。

解析文件的具体代码位于 MPEG4Extractor.cpp 中的 MPEG4Extractor::parseChunk 函数里面。

该函数中的 chunk 对应的就是 box, 函数最开始先解析 typesize .

  1. // 开始4字节为 box 大小, 后面紧跟的 4 字节为 box type
  2. uint64_t chunk_size = ntohl(hdr[0]);
  3. uint32_t chunk_type = ntohl(hdr[1]); //大端序转换
  4. off64_t data_offset = *offset + 8; // 找到 box 数据区的偏移
  5. // 如果size区为1, 那么后面8字节作为size
  6. if (chunk_size == 1) {
  7. if (mDataSource->readAt(*offset + 8, &chunk_size, 8) < 8) {
  8. return ERROR_IO;
  9. }
  10. chunk_size = ntoh64(chunk_size);
  11. data_offset += 8;
  12. if (chunk_size < 16) {
  13. // The smallest valid chunk is 16 bytes long in this case.
  14. return ERROR_MALFORMED;
  15. }
  16. } else if (chunk_size < 8) {
  17. // The smallest valid chunk is 8 bytes long.
  18. return ERROR_MALFORMED;
  19. }

通过注释和代码,我们知道对于 size 的处理和前面所述是一致的。然后就会根据不同的 chunk_type ,进入不同的逻辑,

如果 box 中还包含 子 box 就会递归调用该函数进行解析。

Part 2 漏洞分析

CVE-2015-3864 漏洞产生的原因是,在处理 tx3g box时,对于获取的 size 字段处理不当,导致分配内存时出现整数溢出,进而造成了堆溢出。

size 为之前所解析的所有 tx3g box 的长度总和。chunk_size 为当前要处理的 tx3g box 的长度。然后 size + chunk_size 计算要分配的内存大小。 chunk_sizeuint64_t 类型的,chunk_size 我们在文件格式中我们所能控制的最大大小为 0xFFFFFFFFFFFFFFFF ( 看 part1 实例二 ) ,也是 64 位,但是我们还有一个 size 为可以控制,这样一相加,就会造成 整数溢出 , 导致分配小内存。而我们的 数据大小则远远大于分配的内存大小,进而造成堆溢出

Part 3 漏洞利用

概述

现在我们已经拥有了堆溢出的能力,如果是在 ptmalloc 中,可以修改下一个堆块的元数据来触发 crash ,甚至可能完成漏洞利用。不过从 android 5开始,安卓已经开始使用 jemalloc 作为默认的堆分配器。

jemalloc 中,小内存分配采用 regions 进行分配, region 之间是没有 元数据 的 (具体可以去网上搜 jemalloc 的分析的文章),所以 在 ctf 中常见的通过修改 堆块元数据 的漏洞利用方法在这里是没法用了。

不过所有事情都有两面性。region 间是直接相邻的,那我就可以很方便的修改相邻内存块的数据。 如果我们在 tx3g 对应内存块的后面放置一个含有关键数据结构的内存块,比如一个对象,在 含有虚函数 的类的 对象开始4字节(32位下),会存放一个 虚表指针 .

对象 调用 虚函数 时会从 虚表指针 指向的位置的 某个偏移(不同函数,偏移不同) 处取到相应的函数指针,然后跳过去执行。

如果我们修改对象的虚表指针,我们就有可能在程序调用虚函数时,控制程序的流程。

一些重要的 chunk_type(box type)

tx3g box

上一节提到,我们可以修改对象的虚表指针,以求能够控制程序的跳转。那我们就需要找到一个能够在解析 box 数据能时分配的对象。

MPEG4DataSource 就是这样一个类。

可以看到该对象继承自 DataSource, 同时还有几个虚函数。

我们可以在ida中看看虚表的构成。

可以看到 readAt 方法在虚表的第7项,也就是虚表偏移 0x1c 处。同时MPEG4DataSource在我这的大小为 0x20 .再看一下漏洞位置的代码。

可以看到如果当前解析的 tx3g box 不是第一个tx3g box(即size>0),会先调用 memcpy , 把之前所有 tx3g box中的数据拷贝到刚刚分配的内存。

如果我们先构造一个 tx3g ,其中包含的数据大于 0x20, 然后在构造一个 tx3g 构造大小使得 size+chunk_size = 0x20, 然后通过 memcpy 就可以覆盖 MPEG4DataSource 的虚表了。exploit 中就是这样干的。

pssh box

看看代码

划线位置说明了 pssh 的结构。

  1. pssh 的结构
  2. 开始8字节 表示 box 的性质
  3. 00 00 00 40 70 73 73 68
  4. size: 0x40,
  5. type: pssh :
  6. + 0xc 开始 16字节 pssh.uuid
  7. + 0x1c开始4字节为 pssh.datalen
  8. + 0x20 开始为 pssh.data
  9. 可以查看 代码,搜索关键字: FOURCC('p', 's', 's', 'h')

这里先分配 pssh.datalen 大小的内存,然后把 pssh.data 拷贝到刚刚分配的内存。完了之后会把 分配到的 PsshInfo 结构体增加到 类属性值 Vector<PsshInfo> mPssh 中, mPsshMPEG4Extractor::~MPEG4Extractor() 中才会被释放。

所以在解析完 MPEG4格式前,通过 pssh 分配的内存会一直在内存中。

avcC box 和 hvcC box

这两个 box 的处理基本一致,以 avcC 为例进行介绍。解析代码如下

  1. case FOURCC('a', 'v', 'c', 'C'):
  2. {
  3. // 这是一块临时分配, buffer 为智能指针,在 函数返回时相应内存会被释放。
  4. sp<ABuffer> buffer = new ABuffer(chunk_data_size);
  5. if (mDataSource->readAt(
  6. data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
  7. return ERROR_IO;
  8. }
  9. // 在这里,会释放掉原来那个,新分配内存来容纳新的数据。
  10. // 因此我们有了一个 分配,释放 内存能力
  11. // setData 中会释放掉原来的buf, 新分配一个 chunk_data_size
  12. mLastTrack->meta->setData(
  13. kKeyAVCC, kTypeAVCC, buffer->data(), chunk_data_size);
  14. *offset += chunk_size;
  15. break;
  16. }

首先根据 chunk_data_size 分配 ABufferbufferchunk_data_sizeboxsize 域指定,注意buffer是一个智能指针,在这里,它会在函数返回时释放。

ABuffer 中是直接调用的 malloc 分配的内存。

接下来读取数据到 buffer->data(), 最后调用 mLastTrack->meta->setData 保存数据到 meta, 在 setData 内部会先释放掉之前的内存,然后分配的内存,存放该数据,此时分配内存的大小还是chunk_data_size, 我们可控。

hvcC 的处理方式基本一样。所以通过这两个 box 我们可以 分配指定大小的内存,并且可以随时释放前面分配的那个内存块 。我们需要使用这个来布局tx3g内存块 和 MPEG4DataSource 内存块。

修改对象虚表指针

下面结合exploit 和上一节的那几个关键 box ,分析通过布局内存,使得我们可以修改 MPEG4DataSource 的虚表指针。

为了便于说明,取了 exploit 中的用于 修改对象虚表指针的相关代码进行解析 ( 我调试过程做了部分修改 )

首先看到第7,8行,构造了第一个 tx3g box, 大小为 0x3a8, 后面在触发漏洞时,会先把这部分数据拷贝到分配到的小内存buffer中,然后会溢出到下一个 regionMPEG4DataSource 内存块。使用 cyclic 可以在程序 crash 时,计算 bufferMPEG4DataSource 之间的距离。

13 行,调用了 memory_leak 函数, 该函数通过使用 pssh 来分配任意大小的内存,在这里分配的是 alloc_size ,即 0x20. 因为MPEG4DataSource 的大小为 0x20 ,就保证内存的分配会在同一个 run 中分配。这些这样这里分配了 40x20 的内存块,我认为是用来清理之前可能使用内存时,产生的内存碎片,确保后面内存分配按照我们的顺序进行分配。此时内存关系

  1. | pssh | - | pssh |

1725 行,清理内存后,开始分配 avcChvcC, 大小也是 0x20, 然后在第 25 行又进行了内存碎片清理,原因在于我们在分配 avcChvcC时,会使用到 new ABuffer(chunk_data_size),这个临时的缓冲区,这个会在函数返回时被释放(请看智能指针相关知识)

同时多分配了几个 pssh 确保可以把 avcChvcC包围在中间。所以现在的内存关系是

  1. | pssh | - | pssh | pssh | avcC | hvcC | pssh |

然后是 第 29 行, 再次分配 hvcC ,不过这次的大小 为 alloc_size * 2, 触发 hvcC 的释放,而且确保不会占用 刚刚释放的 内存.(jemalloc中 相同大小的内存在同一个run中分配)

  1. | pssh | - | pssh | pssh | avcC | .... | pssh |

接下来构造 stblMPEG4DataSource 占据刚刚空出来的 内存。

  1. | pssh | - | pssh | pssh | avcC | MPEG4DataSource | pssh |

接下来, 第 38 行用同样的手法分配释放 avcC

  1. | pssh | - | pssh | pssh | .... | MPEG4DataSource | pssh |

然后使用整数溢出,计算得到第二个 tx3g 的长度值,使得最后分配到的内存大小为0x20, 用来占据刚刚空闲的 avcC 的 内存块,于是现在的内存布局,就会变成这样。

  1. | pssh | - | pssh | pssh | tx3g | MPEG4DataSource | pssh |

然后在

就会溢出修改了 MPEG4DataSource 的虚表指针。然后在下面的 readAt 函数调用出会 crash.

我测试时得好几次才能成功一次,估计和内存碎片相关。

  1. Thread 10 received signal SIGSEGV, Segmentation fault.
  2. 0xb66b57cc in android::MPEG4Extractor::parseChunk (this=this@entry=0xb74e2138, offset=offset@entry=0xb550ca98, depth=depth@entry=0x2) at frameworks/av/media/libstagefright/MPEG4Extractor.cpp:1905
  3. 1905 if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
  4. ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
  5. $r0 : 0xb74e27b8 0x61616169 ("iaaa"?)
  6. $r1 : 0xb74e2bb8 0x00000000
  7. $r2 : 0x61616169 ("iaaa"?)
  8. $r3 : 0x00000000
  9. $r4 : 0xb550c590 0x00000428
  10. $r5 : 0xfffffbf8
  11. $r6 : 0xb550c580 0xb74e5c98 0x28040000
  12. $r7 : 0xb550c570 0xfffffbf8
  13. $r8 : 0xb74e2138 0xb6749f18 0xb66b2841 <android::MPEG4Extractor::~MPEG4Extractor()+1> ldr r3, [pc, #188] ; (0xb66b2900 <android::MPEG4Extractor::~MPEG4Extractor()+192>)
  14. $r9 : 0x74783367 ("g3xt"?)
  15. $r10 : 0xb550ca98 0x01000a98
  16. $r11 : 0xb74e2790 0x28040000
  17. $r12 : 0x00000000
  18. $sp : 0xb550c530 0xb74e2bb8 0x00000000
  19. $lr : 0xb66b57bd <android::MPEG4Extractor::parseChunk(long+0> ldr r1, [r4, #0]
  20. $pc : 0xb66b57cc <android::MPEG4Extractor::parseChunk(long+0> ldr r6, [r2, #28]
  21. $cpsr : [THUMB fast interrupt overflow carry ZERO negative]
  22. ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
  23. $r0 : 0x00000000
  24. $r1 : 0xb74e2bb8 0x00000000
  25. $r2 : 0x61616169 ("iaaa"?)
  26. $r3 : 0x00000000
  27. $r4 : 0xb550c590 0x00000428
  28. $r5 : 0xfffffbf8
  29. $r6 : 0xb550c580 0xb74e5c98 0x28040000
  30. $r7 : 0xb550c570 0xfffffbf8
  31. $r8 : 0xb74e2138 0xb6749f18 0xb66b2841 <android::MPEG4Extractor::~MPEG4Extractor()+1> ldr r3, [pc, #188] ; (0xb66b2900 <android::MPEG4Extractor::~MPEG4Extractor()+192>)
  32. $r9 : 0x74783367 ("g3xt"?)
  33. $r10 : 0xb550ca98 0x01000a98
  34. $r11 : 0xb74e2790 0x28040000
  35. $r12 : 0x00000000
  36. $sp : 0xb550c530 0xb74e2bb8 0x00000000
  37. $lr : 0xb66b57bd <android::MPEG4Extractor::parseChunk(long+0> ldr r1, [r4, #0]
  38. $pc : 0xb66b57cc <android::MPEG4Extractor::parseChunk(long+0> ldr r6, [r2, #28]
  39. $cpsr : [THUMB fast interrupt overflow carry ZERO negative]

可以看到断在了<android::MPEG4Extractor::parseChunk(long+0> ldr r6, [r2, #28],去 ida 里面找到对应的位置。

r2存放的就是虚表指针,可以确定成功修改了 虚函数表指针。

偏移也符合预期。

堆喷射

上面我们已经成功修改了MPEG4DataSource 的虚表指针,并在虚函数调用时触发了 crash .

我们现在能够修改对象的 虚表指针,并且能够触发虚函数调用。我们需要在一个可预测的内存地址精准的布置我们的数据,然后把虚表指针修改到这里,在 exploit 中使用了

  1. spray_size = 0x100000
  2. spray_count = 0x10
  3. sample_table(heap_spray(spray_size) * spray_count)

来进行堆喷射

heap_spray 函数 就是使用 pssh 来喷射的内存。每次分配 0x100 页,共分配了 0x10 次。 exploit 作者在 博客中写道,这样就可以在可预测的内存地址中定位到特定数据。在这里就是 用于 stack_pivotgadget.

关于堆喷射

在看雪上大佬们进行了讨论

https://bbs.pediy.com/thread-222893-1.htm

最后

这个 exploit 写的确实强悍,提示我在进行漏洞利用时,要关注各种可能分配内存的地方,灵活的使用代码中的内存分配,来布局内存。 同时研究一个漏洞要把相关知识给补齐。对于这个漏洞就是 MPEG4 的文件格式和 相关的处理代码了。

一些tips:

  • 使用 gef + gdb-multiarch 来调试 , pwndbg 我用着非常卡, gef 就不会
  • 调试过程尽量使用脚本减少重复工作量。

使用的一些脚本。

使用 gdbserver attach mediaserver 并转发端口的脚本

  1. adb root
  2. adb forward tcp:1234 tcp:1234
  3. a=`adb shell "ps | grep mediaserver" | awk '{printf $2}'`
  4. echo $a
  5. adb shell "gdbserver --attach :1234 $a"

gdb 的调试脚本

  1. set arch armv5
  2. gef-remote 127.0.0.1:1234
  3. set solib-search-path debug_so/
  4. directory android-5.1.0_r3/
  5. gef config context.layout "regs -source"
  6. set logging file log.txt
  7. set logging on
  8. break frameworks/av/media/libstagefright/MPEG4Extractor.cpp:1897
  9. break frameworks/av/media/libstagefright/MPEG4Extractor.cpp:1630
  10. break frameworks/av/media/libstagefright/MPEG4Extractor.cpp:1647
  11. break frameworks/av/media/libstagefright/MPEG4Extractor.cpp:884
  12. commands 1
  13. p chunk_size
  14. p buffer
  15. c
  16. end
  17. commands 2
  18. p buffer
  19. end
  20. commands 3
  21. p buffer
  22. c
  23. end
  24. commands 4
  25. hexdump dword mDataSource 0x4
  26. c
  27. end

参考:

https://census-labs.com/media/shadow-infiltrate-2017.pdf

https://googleprojectzero.blogspot.hk/

http://blog.csdn.net/zhuweigangzwg/article/details/17222951

CVE-2015-3864漏洞利用分析(exploit_from_google)的更多相关文章

  1. apt28组织新的flash漏洞利用包dealerschoice分析

    17号paloalto发布了文章dealerschoice-sofacys-flash-player-exploit-platform,文中提到apt28正在编写adobe flash player的 ...

  2. Java反序列化漏洞通用利用分析

    原文:http://blog.chaitin.com/2015-11-11_java_unserialize_rce/ 博主也是JAVA的,也研究安全,所以认为这个漏洞非常严重.长亭科技分析的非常细致 ...

  3. Windows漏洞利用与防护(2015.8)

    Windows平台下的漏洞利用与防护 0x00 概述 在过去的二十几年,Windows作为网络安全的主战场之一,攻于防的较量从未停息过.内存破坏漏洞作为研究的重点之一,经历了很多的发展也沉淀了前辈们许 ...

  4. CVE-2013-2551漏洞成因与利用分析(ISCC2014 PWN6)

    CVE-2013-2551漏洞成因与利用分析 1. 简介 VUPEN在Pwn2Own2013上利用此漏洞攻破了Win8+IE10,5月22日VUPEN在其博客上公布了漏洞的细节.它是一个ORG数组整数 ...

  5. Lib之过?Java反序列化漏洞通用利用分析

    转http://blog.chaitin.com/ 1 背景 2 Java反序列化漏洞简介 3 利用Apache Commons Collections实现远程代码执行 4 漏洞利用实例 4.1 利用 ...

  6. CVE-2014-1767 利用分析(2015.2)

    CVE-2014-1767利用分析 参考这篇文章利用思路,重现利用,主要说明自己在实现的时候遇到的坑. 利用思路 1. 第一次 IoControl,释放 MDL,我们通过 VirtualAddress ...

  7. CVE-2015-0057 POC构造 & 利用分析(2015.7)

    CVE-2015-0057 POC构造 & 利用分析 主要内容: 构造POC 利用思路 0x00 初探 从这篇文章可以获知: 1.问题出在 win32k!xxxEnableWndSBArrow ...

  8. CVE-2014-0322漏洞成因与利用分析

    CVE-2014-0322漏洞成因与利用分析 1. 简介 此漏洞是UAF(Use After Free)类漏洞,即引用了已经释放的内存,对指定内存处的值进行了加1.其特点在于攻击者结合flash实现了 ...

  9. CVE-2013-3897漏洞成因与利用分析

    CVE-2013-3897漏洞成因与利用分析 1. 简介 此漏洞是UAF(Use After Free)类漏洞,即引用了已经释放的内存.攻击者可以利用此类漏洞实现远程代码执行.UAF漏洞的根源源于对对 ...

随机推荐

  1. 【bzoj5210】最大连通子块和 动态dp

    动态$dp$好题 考虑用树链剖分将整棵树剖成若干条链. 设x的重儿子为$son[x]$,设$x$所在链链头为$top[x]$ 对于重链上的每个节点(不妨设该节点编号为$x$)令$f[x]$表示以$x$ ...

  2. CentOS7启动Tomcat报错:./startup.sh: Permission denied

    错误信息:./startup.sh: Permission denied 执行./startup.sh,或者./shutdown.sh的时候, 报:Permission denied,因为是执行tom ...

  3. (转)CentOS7下yum安装mysql配置多实例

    原文:http://blog.csdn.net/poklau/article/details/54951798

  4. Win7 Eclipse调试Centos Hadoop2.2-Mapreduce(转)

    一. 自己搭建开发环境 今天自己搭建了一套Centos5.3 + Hadoop2.2 + Hbase0.96.1.1的开发环境,Win7 Eclipse调试MapReduce成功.可能是版本比较高的原 ...

  5. JDK1.10+scala环境的搭建之windows环境

    第一步:安装jdk 1,http://www.oracle.com/technetwork/java/javase/downloads/jdk10-downloads-4416644.html 去找下 ...

  6. 删除none 的images报错 image has dependent child images 解决办法

    这个错是因为在要删除的images之后创建了该images的父images 方法: docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Pa ...

  7. Java 生成指定范围的随机数

    /** * 生成[min, max]之间的随机整数 * * @param min 最小整数 * @param max 最大整数 * @return * @author jqlin */ private ...

  8. springweb flux 编程模型

    Spring WebFlux 编程模型是在spring5.0开始,springbot2.0版本设计出来的新的一种反应式变成模型.它脱胎于reactor模式,是java nio 异步编程模型. 传统一般 ...

  9. 前端性能优化---缓存篇SDK

    1.把前端最常用的资源css.js存在本地1.1  前端缓存技术SessionStorage 优点:临时存储神器,关闭页面标签自动回收,不可以跨页面交互. 取值的时候有两种方法,一种是用session ...

  10. DOM-访问元素

    id:元素在文档中唯一标识符. title:有关元素的附加说明信息,一般通过工具提示条显示出来. lang:元素内容的语言编码,很少使用 dir:语言方向,职位“Ltr”(从左至右).Rtl(从右至左 ...