欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]cve-2015-6620学习总结

[原创]cve-2015-6620学习总结

cve-2015-6620学习总结

前言

想学习下android漏洞方面的知识,搜了下发现Flanker Edward在知乎上有个回答,提到了binder的经典漏洞cve-2015-6620,所以就从这个漏洞开始学习。作者提供了poc以及文档,这篇笔记主要记录下学习中遇到的问题,以及自己的一些理解。

环境搭建与基础知识

第一次调试android漏洞,搭建环境花了些力气,主要有如下环境,推荐安装pead-arm和shadow这两个gdb插件。

  1. android源码环境:Ubuntu16.04 android_6.0.0_r1
  2. gdb调试环境搭建
  3. peda-arm 安装,调试界面更加方便
  4. shadow 安装,方便调试jemalloc

这是android平台上的binder方面的漏洞,所以涉及一些android底层的知识需要学习下。

  1. binder
  2. 智能指针

漏洞成因

cve-2015-6620包含两个漏洞,编号分别为24123723和24445127。主要分析的是24445127 MediaCodecInfo越界访问,因为这个漏洞可以利用的点更多些。漏洞存在于MediaCodcList服务。该Binder服务提供了一个getCodecInfo的功能,存在漏洞的代码如下:

//http://androidxref.com/6.0.0_r1/xref/frameworks/av/media/libmedia/IMediaCodecList.cpp#54

status_t BnMediaCodecList::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

    switch (code) {

        case GET_CODEC_INFO:

        {

            CHECK_INTERFACE(IMediaCodecList, data, reply);

            size_t index = static_cast<size_t>(data.readInt32());

            const sp<MediaCodecInfo> info = getCodecInfo(index); //调用服务端的实现

            if (info != NULL) {

                reply->writeInt32(OK);

                info->writeToParcel(reply);

            } else {

                reply->writeInt32(-ERANGE);

            }

            return NO_ERROR;

        }

        break;

从Parce中读取从客户端传来的索引index,然后调用在服务端的实现的getCodecInfo。看下在MediaCodecList中实现的getCodecInfo

// http://androidxref.com/6.0.0_r1/xref/frameworks/av/include/media/stagefright/MediaCodecList.h#49

struct MediaCodecList : public BnMediaCodecList { 

    Vector<sp<MediaCodecInfo> > mCodecInfos;

     virtual sp<MediaCodecInfo> getCodecInfo(size_t index) const {

        return mCodecInfos.itemAt(index);   // 未进行任何边界检查

    }

}

可以看到直接调用了vector的itemAt函数,并未进行任何边界检查。而index是我们作为客户端程序可以控制的,这个地方就存在一个越界访问的漏洞。

漏洞利用

根据漏洞的成因,我们现在有这样一个能力:可以越界访问Binder服务所在进程中的一个vector<sp<MediaCodecInfo>>,但是只能读取不能写入。漏洞的作者利用这样一种能力可以实现任意地址读取和pc寄存器的控制,很是神奇。主要分析下pc控制的原理。在分析poc原理前,需要了解相关对象在内存的布局,如下图所示:

pc control poc原理分析

一个越界读可以造成pc的控制,关键在于getCodecInfo的调用: const sp\<MediaCodecInfo\> info = getCodecInfo(index);

//sp 拷贝构造函数

template<typename T>

sp<T>::sp(const sp<T>& other)

: m_ptr(other.m_ptr)

  {

    if (m_ptr) m_ptr->incStrong(this);

  }

上面的代码是用getCodecInfo函数的返回新建了一个info对象,这就会调用info的拷贝构造函数。info的类型为sp,sp的拷贝构造函数如上所示。可以看看getCodecInfo的汇编版本,像这样返回对象的函数,一般会把R0指向返回对象保存的地址。

//libstagefright.so

.text:000A9478 ; android::sp<android::MediaCodecInfo> __usercall android::MediaCodecList::getCodecInfo@<R0>(const android::MediaCodecList *this@<R1>, size_t index@<R2>)

.text:000A9478 return_obj = R0                         ;保存的就是上面info的地址

.text:000A9478 this = R1                               ; const android::MediaCodecList *

.text:000A9478 index = R2                              ; size_t

.text:000A9478                 PUSH.W          {R11,LR}

.text:000A947C                 MOV             R3, R0

.text:000A947E                 LDR             R0, [this,#0x5C]

.text:000A9480                 LDR.W           R0, [R0,index,LSL#2] ; 这里可以越界读取

.text:000A9484                 STR             R0, [R3]; 设置info.m_prt

.text:000A9486                 CMP             R0, #0

.text:000A9488                 ITT NE

.text:000A948A                 MOVNE           this, R3 ; 调用info的拷贝构造函数,因为inline优化直接调用了(info.m_ptr)->incStrong()

.text:000A948C                 BLXNE           _ZNK7android7RefBase9incStrongEPKv ; android::RefBase::incStrong(void const*)

.text:000A9490                 POP.W           {R11,PC}

.text:000A9490 ; End of function android::MediaCodecList::getCodecInfo(uint)

可以看到会将vector的内容读取到R0中,如果R0不为零,会调用incStrong, 代码如下:

//http://androidxref.com/6.0.0_r1/xref/system/core/libutils/RefBase.cpp#322

void RefBase::incStrong(const void* id) const

{

    weakref_impl* const refs = mRefs;

    refs->incWeak(id);

    refs->addStrongRef(id);

    const int32_t c = android_atomic_inc(&refs->mStrong);

    ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

#if PRINT_REFS

    ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);

#endif

    if (c != INITIAL_STRONG_VALUE)  {

        return;

    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);

    refs->mBase->onFirstRef(); //这里有虚函数的调用

}

汇编代码版本,可以清楚看到存在虚函数的调用:

// libutils.so

.text:0000E6BE ; void __fastcall android::RefBase::incStrong(const android::RefBase *const this, const void *id)

.text:0000E6BE                 EXPORT _ZNK7android7RefBase9incStrongEPKv     

.text:0000E6BE                                         

.text:0000E6BE this = R0                                         ; const android::RefBase *const

.text:0000E6BE id = R1                                           ; const void *

.text:0000E6BE                 PUSH            {R4,LR}

.text:0000E6C0                 LDR             R4, [this,#4]     ;this存放的就是越界读取的内容

.text:0000E6C2 refs = R4                               ; android::RefBase::weakref_impl *const

.text:0000E6C2                 MOV             this, refs ; this

.text:0000E6C4                 BLX             j__ZN7android7RefBase12weakref_type7incWeakEPKv ; 

.text:0000E6C8                 DMB.W           SY

.text:0000E6CC                 LDREX.W         R3, [refs]

.text:0000E6D0                 ADDS            R2, R3, #1

.text:0000E6D2                 STREX.W         R1, R2, [refs]

.text:0000E6D6                 CMP             R1, #0

.text:0000E6D8                 BNE             loc_E6CC

.text:0000E6DA                 CMP.W           R3, #0x10000000

.text:0000E6DE                 BNE             locret_E700

.text:0000E6E0                 DMB.W           SY

.text:0000E6E4                 LDREX.W         R0, [refs]

.text:0000E6E8                 ADD.W           R12, R0, #0xF0000000

.text:0000E6EC                 STREX.W         R3, R12, [refs]

.text:0000E6F0                 CMP             R3, #0

.text:0000E6F2                 BNE             loc_E6E4

.text:0000E6F4                 LDR             R0, [refs,#8]

.text:0000E6F6                 LDR             refs, [R0]  ; vtable

.text:0000E6F8                 LDR             R2, [R4,#8] ; 可以通过这里控制pc

.text:0000E6FA                 POP.W           {R4,LR}

.text:0000E6FE                 BX              R2

梳理一下就是,越界读取的内容放入R0,然后进行如下操作:

refs = [R0 + 4]

if ([refs] == 0x10000000)

    mbase = [refs + 8]

    vtable = [mbase]

    call [vtable + 8]

也就是说如果我们在内存中伪造了合适的MeidaCodecInfo,并且将指向该伪造的MediaCodecInfo的指针放入vector<sp<MediaCodecInfo>>存储区的后面,这样我们就可以通过越界访问,读取到指向该伪造的MediaCodecInfo的指针,进而控制pc。我们可以在内存中伪造如下的MediaCodecInfo:

//BASEADDR 为假MediaCodecInfo的起始地址

*(BASEADDR) = vtale; //设置MediaCodecInfo vtable 随便填写

*((unsigned int *)BASEADDR + 1) = BASEADDR + 12;     //mRefs, 使他指向BASEADDR + 12

*((unsigned int *)BASEADDR + 3) = 0x10000000;        //mRefs指向此处,即虚假的info->mRefs的起始地址

*((unsigned int *)BASEADDR + 5) = BASEADDR + 0x20;   //info->mRefs->mBase字段,使他指向BASEADDR + 0x20

*((unsigned int*)BASEADDR + 8) = BASEADDR + 0x20 + 4;  //mBase的vtable字段,使他指向BASEADDR + 0x20 + 4

*((unsigned int*)BASEADDR + 11) = 0x61616161;          //vtable + 8, 我们可以在此处放置目标pc

最终poc运行成功,mediaserver运行到我们指定的位置:0x61616161

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功