Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android提權漏洞CVE-2014-7920&CVE-2014-7921分析

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

編輯:關於Android編程

這是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

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved