首页
社区
课程
招聘
[原创]AOSP常见漏洞简介
2019-2-26 16:50 5014

[原创]AOSP常见漏洞简介

2019-2-26 16:50
5014

Heap/Stack Overflow(CVE-2017-0541)


漏洞出现在PushcdlStack函数中,如下所示


# /external/sonivox/arm-wt-22k/lib_src/eas_mdls.c

static EAS_RESULT PushcdlStack (EAS_U32 *pStack, EAS_INT *pStackPtr, EAS_U32 value)

{

    /* stack overflow, return an error */

    if (*pStackPtr >= CDL_STACK_SIZE)

        return EAS_ERROR_FILE_FORMAT;

    /* push the value onto the stack */

    *pStackPtr = *pStackPtr + 1;

    pStack[*pStackPtr] = value;

    return EAS_SUCCESS;

}


程序中将*pStackPtr加一后,执行pStack[*pStackPtr] = value。这里没有校验*pStackPtr加一后的值是否超过pStack大小,造成栈溢出。


回到调用PushcdlStack函数的上一层函数Parse_cdl,如下所示


static EAS_RESULT Parse_cdl (SDLS_SYNTHESIZER_DATA *pDLSData, EAS_I32 size, EAS_U32 *pValue)

{

    EAS_RESULT result;

    EAS_U32 stack[CDL_STACK_SIZE];

    EAS_U16 opcode;

    EAS_INT stackPtr;

    EAS_U32 x, y;

    DLSID dlsid;



    stackPtr = -1;

    *pValue = 0;

    x = 0;

    while (size)

    {

        /* read the opcode */

        if ((result = EAS_HWGetWord(pDLSData->hwInstData, pDLSData->fileHandle, &opcode, EAS_FALSE)) != EAS_SUCCESS)

            return result;



        /* handle binary opcodes */

        if (opcode <= DLS_CDL_EQ)

        {

            /* 省略部分代码 */

        }



        else if (opcode == DLS_CDL_NOT)

        {

            /* 省略部分代码 */

        }



        else if (opcode == DLS_CDL_CONST)

        {

            if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &x, EAS_FALSE)) != EAS_SUCCESS)

                return result;

        }



        else if (opcode == DLS_CDL_QUERY)

        {

            /* 省略部分代码 */

        }



        else if (opcode == DLS_CDL_QUERYSUPPORTED)

        {

            /* 省略部分代码 */

        }

        else

            { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Unsupported opcode %d in DLS file\n", opcode); */ }



        /* push the result on the stack */

        if ((result = PushcdlStack(stack, &stackPtr, x)) != EAS_SUCCESS) //漏洞点

            return result;

    }


    /* pop the last result off the stack */

    return PopcdlStack(stack, &stackPtr, pValue);

}


部分无关代码这里没有显示,只看漏洞相关代码,opcode是从文件读取中的,所以可控;当opcode == DLS_CDL_CONST时,x的值也来从文件中读取,所以可控;

循环的最后会执行漏洞函数PushcdlStack(stack, &stackPtr, x),stack大小为CDL_STACK_SIZE(CDL_STACK_SIZE的值为8),stackPtr初始值为-1,

因此我们只要循坏执行PushcdlStack(stack, &stackPtr, x)函数8次之后,stackPtr等于7,再执行一次PushcdlStack(stack, &stackPtr, x),

即可将可控x写入stack[8],造成栈溢出。


漏洞是在解析.xmf文件时出现的


修复方案就是将PushcdlStack函数if (*pStackPtr >= CDL_STACK_SIZE)改为if (*pStackPtr >= CDL_STACK_SIZE - 1)


Integer Overflow(CVE-2017-0597)


以下是该漏洞的git diff


@@ -110,9 +110,24 @@

     mUid = clientUid;

     // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);

+

+    size_t bufferSize = buffer == NULL ? roundup(frameCount) : frameCount;

+    // check overflow when computing bufferSize due to multiplication by mFrameSize.

+    if (bufferSize < frameCount  // roundup rounds down for values above UINT_MAX / 2

+            || mFrameSize == 0   // format needs to be correct

+            || bufferSize > SIZE_MAX / mFrameSize) {

+        android_errorWriteLog(0x534e4554, "34749571");

+        return;

+    }

+    bufferSize *= mFrameSize;

+

     size_t size = sizeof(audio_track_cblk_t);

-    size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize;

     if (buffer == NULL && alloc == ALLOC_CBLK) {

+        // check overflow when computing allocation size for streaming tracks.

+        if (size > SIZE_MAX - bufferSize) {

+            android_errorWriteLog(0x534e4554, "34749571");

+            return;

+        }

         size += bufferSize;

     }


通过比较,可以看出漏洞是由于对frameCount的值没有进行校验,导致的整形溢出


frameCount本身是无符号整型


在size_t bufferSize = (buffer == NULL ? roundup(frameCount) : frameCount) * mFrameSize;中frameCount*frameCount肯回造成溢出

导致最终bufferSize的值小于frameCount) * mFrameSize


之后的代码会以bufferSize的值申请一块内存,之后对这块内存进行访问时,会造成缓冲区溢出


Type Confusion(CVE-2017-0546)


直接查看漏洞点


void SurfaceFlinger::setTransactionState(

        const Vector<ComposerState>& state, ——>State是我们可以控制的

        const Vector<DisplayState>& displays,

        uint32_t flags)

{

    /* 省略部分代码 */

    count = state.size();

    for (size_t i=0 ; i<count ; i++) {

        const ComposerState& s(state[i]); ——>循环处理state[i]

        // Here we need to check that the interface we're given is indeed

        // one of our own. A malicious client could give us a NULL

        // IInterface, or one of its own or even one of our own but a

        // different type. All these situations would cause us to crash.

        //

        // NOTE: it would be better to use RTTI as we could directly check

        // that we have a Client*. however, RTTI is disabled in Android.

        if (s.client != NULL) {

            sp<IBinder> binder = IInterface::asBinder(s.client);——> s.client是一个IBinder指针

            if (binder != NULL) {

                String16 desc(binder->getInterfaceDescriptor());

                if (desc == ISurfaceComposerClient::descriptor) {—->比较binder->getInterfaceDescriptor()和ISurfaceComposerClient::descriptor的值

                    sp<Client> client( static_cast<Client *>(s.client.get()) );——>类型转换

                    transactionFlags |= setClientStateLocked(client, s.state);

                }

            }

        }

    }

/* 省略部分代码 */

}


State是我们可以控制的,s.client是一个IBinder指针,之后通过比较binder->getInterfaceDescriptor()字符串和ISurfaceComposerClient::descriptor

的值,之后进行类型转换,将s.client转换为Client,之后调用setClientStateLocked(client, s.state)


在setClientStateLocked函数中会执行Client对象的虚函数


uint32_t SurfaceFlinger::setClientStateLocked(

        const sp<Client>& client,

        const layer_state_t& s)

{

    uint32_t flags = 0;

    sp<Layer> layer(client->getLayerUser(s.surface));

/* 省略部分代码 */

}


可以看到,类型转换前,校验知识简单的比较了两个字符串的值,我们可以伪造一个符合条件的对象,进而在调用虚函数时,执行伪造对象的虚函数


修复方案如下


    count = state.size();

    for (size_t i=0 ; i<count ; i++) {

        const ComposerState& s(state[i]);

        // Here we need to check that the interface we're given is indeed

        // one of our own. A malicious client could give us a NULL

        // IInterface, or one of its own or even one of our own but a

        // different type. All these situations would cause us to crash.

        //

        // NOTE: it would be better to use RTTI as we could directly check

        // that we have a Client*. however, RTTI is disabled in Android.

        if (s.client != NULL) {

            sp<IBinder> binder = IInterface::asBinder(s.client);

            if (binder != NULL) {

                if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) != NULL) {

                    sp<Client> client( static_cast<Client *>(s.client.get()) );

                    transactionFlags |= setClientStateLocked(client, s.state);

                }

            }

        }

    }



NPD(Null Pointer Dereference)(CVE-2016-6765)


漏洞代码如下:


status_t MPEG4Extractor::parseChunk(off64_t *offset, int depth) {

/* 省略部分代码 */

        case FOURCC('b', 't', 'r', 't'):

        {

            *offset += chunk_size;



            uint8_t buffer[12];

            if (chunk_data_size != sizeof(buffer)) {

                return ERROR_MALFORMED;

            }



            if (mDataSource->readAt(

                    data_offset, buffer, chunk_data_size) < chunk_data_size) {

                return ERROR_IO;

            }



            uint32_t maxBitrate = U32_AT(&buffer[4]);

            uint32_t avgBitrate = U32_AT(&buffer[8]);

            if (maxBitrate > 0 && maxBitrate < INT32_MAX) {

                mLastTrack->meta->setInt32(kKeyMaxBitRate, (int32_t)maxBitrate); ——-> 空指针引用

            }

            if (avgBitrate > 0 && avgBitrate < INT32_MAX) {

                mLastTrack->meta->setInt32(kKeyBitRate, (int32_t)avgBitrate); ——-> 空指针引用

            }

            break;

        }

/* 省略部分代码 */

}


在调用mLastTrack指针的函数前没有验证mLastTrack指针是否为空,造成空指针引用漏洞


在MPEG4Extractor对象的构造函数中,将mLastTrack的值置为空


MPEG4Extractor::MPEG4Extractor(const sp<DataSource> &source)

    : mMoofOffset(0),

      mMoofFound(false),

      mMdatFound(false),

      mDataSource(source),

      mInitCheck(NO_INIT),

      mHasVideo(false),

      mHeaderTimescale(0),

      mFirstTrack(NULL),

      mLastTrack(NULL), ———> mLastTrack的值置为空

      mFileMetaData(new MetaData),

      mFirstSINF(NULL),

      mIsDrm(false) {

}


修复方案就是增加空指针校验,通过查看源码可以看到处理其他case时是有校验的,可是偏偏这一个case没有校验,有点意思


TOCTOU(Time Of Check Time Of Use)(CVE-2017-0419)


漏洞点


1207status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,

1208                                             uint32_t cmdSize,

1209                                             void *pCmdData,

1210                                             uint32_t *replySize,

1211                                             void *pReplyData)

1212{



/* 省略部分代码 */



1232        Mutex::Autolock _l(mCblk->lock);

1233        if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE ||

1234            mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) {

1235            mCblk->serverIndex = 0;

1236            mCblk->clientIndex = 0;

1237            return BAD_VALUE;

1238        }

1239        status_t status = NO_ERROR;

1240        while (mCblk->serverIndex < mCblk->clientIndex) {

1241            int reply;

1242            uint32_t rsize = sizeof(int);

1243            int *p = (int *)(mBuffer + mCblk->serverIndex);   —————>越界访问

1244            int size = *p++;



/* 省略部分代码 */

}


这里主要是因为mCblk对象的特殊性,mCblk位于共享内存,在一个进程使用期间被另一个进程使用,通过进程间通信控制mCblk这个对象

所以这里即使判断了mCblk->clientIndex和mCblk->serverIndex值的有效性,在之后有可能mCblk->serverIndex和mCblk->serverIndex值会被其他进程改变

从而在之后越界访问缓冲区


修复方案就是保存事先mCblk->clientIndex和mCblk->serverIndex值,如下:


1380        Mutex::Autolock _l(mCblk->lock);

1381        // keep local copy of index in case of client corruption b/32220769

1382        const uint32_t clientIndex = mCblk->clientIndex;  ——> 保存mCblk->clientIndex

1383        const uint32_t serverIndex = mCblk->serverIndex;  —-> 保存mCblk->serverIndex

1384        if (clientIndex > EFFECT_PARAM_BUFFER_SIZE ||

1385            serverIndex > EFFECT_PARAM_BUFFER_SIZE) {

1386            mCblk->serverIndex = 0;

1387            mCblk->clientIndex = 0;

1388            return BAD_VALUE;

1389        }

1390        status_t status = NO_ERROR;

1391        effect_param_t *param = NULL;

1392        for (uint32_t index = serverIndex; index < clientIndex;) {

1393            int *p = (int *)(mBuffer + index);

1394            const int size = *p++;


Missing Permission Check(CVE-2017-0490)


漏洞代码如下


50    public static WifiConfiguration buildConfig(String uriString, byte[] data, Context context)

51            throws IOException, GeneralSecurityException, SAXException {

52        Log.d(TAG, "Content: " + (data != null ? data.length : -1));

53

54        byte[] b64 = Base64.decode(new String(data, StandardCharsets.ISO_8859_1), Base64.DEFAULT);

55        Log.d(TAG, "Decoded: " + b64.length + " bytes.");

56

57        dropFile(Uri.parse(uriString), context);


传入的URI在解析后直接被调用了删除dropfile。缺少了权限检查


修复方案:


官网修复方案是直接把dropFile(Uri.parse(uriString), context);这一句删掉了


OOB(Out Of Boundary)(CVE-2015-6620)


漏洞点:


status_t BnMediaCodecList::onTransact(

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

{

/* 省略部分代码 */

        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;

/* 省略部分代码 */

}


漏洞代码中通过getCodecInfo(index)得到对应的info对象指针,而index是从可控的data中得到的


再看getCodecInfo函数


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

        return mCodecInfos.itemAt(index);

    }


该函数没有对index的值做校验,意味着可以对向量mCodecInfos后的值进行访问,造成越界访问


修复方案:


   

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

        if (index >= mCodecInfos.size()) {

            ALOGE("b/24445127");

            return NULL;

        }

        return mCodecInfos.itemAt(index);

    }


对index进行了校验


UAF(Use After Free)(CVE-2017-0444)


漏洞点:


status_t SoftAVC::initDecoder() {

/* 省略部分代码 */

        status = ivdec_api_function(mCodecCtx, (void *)&s_create_ip, (void *)&s_create_op);



        mCodecCtx = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle;

        mCodecCtx->pv_fxns = dec_fxns;

        mCodecCtx->u4_size = sizeof(iv_obj_t);



        if (status != IV_SUCCESS) {

            ALOGE("Error in create: 0x%x",

                    s_create_op.s_ivd_create_op_t.u4_error_code);

            deInitDecoder();

            mCodecCtx = NULL;

            return UNKNOWN_ERROR;

        }

/* 省略部分代码 */

}


漏洞很明显,程序居然是先使用再校验是否分配成功,有点意思

在ivdec_api_function函数中分配了一块堆,保存在mCodecCtx上,如果分配错误就会释放掉分配的内存

可是漏洞程序因为是先使用,所以造成了UAF


更有意思的是在ivdec_api_function函数中释放mCodecCtx的位置

本因该在释放堆块之后将mCodecCtx置为null,可没看到对应代码,这也是漏洞产生原因之一


修复方案就是将校验和使用调换个位置:


    

     status = ivdec_api_function(mCodecCtx, (void *)&s_create_ip, (void *)&s_create_op);



        if (status != IV_SUCCESS) {

           ALOGE("Error in create: 0x%x",

                   s_create_op.s_ivd_create_op_t.u4_error_code);

            deInitDecoder();

            mCodecCtx = NULL;

            return UNKNOWN_ERROR;

        }



        mCodecCtx = (iv_obj_t*)s_create_op.s_ivd_create_op_t.pv_handle;

        mCodecCtx->pv_fxns = dec_fxns;

        mCodecCtx->u4_size = sizeof(iv_obj_t);



[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2019-2-26 16:52 被五千年木编辑 ,原因: 略
收藏
点赞4
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  Editor   +1.00 2019/02/27 感谢分享~
最新回复 (5)
雪    币: 19584
活跃值: (60093)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2019-2-26 17:17
2
0
感谢分享!
雪    币: 1
活跃值: (693)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
saliey 2019-2-26 19:01
3
0
雪    币: 2375
活跃值: (433)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
petersonhz 2019-2-26 21:17
4
0
这些漏洞是楼主发现的么
雪    币: 351
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
五天 2019-2-27 08:56
5
0
雪    币: 169
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hongde 2019-2-27 15:42
6
0
游客
登录 | 注册 方可回帖
返回