Proteas of 360 NirvanTeam
近期發現有人會在微信群中分享視頻鏈接,當使用蘋果設備的用戶點擊這個視頻鏈接播放視頻時會造成蘋果設備重啟。發現這個問題后,[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),接著為了定位內核崩潰發生的模塊及具體代碼,下文將做詳細分析。
由于在分析的過程中我們很可能需要多次造成內核崩潰,如果每次都從微信中觸發崩潰會很麻煩,也很不合理,因此我們需要先獲取視頻,然后用自己的程序來播放視頻。
這個程序很簡單,運行起來的界面如下圖:
只要點擊“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
首先播放視頻,系統會發生崩潰,在設備重啟后,從設備上讀取崩潰日志,崩潰日志的主要內容如下:
上圖中比較重要的值已經被圈出來了,其中 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指向如下圖:
LR 指向如下圖:
從崩潰日志中可以知道崩潰時 x0 = 0x0000000000000000
,可以確定崩潰的原因是由于對空指針解引用,即:沒有校驗入口參數的合法性。
我們知道 iOS 內核(xnu)本身不包含視頻處理相關的功能,視頻相關的功能基本是由某個內核擴展進行處理的,下面就需要找出具體是哪個內核擴展出問題。
比較幸運的是, LR 指向的地址周圍包含了內核擴展的信息:
我們獲取運行時內核擴展信息,看看有沒有與 AppleVXD393 相關的內核擴展,結果如下:
可以看到我們找到了相關的內核擴展。
由于目前已經可以解密較新的 armv7 的內核,查看相關的內核沒有發現這個內核擴展,且在 iOS 7.x 的 armv7 設備上測試播放視頻,內核并沒有崩潰。綜合相關信息,這個問題可能只影響 arm64 設備。
首先需要確定這個內核擴展是不是在沙盒內使用的,經過測試發現:這個內核擴展不是在沙盒中調用的,下面就需要確定具體是哪個模塊在使用這個服務。 因為調用內核擴展的服務時,需要根據服務名稱來獲取服務,于是在 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 中,查看導出表:
可以看到與 AppleVXD393 相關的函數,MP4VH6.videodecoder 應該封裝了內核擴展 com.apple.driver.AppleVXD393 的用戶空間接口。
通過上面的分析我們已經知道: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 平臺的視頻解碼功能的大致架構。
首先看下視頻解碼時的調用棧:
(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,在發現待解碼的視頻幀序號與引起內核崩潰的視頻幀序號一致時,修改交由內核解碼的數據,查看、對比崩潰日志,總結輸入對崩潰時寄存器值的影響。 對緩沖區一共進行如下幾個填充測試:
緩沖區填充測試,0x00 vs. 0x01:
可以看到 PC 寄存器有差別,說明走的不是相同的代碼路徑。在對緩沖填充 0x01 時,內核的崩潰點為:
緩沖區填充測試,0x01 vs. 0x02:
可以看到 x0 的部分位是可控的,x12, x13 是從 x1 指向的地址處加載的。至此已經完成了對問題的分析,至于利用,就大家各顯神通吧。
一、經過處理的只包含單幀的視頻,
鏈接:http://yunpan.cn/cc5KWG7p368yN
提取碼:4e17
MD5:f4260553b0628f2e6bbb88e2c7b124e6
二、如果大家有利用思路,歡迎關注微博交流:@NirvanTeam
此外,對iOS安全領域開發、漏洞分析、挖掘感興趣的同學[email protected]@#聯系我們。