iPhone藍色畫面0day漏洞分析:播放影片觸發核心拒絕服務

wyzsk發表於2020-08-19
作者: 360安全衛士 · 2015/07/24 14:33

Proteas of 360 NirvanTeam

0x00 前言


近期發現有人會在微信群中分享影片連結,當使用蘋果裝置的使用者點選這個影片連結播放影片時會造成蘋果裝置重啟。發現這個問題後,[email protected]析,在非越獄iPhone裝置iOS 8.0.2、 iOS 8.4、iOS 8.4.1 Beta 1、iOS 9 Beta 3 系統上進行測試,均導致裝置藍色畫面並重啟,判斷為0Day所致,決定詳細分析導致iPhone藍色畫面的原因。

透過dump arm64位核心及Panic Log詳細分析,確定崩潰發生在核心擴充套件 AppleVXD393.kext 中。該核心擴充套件主要用來解碼影片幀,導致漏洞利用的原因為:沒有校驗指標的合法性,對空指標進行解引用所致;此外在分析過程中,我們發現了該擴充套件模組還存在另外一個漏洞利用的0Day,已經提交蘋果官方等待確認。

為了方便測試,我們首先編寫了一個簡單 App,目的是便於測試、觸發系統崩潰,觸發後導致藍色畫面重啟如下圖(影片演示:http://v.youku.com/v_show/id_XMTI5MTgzNjc2NA),接著為了定位核心崩潰發生的模組及具體程式碼,下文將做詳細分析。

enter image description here

0x01 漏洞影響


  1. 該DoS影響非越獄裝置,綜合手中的測試裝置及逆向分析,可影響iOS 8 以上的所有64位裝置:iPhone 5s,iPhone 6, iPhone 6 Plus,iPad Air, iPad mini 等。
  2. 如果利用網站或者藉助某個平臺,可以造成大規模的拒絕服務。

0x02 編寫輔助崩潰的程式


由於在分析的過程中我們很可能需要多次造成核心崩潰,如果每次都從微信中觸發崩潰會很麻煩,也很不合理,因此我們需要先獲取影片,然後用自己的程式來播放影片。

這個程式很簡單,執行起來的介面如下圖:

enter image description here

只要點選“Play Video”就會調起播放器播放相應影片,進而引起系統崩潰。這裡遇到個小問題,之前播放影片都是使用 MPMoviePlayerController,但是 MPMoviePlayerController 在播放有問題的影片時一直處於載入狀態,於是換 AVPlayer 來播放影片,為了方便大家構建 Demo 進行測試,給出 “Play Video”的相應程式碼:

#!java
@implementation ViewController
// 配置介面
- (void)viewDidLoad {
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor lightGrayColor];

    UIButton *playBtn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    playBtn.frame = CGRectMake(40.0f, 40.0f, 200.0f, 48.0f);
    playBtn.center = self.view.center;
    playBtn.backgroundColor = [UIColor darkGrayColor];
    [playBtn setTitle:@"Play Video" forState:UIControlStateNormal];
    [playBtn setTitle:@"Play Video" forState:UIControlStateHighlighted];
    playBtn.titleLabel.font = [UIFont systemFontOfSize:32.0f];

    [playBtn addTarget:self action:@selector(onPlayButtonClicked:) forControlEvents:UIControlEventTouchUpInside];

    [self.view addSubview:playBtn];
}
// 防止螢幕旋轉時介面錯亂,影響心情
- (NSUInteger)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskPortrait;
}
// 響應按鈕點選,調起播放器
- (void)onPlayButtonClicked:(UIButton *)aSender
{
    NSString *videoPath = [[NSBundle mainBundle] pathForResource:@"crash" ofType:@"mp4"];

    self.avPlayer = [AVPlayer playerWithURL:[NSURL fileURLWithPath:videoPath]];

    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
    self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
    playerLayer.frame = self.view.bounds;
    [self.view.layer addSublayer: playerLayer];

    [self.avPlayer play];
}
@end

0x03 定位崩潰點與相關模組


首先播放影片,系統會發生崩潰,在裝置重啟後,從裝置上讀取崩潰日誌,崩潰日誌的主要內容如下:

enter image description here

上圖中比較重要的值已經被圈出來了,其中 pc 與 lr 用來定位崩潰點。另外 kernel slide 也非常重要,因為上圖中的暫存器值都是經過 slide 後的值,且這個 slide 每次啟動都會變化(KASLR),我們首先需要換算出真實的地址:

pc = 0xffffff80020c92e0 = 0xffffff800e2c92e0 - 0x000000000c200000 
lr = 0xffffff8003043a58 = 0xffffff800f243a58 - 0x000000000c200000

在獲得真實的崩潰地址後,我們 dump 核心(因為目前還沒有可以解密的 arm64 核心):

[+] kernel slide: 0x2000000
[+] kernel start: 0xffffff8004002000
[+] vm perm value: 0x9d46a8bdc73a4755

可以看到這次啟動的 slide 的值與崩潰時的核心 slide 是不同的,接下來我們將崩潰地址換算到當前 dump 到的核心:

pc = 0xffffff80040c92e0 = 0xffffff80020c92e0 + 0x2000000
lr = 0xffffff8005043a58 = 0xffffff8003043a58 + 0x2000000

然後在 IDA Pro 定位到相應的地址, PC指向如下圖:

enter image description here

LR 指向如下圖:

enter image description here

從崩潰日誌中可以知道崩潰時 x0 = 0x0000000000000000,可以確定崩潰的原因是由於對空指標解引用,即:沒有校驗入口引數的合法性。

我們知道 iOS 核心(xnu)本身不包含影片處理相關的功能,影片相關的功能基本是由某個核心擴充套件進行處理的,下面就需要找出具體是哪個核心擴充套件出問題。

比較幸運的是, LR 指向的地址周圍包含了核心擴充套件的資訊:

enter image description here

enter image description here

我們獲取執行時核心擴充套件資訊,看看有沒有與 AppleVXD393 相關的核心擴充套件,結果如下:

enter image description here

可以看到我們找到了相關的核心擴充套件。

由於目前已經可以解密較新的 armv7 的核心,檢視相關的核心沒有發現這個核心擴充套件,且在 iOS 7.x 的 armv7 裝置上測試播放影片,核心並沒有崩潰。綜合相關資訊,這個問題可能隻影響 arm64 裝置。

0x04 確定使用者空間中呼叫 AppleVXD393 服務的模組


首先需要確定這個核心擴充套件是不是在沙盒內使用的,經過測試發現:這個核心擴充套件不是在沙盒中呼叫的,下面就需要確定具體是哪個模組在使用這個服務。 因為呼叫核心擴充套件的服務時,需要根據服務名稱來獲取服務,於是在 grep 了下 iOS 8.0.2 的系統庫,得到如下資訊:

grep -r "AppleVXD393" _cached-dyld/v8.0.2/libraries-arm64
Binary file MediaToolbox.framework/MediaToolbox matches
Binary file VideoToolbox.framework/VideoToolbox matches
Binary file VideoDecoders/MP4VH6.videodecoder matches

由於播放 mp4 引起的崩潰,所以 MP4VH6.videodecoder 讓我們更感興趣,grep MP4VH6 得到的資訊如下:

grep -r "MP4VH6" _cached-dyld/v8.0.2/libraries-arm64 
Binary file VideoToolbox.framework/VideoToolbox matches
Binary file VideoDecoders/MP4VH6.videodecoder matches

結合對 VideoToolBox 的反彙編分析,可以知道 VideoToolBox 利用 MP4VH6.videodecoder 來做 MP4 解碼,將 MP4VH6.videodecoder 拖到 IDA Pro 中,檢視匯出表:

enter image description here

可以看到與 AppleVXD393 相關的函式,MP4VH6.videodecoder 應該封裝了核心擴充套件 com.apple.driver.AppleVXD393 的使用者空間介面。

0x05 確定影片解碼服務


透過上面的分析我們已經知道:1、MP4VH6.videodecoder 封裝了影片解碼服務的使用者空間介面;2、VideoToolBox.framework 直接呼叫了 MP4VH6.videodecoder。接下來我們需要知道哪個後臺程式透過 xpc 對外提供影片解碼服務,ps + grep:

ps aux |grep media
mobile     214   MusicLibrary.framework/Support/medialibraryd
mobile     119   /usr/sbin/mediaserverd
mobile     117   MediaRemote.framework/Support/mediaremoted

由於 xpc 服務都需要checkin,逆向分析這三個程式,最後發現 mediaserverd checkin 瞭如下兩個服務:

com.apple.mediaserverd
com.apple.audio.SystemSounds

mediaserverd 可能是我們要找的後臺服務。為了確定這一點,又除錯了下 mediaserverd,斷點命中後,得到資訊如下:

(lldb) x/s $x0
0x19368065f: "AppleVXD393"
(lldb) bt
* thread #16: tid = 0x21cb, IOKit`IOServiceMatching
  * frame #0:  IOKit`IOServiceMatching
    frame #1: H264H6.videodecoder`AppleVXD393CheckPlatform + 60
    frame #2: H264H6.videodecoder`H264H6Register + 16
    frame #3: VideoToolbox`VTLoadVideoDecoders + 168
    frame #4: libsystem_pthread.dylib`__pthread_once_handler + 80
    frame #5: libsystem_platform.dylib`_os_once + 56
    frame #6: libsystem_pthread.dylib`pthread_once + 76
    frame #7: VideoToolbox`VTSelectAndCreateVideoDecoderInstanceInternal + 136
    frame #8: VideoToolbox`VTSelectAndCreateVideoDecoderInstance + 44
    frame #9: MediaToolbox`FPSupport_GetDefaultTrackIDForMediaType + 428
    frame #10: MediaToolbox`itemfig_setBasicInspectables + 808
    frame #11: MediaToolbox`itemfig_retrieveAssetBasicsIfReady + 276
    frame #12: MediaToolbox`itemfig_assetPropertyLoaded + 324

至此確定 mediaserverd 負責提供影片解碼服務,也可以大概知道 iOS 平臺的影片解碼功能的大致架構。

0x06 分析可控性


首先看下影片解碼時的呼叫棧:

(lldb) bt
* thread #19: tid = 0x2020,  IOKit`IOConnectCallMethod
  * frame #0: IOKit`IOConnectCallMethod
    frame #1: IOKit`IOConnectCallStructMethod + 52
    frame #2: MP4VH6.videodecoder`AppleVXD393DecodeFrameInternal + 428
    frame #3: MP4VH6.videodecoder`AppleVXD393DecodeFrame + 1264
    frame #4: MP4VH6.videodecoder`AppleVXD393WrapperMPEG4DecoderDecodeFrame + 728
    frame #5: VideoToolbox`vtDecompressionDuctDecodeSingleFrame + 348
    frame #6: VideoToolbox`VTDecompressionSessionDecodeFrame + 440
    frame #7: MediaToolbox`vmc2DecodeUntilHighWaterMet + 3308
    frame #8: MediaToolbox`activitySchedulerOnThread + 72
    frame #9: CoreMedia`figThreadMain + 248
    frame #10: libsystem_pthread.dylib`_pthread_body + 164
    frame #11: libsystem_pthread.dylib`_pthread_start + 160

為了確定可控性,於是編寫了一個 Tweak 來 Hook mediaserverd,並 Hook IOConnectCallStructMethod,在發現待解碼的影片幀序號與引起核心崩潰的影片幀序號一致時,修改交由核心解碼的資料,檢視、對比崩潰日誌,總結輸入對崩潰時暫存器值的影響。 對緩衝區一共進行如下幾個填充測試:

enter code here

緩衝區填充測試,0x00 vs. 0x01:

enter image description here

可以看到 PC 暫存器有差別,說明走的不是相同的程式碼路徑。在對緩衝填充 0x01 時,核心的崩潰點為:

enter image description here

緩衝區填充測試,0x01 vs. 0x02:

enter image description here

可以看到 x0 的部分位是可控的,x12, x13 是從 x1 指向的地址處載入的。至此已經完成了對問題的分析,至於利用,就大家各顯神通吧。

0x07 其他


一、經過處理的只包含單幀的影片,

連結:http://yunpan.cn/cc5KWG7p368yN

提取碼:4e17

MD5:f4260553b0628f2e6bbb88e2c7b124e6

二、如果大家有利用思路,歡迎關注微博交流:@NirvanTeam

此外,對iOS安全領域開發、漏洞分析、挖掘感興趣的同學[email protected]@360.cn聯絡我們。

本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章