-
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);
原文地址:https://www.cnblogs.com/elvirangel/p/10448590.html