Android提權漏洞CVE-2014-7920&CVE-2014-7921分析

沒羽@阿里移動安全,更多安全類技術乾貨,請訪問阿里聚安全博客html


 

 

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

2016年1月24日漏洞做者發佈了漏洞分析及exploit[2],拿到exploit後在幾個Android版本上均沒能運行成功,遂分析緣由,學習漏洞利用思路。記錄以下,歡迎你們交流學習。git

不熟悉Android Binder的同窗,請自行網上搜索學習資料,下面直接分析漏洞。github

0x1漏洞成因

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

一個是AudioFlinger[4],service name爲「media.audio_flinger」;另外一個是AudioPolicyService[5],service name爲「media.audio_policy」。數組

1.1內存寫漏洞

內存寫漏洞產生在「media.audio_policy」中,接口類爲IAudioPolicyService[6][7],其中startOutput()接口存在遞增的內存寫漏洞,stopOutput()接口存在遞減的內存寫漏洞。安全

       startOutput()接口定義爲:session

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()收到請求,而後執行下面的代碼:

繼續調用AudioPolicyService ::startOutput()[9]方法:

mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);

mpAudioPolicy爲audio_policy類型,audio_policy:: start_output()在audio_policy_hal.cpp中被定義爲ap_start_output(),該方法調用:

lap->apm->startOutput()由AudioPolicyManagerBase:: startOutput()方法實現,該方法調用:

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

當2個if語句都爲假時,mRefCount[stream] += delta;語句將被執行。

此時若是索引stream可被控制,那麼mRefCount內存的相對偏移內存將可被修改成加delta。恰巧stream爲接口參數之一,也沒校驗,在AudioPolicyManagerBase:: startOutput()中傳入的delta爲1 ,也就是說這裏存在一個遞增1的內存寫漏洞。這個內存寫漏洞的產生須要知足如下條件:

  • isDuplicated()爲False:幸運的是默認狀況大部分output不是duplicated的。
  • (delta + (int)mRefCount[stream]) < 0:因爲這個判斷條件,mRefCount[stream]<0x7FFFFFFF時纔會爲False,這就限制了這個內存寫漏洞,只能對內存內容小於0x7FFFFFFF的內存值進行遞增。

2)stopOutput的遞減寫漏洞

stopOutput()接口相似於startOutput()接口,咱們直接看AudioPolicyManagerBase::stopOutput()方法,該方法調用的是:

outputDesc->changeRefCount(stream, -1);

與startOutput()接口相似,也存在相同的寫限制,不一樣的是這遞減1的內存寫漏洞。

1.2內存讀漏洞

內存寫漏洞也產生在「media.audio_policy」中,問題出在isStreamActive()接口。該接口定義爲:

bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs)

相似於startOutput()接口,該接口調用了AudioPolicyManagerBase::isStreamActive()方法,該方法調用:

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

若是根據isStreamActive() 返回值判斷mRefCount[stream]是否爲0,須要知足2個條件:

  • mRefCount[stream] != 0;
  • ns2ms(sysTime - mStopTime[stream]) > inPastMs:

sysTime - mStopTime[stream]爲時間差值,爲正值,當inPastMs>=0x80000000時,該不等式成立。

因此能夠經過控制stream和inPastMs的值判斷mRefCount內存相對偏移的值是否爲0。

 

0x2利用以前

2.1利用技巧小結

1)利用內存讀,模糊匹配audio_hw_device對象

audio_hw_device這個結構包含了audio硬件設備函數指針,在「media.audio_policy」和「media.audio_flinger」 service提供的接口中會被調用,這有利於寫exploit。audio_hw_device結構定義以下:

[圖片來自原文]

分析發現audio_hw_device對象中reserved和function_ptrs-> get_supported_devices爲0,其它字段爲非0。該結構用0和非0的形式可表示爲:

前面提到能夠經過isStreamActive()接口判斷內存值是否爲非0,這樣結合audio_hw_device結構的特徵在內存中搜索,恰巧在mRefCount的上下內存區域中能夠搜索到audio_hw_device對象。

2)利用內存寫,泄漏內存地址

 「media.audio_flinger」提供了getInputBufferSize()接口[11],接口定義爲:

其服務端代碼爲:

當客戶端調用getInputBufferSize()接口,服務端最終調用get_input_buffer_size()即audio_hw.cpp::adev_get_input_buffer_size()函數,最後返回size。由arm指令特性可知,get_input_buffer_size(dev, &config)函數的反彙編中,經過R0傳入dev指針,即audio_hw_device對象,函數執行完後,返回值經過R0傳回。若是修改get_input_buffer_size函數指針,讓其指向「BX LR」,那個就可拿到audio_hw_device對象的內存地址。

恰巧get_input_buffer_size ()函數指針也存儲於audio_hw_device對象中,使用內存寫漏洞讓audio_hw_device. get_input_buffer_size指向一個「BX LR」的地址便可獲取audio_hw_device對象地址。

 

2.2踩到的坑

筆者在調試exploit時發生屢次crash,將update後的exploit放在https://github.com/Vinc3nt4H/cve-2014-7920-7921_update,編譯環境:Android 4.3_r2.1,運行環境:AVD 4.3(4.3_r2.1)。

1)搜索audio_hw_device對象時mediaserver crash

在運行exploit時,能夠搜索到audio_hw_device對象,但mediaserver crash了,多是因爲搜索的內存範圍過大,致使非法內存訪問。可縮小搜索範圍試試,好比設置MAX_OFFSET爲-3000。

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

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

3)android 4.4.2上crash

開始時筆者在AVD 4.4.2中執行exploit老是不成功,調試發現audio_hw_device. get_input_buffer_size的值被置了0,以下圖:

由於mediaserver中加載的audio.primary.goldfish.so基址大於0x7FFFFFFF,也就是mRefCount[offset_get_input_buffer_size] > 0x7FFFFFFF,即爲負數,在利用遞增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結構的特徵在mediaserver進程中搜索匹配的對象。利用isStreamActive()的內存讀漏洞讀取mRefCount附件內存區域生產0/非0的內存映射,而後與audio_hw_device結構特徵匹配,計算出audio_hw_device對象相對於mRefCount的相對偏移。

3.2 Bypass ASLR

2.1-2中有提到利用內存寫漏洞獲取內存地址,接下來咱們來分析exploit是如何利用內存寫繞過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,代碼以下:

其中library_offset爲所使用的lib基址與audio.primary.goldfish.so庫基址之間的偏移,gadget_offset爲相對於該lib庫基址的偏移;RELATIVE_ADDRESS_OF_GET_INPUT_BUFFER_SIZE爲adev_get_input_buffer_size函數地址在audio.primary.goldfish.so中的偏移;modify_value()函數是內存遞增1 /遞減1操做的封裝。gadget1爲:

而後調用AudioFlinger::getInputBufferSize()跳到gadget1。

uint32_t read_function_pointer_address = af->getInputBufferSize(0, (audio_format_t)0, (audio_channel_mask_t)0);

gadget1執行時R0爲dev即audio_hw_device對象,參考audio_hw_device結構,R0+0x64爲open_output_stream即adev_open_output_stream的值,經過R0返回。

再減去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:

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

3)設置write gadget

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

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

看gadget3,寫數據調用接口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執行以下:

 

至此咱們將audio_hw_device. get_input_buffer_size指向gadget3,再調用getInputBufferSize(address, 0, value)就能夠向address-0xC內存寫入value。

3.3佈局Gadget Buffer

 

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

1)寫入system()函數參數

看利用代碼:

write32()函數寫數據分爲2步:

a)設置數據偏移

調用modify_value(aps, g_primary_device_offset + 1, offset - g_current_write_offset);修改dev.version的內容,該字段做爲後面數據寫入時的數組偏移;dev.version首次從0x200遞減直到0xC:

b)寫入數據

調用af->getInputBufferSize(g_primary_device_address + sizeof(uint32_t) + 12, (audio_format_t)0, (audio_channel_mask_t)value)觸發gadget3執行,調試時斷在gadget3上:

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

查看[R1]內存,R1[0]爲config. sampleRate即address,爲要寫入的地址,address爲&dev[0]+4+12, R1[1]爲config. channelMask即value ,值爲「/dat」:

gadget3的僞代碼大體以下:

 

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

 

2)寫入system()函數地址

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

3)寫入call gadget

調用gadget3修改audio_hw_device. get_input_buffer_size指針的值,使用指向,libstagefright.so: 0x5EF88+1,記爲gadget4。

gadget4:

3.4觸發代碼執行

利用代碼爲:

af->getInputBufferSize(0, (audio_format_t)0, (audio_channel_mask_t)0);

當調用getInputBufferSize()時觸發gadget4執行:

寄存器值:

調用system()函數,加載外命令/data/local/tmp/a。筆者寫了個遠程shell命名爲a,下圖是運行成功後獲取的shell,爲「media」權限:

0x4參考連接

[1]http://bits-please.blogspot.com/2016/01/android-privilege-escalation-to.html

[2]https://github.com/laginimaineb/cve-2014-7920-7921

[3]http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/mediaserver/main_mediaserver.cpp#40

[4]http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioFlinger.cpp

[5]http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioPolicyService.cpp

[6]http://androidxref.com/4.3_r2.1/xref/frameworks/av/include/media/IAudioPolicyService.h

[7]http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioPolicyService.cpp

[8]http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioPolicyService.cpp#384

[9]http://androidxref.com/4.3_r2.1/xref/frameworks/av/services/audioflinger/AudioPolicyService.cpp#234

[10]http://androidxref.com/4.3_r2.1/xref/hardware/libhardware_legacy/audio/AudioPolicyManagerBase.cpp#3083

[11]http://androidxref.com/4.3_r2.1/xref/frameworks/av/media/libmedia/IAudioFlinger.cpp#346

[12]https://github.com/Vinc3nt4H/cve-2014-7920-7921_update


沒羽@阿里移動安全,更多安全類技術乾貨,請訪問阿里聚安全博客

相關文章
相關標籤/搜索