zoukankan      html  css  js  c++  java
  • AOSP常见漏洞类型简介

    • 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
    ", 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);
     
     
  • 相关阅读:
    TCP链接异常断开后,对端仍然ESTABLISH
    mds0: Many clients (191) failing to respond to cache pressure
    Linux C/C++编译过程中的各种not declared in this scope
    Centos7添加磁盘并分区格式化
    Windows Terminal——安装并配置主题
    Kbone——创建项目报错 name can no longer contain capital letters
    Redis——大批量删除redis的key
    Redis——设置最大内存 | key淘汰机制
    Nightingale——滴滴夜莺部署【一】
    ELK——使用Docker快速搭建
  • 原文地址:https://www.cnblogs.com/elvirangel/p/10448590.html
Copyright © 2011-2022 走看看