來源:Flanker Sky
作者:flanker017

ImageIO
適用于:iPhone 5 及更新機型、iPad 第 4 代及更新機型、iPod touch 第 6 代及更新機型
影響:處理惡意制作的圖像可能會導致任意代碼執行
說明:內存損壞問題已通過改進輸入驗證得到解決。
CVE-2017-2416:騰訊科恩實驗室的 @flanker_hqd

(For English version see https://blog.flanker017.me/cve-2017-2416-gif-remote-exec/)

Abstract

前段時間偶然發現了一個ImageIO.framework中的圖像解析漏洞,通過發送這個惡意圖片,可以在任何有圖片顯示功能的應用中直接觸發該漏洞,特別是各種IM應用(例如iMessage, Telegram, Slack, iMessage和國產流行IM,以及郵件應用例如Mail, Outlook, Inbox, Gmail,還有一些想做IM的金融應用例如alipay等),導致應用崩潰。在精心布置的內存布局下還有遠程代碼執行的可能。

讓問題變得更蛋疼的是,很多客戶端通常會在啟動的時候再去嘗試恢復加載之前的記錄,也包括圖片,這導致每次啟動的時候該漏洞都會被觸發,自動地成為了一個可持續的漏洞 – -b 例如iMessage和Mail即是如此。通過iMessage給一個沒有升級到10.12.4的人發送攻擊圖片,其iMessage就再也打不開了。

DEMO videos

第一個視頻展示了發送一條惡意imessage就導致對方崩潰的過程

然后被攻擊的設備就再也打不開imessage了

Crash trace

* thread #1: tid = 0x17570, 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
ImageIO`IIOReadPlugin::IIOReadPlugin:
->  0x7fff9557f1ab <+67>: mov    al, byte ptr [rdi + 0x40]
    0x7fff9557f1ae <+70>: mov    qword ptr [rbx + 0x20], rdi
    0x7fff9557f1b2 <+74>: mov    byte ptr [rbx + 0xc8], al
    0x7fff9557f1b8 <+80>: xor    eax, eax

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.ImageIO.framework        0x00007fffa144d1ab IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
1   com.apple.ImageIO.framework        0x00007fffa14b8c93 GIFReadPlugin::InitProc(CGImagePlugin*, unsigned long, unsigned long) + 59
2   com.apple.ImageIO.framework        0x00007fffa14177da IIOImageSource::makeImagePlus(unsigned long, __CFDictionary const*) + 252
3   com.apple.ImageIO.framework        0x00007fffa141918b IIOImageSource::getPropertiesAtIndexInternal(unsigned long, __CFDictionary const*) + 57
4   com.apple.ImageIO.framework        0x00007fffa141911c IIOImageSource::copyPropertiesAtIndex(unsigned long, __CFDictionary const*) + 98
5   com.apple.ImageIO.framework        0x00007fffa13f03ca CGImageSourceCopyPropertiesAtIndex + 181
6   com.apple.AppKit                   0x00007fff9cfdbcae +[NSBitmapImageRep _imagesWithData:hfsFileType:extension:zone:expandImageContentNow:includeAllReps:] + 543
7   com.apple.AppKit                   0x00007fff9cfdba68 +[NSBitmapImageRep _imageRepsWithData:hfsFileType:extension:expandImageContentNow:] + 93
8   com.apple.AppKit                   0x00007fff9d4bf08e -[NSImage _initWithData:fileType:hfsType:] + 479

在蘋果平臺上,基本所有的圖像解析功能最后都會調用[NSImage _initWithData:fileType:hfsType:], 隨后IIOImageSource將圖像的解析根據圖像頭特征分配到對應的plugin中。注意這里并不是基于文件擴展名做的判斷,所以后續我們可以通過這個特性繞過過濾實現利用。

漏洞樣例圖片

如果把它拖動到任意macos/iOS app中的時候崩潰了,那么你的系統受該漏洞影響,趕快升級吧。 測試樣例文件下載:Sample PNG Sample GIF 僅供自測使用,請勿用于非法用途例如發送給他人。

漏洞分析

漏洞的一個源頭在GIFReadPlugin::init函數中,觀察如下反匯編代碼:

  v32 = (signed __int16)width * (signed __int64)height;
  if ( v32 > filesize * 1100 * v29 )
  {
    LOBYTE(aspectbyte) = 0;
    v15 = 0LL;
    if ( this->gapC0[8] )
    {
      LOBYTE(aspectbyte) = 0;
      LogError(
        "init",
        498,
        "malformed GIF file (%d x %d) - [canvasSize: %ld  fileSize: %ld   ratio: %d]  \n",
        (unsigned int)(signed __int16)width,
        (unsigned int)(height),    // width >> 16 is height
        (signed __int16)width * (signed __int64)SHIWORD(width),
        filesize,
        v32 / filesize);
      v15 = 0LL;
    }
    goto LABEL_71;
  }

__text:00000000000CC51F                 movsx   rax, r9w
__text:00000000000CC523                 mov     ecx, r9d
__text:00000000000CC526                 shr     ecx, 10h
__text:00000000000CC529                 movsx   rbx, cx
__text:00000000000CC52D                 imul    rbx, rax
__text:00000000000CC531                 imul    rdx, r12, 44Ch
__text:00000000000CC538                 mov     rax, rdx
__text:00000000000CC53B                 imul    rax, rsi
__text:00000000000CC53F                 cmp     rbx, rax

一個攻擊者可以構造負數的高度和長度,bypass掉對filesize的比較,造成后續內存越界訪問。一般來講攻擊者可以通過手動構造圖片輸入流/hook進行發送,或者通過app服務自身提供的web服務來進行發送。前面提到過ImageIO解析圖片的時候并不是通過判斷擴展名來進行的,通過這個特性我們可以同樣bypass一些web圖片上傳界面的過濾,將惡意圖片成功發送到對方設備上,粗發漏洞。

相對來講稍微令人詫異的是蘋果的修復。補丁并沒有打在size比較這里,而是打在了IIOReadPlugin這里。在補丁之前,IIOReadPlugin的關鍵代碼如下所示:

bool __fastcall IIOReadPlugin::IIOReadPlugin(IIOReadPlugin *a1, __int64 a2, int a3, int a4, __int64 a5, unsigned __int8 a6)
{
    unsigned __int8 v6; // r14@1
    IIOReadPlugin *this; // rbx@1
    __int64 v8; // rax@1
    __int64 sessionwrap; // rdi@1
    IIOImageReadSession *session; // rax@2
    IIOImageRead *v11; // rdi@2
    __int64 v12; // rax@2
    __int64 *v13; // rcx@5
    __int64 v14; // rdx@5
    bool result; // al@5

    v6 = a6;
    this = a1;
    a1->vt = (__int64)off_1659D0;
    a1->field_8 = a2;
    v8 = *(_QWORD *)(a2 + 24);
    a1->field_10 = v8;
    a1->field_38 = a3;
    a1->field_3c = a4;
    a1->field_30 = a5;
    sessionwrap = *(_QWORD *)(v8 + 24);
    if ( sessionwrap )
    {
        session = (IIOImageReadSession *)CGImageReadSessionGetSession(sessionwrap); //session is invalid
        this->session = session;
        v11 = (IIOImageRead *)session->imageread; //oob happens here and lead to crash
        LOBYTE(session) = v11->field_40;
        this->field_20 = (__int64)v11;
        this->field_c8 = (char)session;
        v12 = 0LL;
        if ( v11 )
            v12 = IIOImageRead::getSize(v11);
    }
    else
    {
        this->field_20 = 0LL;
        this->session = 0LL;
        this->field_c8 = 1;
        v12 = 0LL;
    }

在10.12.4中,if分支語句變成了如下所示:

  a1->field_8 = cgimgplus;
  imageplus = CGImagePlusGetIPlus(cgimgplus);
  a1->field_10 = imageplus;
  a1->field_38 = v9;
  a1->field_3c = v8;
  a1->field_30 = v7;
  v12 = *(_QWORD *)(imageplus + 32);
  a1->field_18 = v12;
  imageread = *(IIOImageRead **)(v12 + 32);
  if ( imageread )
  {
    v10->field_c8 = *((_BYTE *)imageread + 64);
    v10->field_20 = (__int64)imageread;
    v14 = IIOImageRead::getSize(imageread);
  }
  else
  {
    v10->field_c8 = 0;
    v10->field_20 = 0LL;
    v14 = 0LL;
  }

IIOImageReadSession的使用在這里被移除了。這是否從根源上解決了問題?讓我們拭目以待。

對開發者和用戶的建議

對于想自行防御這個問題的開發者來說(畢竟有很多用戶沒有升級到最新版,鍋還是會被他們扣在開發者頭上),我建議在圖片顯示前先自行檢查下GIF寬度和高度。

對于終端用戶來講,當然升級系統是最好的辦法了。

Timeline

  • 2017.1.10 Initial discovery
  • 2017.1.16 Report to Apple
  • 2017.1.24 Apple responds on they are working on a fix
  • 2017.3.23 CVE-2017-2416 assigned
  • 2017.3.28 Advisory published at https://support.apple.com/zh-cn/HT207617
  • 2017.4.6 Public disclosure

Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/268/