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

來源:互聯網
上載者:User

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

這是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]披露過程如下:

2016年1月24日漏洞作者發布了漏洞分析及exploit[2],拿到exploit後在幾個Android版本上均沒能運行成功,遂分析原因,學習漏洞利用思路。記錄如下,歡迎大家交流學習。

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

0x1漏洞成因

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

一個是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()介面定義為:

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的記憶體寫漏洞。這個記憶體寫漏洞的產生需要滿足以下條件:

lisDuplicated()為False:幸運的是預設情況大部分output不是duplicated的。

l(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個條件:

lmRefCount[stream] != 0;

lns2ms(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_tchannelMask)),走到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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.