歡迎加入QQ討論群258996829
一葉知秋 頭像
蘋果2袋
2
一葉知秋

Xcode 7 Bitcode的工作流程及安全性評估

發(fā)布時間:2015-12-18 20:03  回復(fù):0  查看:3808   最后回復(fù):2015-12-18 20:03  

簡介

隨著 Xcode 7 的發(fā)布,蘋果為 Xcode 增加了一個新的特性 Bitcode [1]:

Xcode 7 Bitcode的工作流程及安全性評估

新的特性往往意味著新的攻擊面。本文首先介紹什么是 Bitcode 及 Bitcode 相關(guān)的工作流程,在熟悉了 Bitcode 的工作流程后,接下來是評估 Bitcode 相關(guān)的攻擊面,最后介紹針對各個攻擊面的測試方法及目前的測試結(jié)果。

什么是 Bitcode

簡單來說,Bitcode 是 LLVM-IR 在磁盤上的一種二進(jìn)制表示形式。關(guān)于 Bitcode 詳細(xì)描述,請參考[2],這里會用例子來讓大家對 Bitcode 有個感性認(rèn)識。

先寫一個簡單的 C 程序,功能是計(jì)算兩個數(shù)的和,代碼如下:

int add(int a, int b)
{
      int c = a + b;
      return c;
}
將如上程序保存為 add.c,然后我們將源程序編譯成 Bitcode:
clang -emit-llvm -c add.c -o add.bc

執(zhí)行如上命令會生成 add.bc,我們使用二進(jìn)制編輯器打開生成的文件,查看文件內(nèi)容:

Xcode 7 Bitcode的工作流程及安全性評估

由于 Bitcode 是 LLVM-IR 的二進(jìn)制表示形式,如上圖,在不了解編碼方式的前提下基本不可讀。下面我們把 Bitcode 轉(zhuǎn)換成文本形式:

llvm-dis add.bc -o add.ll
用文本編輯器打開 add.ll,可以看到 add 函數(shù)的 LLVM-IR 內(nèi)容如下:
; ModuleID = 'add.bc'
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.11.0"
; Function Attrs: nounwind ssp uwtable
; 如下是 add() 對應(yīng)的 LLVM-IR
; 可以注意到這種表示形式會申請很多變量,
; 感興趣的同學(xué)可以了解下  Static Single Assignment (SSA)
define i32 @add(i32 %a, i32 %b) #0 {
  %1 = alloca i32, align 4           ; 變量 1,4 字節(jié)空間,后續(xù)用來存放參數(shù) a
  %2 = alloca i32, align 4           ; 變量 2,4 字節(jié)空間,后續(xù)用來存放參數(shù) b
  %c = alloca i32, align 4           ; 變量 c,4 字節(jié)空間,后續(xù)用來存放結(jié)果 c
  store i32 %a, i32* %1, align 4 ; 將 a 保存到變量 1 中
  store i32 %b, i32* %2, align 4 ; 將 b 保存到變量 2 中
  %3 = load i32, i32* %1, align 4    ; 將立即數(shù) 1 保存到變量 3 中
  %4 = load i32, i32* %2, align 4    ; 將立即數(shù) 2 保存到變量 4 中
  %5 = add nsw i32 %3, %4            ; 將變量 3 與變量 4 的和保存到變量 5 中
  store i32 %5, i32* %c, align 4 ; 將變量 5 保存到結(jié)果 c 中
  %6 = load i32, i32* %c, align 4    ; 將結(jié)果 c 保存到變量 6 中
  ret i32 %6                     ; 返回變量 6
}

對比源碼與已經(jīng)注釋過的 add() 函數(shù)的 LLVM-IR 表示,大家應(yīng)該對 LLVM-IR 有個感性認(rèn)識了,下面我們一起看下 Bitcode 的工作流程。

工作流程

蘋果關(guān)于工作流程的描述:

“ When you archive for submission to the App Store, Xcode compiles your app into an intermediate representation. The App Store then compiles the bitcode down into the 64- or 32-bit executables as necessary.”

如上的工作流程可以分為兩個階段:

1.在將應(yīng)用上傳到 AppStore 時,Xcode 會將程序?qū)?yīng)的 Bitcode 一起上傳。
2.AppStore 會將 Bitcode 重新編譯為可執(zhí)行程序,供用戶下載。

下面會將 Bitcode 相關(guān)的完整的工作流程分解為如下幾個問題或子過程并分別做說明:

1.Where is the Bitcode?
2.嵌入 Bitcode 的方法
3.從 Bitcode 生成可執(zhí)行程序的方法

Where is the Bitcode?

參考蘋果的描述,只有在 Archive 時才會生成 Bitcode,于是建立了一個測試工程:

Xcode 7 Bitcode的工作流程及安全性評估

執(zhí)行 Archive,然后查看生成的包結(jié)構(gòu):

Xcode 7 Bitcode的工作流程及安全性評估

經(jīng)過分析在如上的目錄中并沒有直接找到 Bitcode,接下來檢查生成的 MachO。使用 MachOView 加載生成的 MachO,結(jié)果如下圖:

Xcode 7 Bitcode的工作流程及安全性評估

從上圖可以看到最終的可執(zhí)行程序中多了 LLVM 相關(guān)的 Segment 與 Section。繼續(xù)查看對應(yīng)的 Section 的信息:

Xcode 7 Bitcode的工作流程及安全性評估

如上圖,Section __bundle 中保存的是一個 xar 文檔,提取出 xar 文檔,然后使用如下命令解開文檔:

解開:xar -x -f XXX.xar

解開后,可以看到 Bitcode 文件。

總結(jié):程序?qū)?yīng)的 Bitcode 被 Xcode 打包成 xar 文檔,嵌入的 MachO 中。

下面我們看下在 MachO 中嵌入 Bitcode 的方法。

嵌入 Bitcode 的方法

方法一

通過對比 Archive 與非 Archive 時的編譯參數(shù),發(fā)現(xiàn)只要在如下圖所示的位置添加編譯參數(shù):-fembed-bitcode,即可讓 Xcode 普通編譯時也在 MachO 中嵌入 Bitcode:

Xcode 7 Bitcode的工作流程及安全性評估

方法二

方法一雖然很方便,但是 IDE 做了太多工作,不便于理解具體過程,接下來我們自己編譯可執(zhí)行文件。從源代碼生成可執(zhí)行程序主要分為:編譯、鏈接兩個過程,為了控制這兩個過程,下面會講解 Makefile 的配置,及這兩個過程用到的參數(shù)。

在使用 Makefile 編譯 iOS 程序時,有些通用的配置,如下的通用配置,供大家參考:

SDK_iOS := $(shell xcodebuild -version -sdk iphoneos Path)
CC_iOS := $(shell xcrun --sdk iphoneos --find clang)
LD_iOS := $(CC_iOS)
SYS_ROOT = -isysroot $(SDK_iOS)
SDK_SETTINGS_iOS = $(SYS_ROOT) -I$(SDK_iOS)/usr/include -I$(SDK_iOS)/usr/local/include
MIN_VER_iOS = -miphoneos-version-min=8.0
ARCH_iOS = -arch arm64
以 main.m 為例說明編譯需要的參數(shù):
CC_FLAGS_COMMON = -fblocks -std=gnu99 -fobjc-arc -g -fembed-bitcode
CC_FLAGS=-x objective-c $(ARCH_iOS) $(CC_FLAGS_COMMON)
COMPILE_iOS_OBJ=$(CC_iOS) $(MIN_VER_iOS) $(SDK_SETTINGS_iOS) $(CC_FLAGS)
 
$(COMPILE_iOS_OBJ) -c main.m -o main.o
將 main.o,AppDelegate.o,ViewController.o 鏈接成可執(zhí)行程序的參數(shù):
LDFLAGS=$(SYS_ROOT) \
        -dead_strip \
        -fembed-bitcode \
        -fobjc-arc -fobjc-link-runtime
LINK_iOS_BIN=$(LD_iOS) $(ARCH_iOS) $(MIN_VER_iOS) $(LDFLAGS)
LDFLAGS_CUSTOM=-framework Foundation -framework UIKit
$(LINK_iOS_BIN) $(LDFLAGS_CUSTOM) AppDelegate.o ViewController.o main.o -o XBCTest

大家把如上的 Makefile 片段稍加修改,整理到一個 Makefile 文件中,就可以通過 make 命令嵌入 Bitcode 到可執(zhí)行程序。

方法三

在這個方法中我們會將上面的步驟進(jìn)一步分解,具體過程為:

源碼→Bitcode→xar→可執(zhí)行程序

源碼→Bitcode

在這個過程中我們將 iOS 應(yīng)用的源碼編譯成 Bitcode,下面會 main.m 為例來說明使用的參數(shù):

CC_FLAGS_COMMON_BC = $(CC_FLAGS_COMMON)
COMPILE_iOS_32_BC = $(CC_iOS) -cc1 -x objective-c $(CC_FLAGS_COMMON_BC) -triple thumbv7-apple-ios8.0.0 -disable-llvm-optzns -target-abi apcs-gnu -mfloat-abi soft $(SYS_ROOT)
COMPILE_iOS_64_BC = $(CC_iOS) -cc1 -x objective-c $(CC_FLAGS_COMMON_BC) -triple arm64-apple-ios8.0.0 -disable-llvm-optzns -target-abi darwinpcs $(SYS_ROOT)
 
$(COMPILE_iOS_64_BC) -emit-llvm-bc main.m -o main.bc

完成這個過程后,我們可以得到三個Bitcode 文件:

1.main.bc

2.AppDelegate.bc

3.ViewController.bc

Bitcode→xar

在這一步我們會將如上得到的三個 Bitcode 文件打包到一個 xar 文檔中。打包沒什么特別,需要注意的是需要與 Xcode 生成的 xar 保持兼容,具體參數(shù)如下:

生成:xar –toc-cksum none -c -f BC.xar main.bc AppDelegate.bc ViewController.bc

xar→可執(zhí)行程序

為了簡化過程,這里我們會跳出 Makefile,使用 Xcode,首先清除如下的編譯參數(shù):

Xcode 7 Bitcode的工作流程及安全性評估

將剛剛生成的 BC.xar 拷貝到測試工程的根目錄:

Xcode 7 Bitcode的工作流程及安全性評估

編輯工程設(shè)置的 Other Linker Flags,添加:-Wl,-sectcreate,__LLVM,__bundle,$(SRCROOT)/BC.xar,如下圖:

Xcode 7 Bitcode的工作流程及安全性評估

編譯程序,查看生成的 MachO 文件,可以看到 Bitcode 已經(jīng)被添加到了 MachO 中。

如上我們介紹了在 MachO 中嵌入 Bitcode 的方法,對應(yīng)的是第一個過程:“在將應(yīng)用上傳到 AppStore 時,Xcode 會將程序?qū)?yīng)的 Bitcode 一起上傳”,下面我們說明第二個過程。

從 Bitcode 生成可執(zhí)行程序的方法

第二個過程為:“AppStore 會將 Bitcode 重新編譯為可執(zhí)行程序,供用戶下載”。第二個過程是在蘋果的 Server 上進(jìn)行的,我們沒法直接獲得細(xì)節(jié),但是應(yīng)該都是基于相同的工具鏈,我們可以模擬這個過程。

從 MachO 中提取 Bitcode

AppStore 拿到我們上傳的 IPA 后,首先需要從 IPA 內(nèi)的 MachO 文件中提取出包含 Bitcode 的 xar 文檔。在 Xcode 的工具鏈中有個工具 segedit 可以用來從 MachO 提取 Section,提取 xar 的具體參數(shù)如下:

segedit ./XBCTest -extract "__LLVM" "__bundle" Embedded-BC.xar
提取到 xar 后,解開 xar:
解開:xar -x -f Embedded-BC.xar

得到如下幾個 Bitcode 文件:

Xcode 7 Bitcode的工作流程及安全性評估

還可以使用 llvm-dis 工具將如上文件處理成可讀形式,從而了解每個文件的內(nèi)容。

生成可執(zhí)行程序

在有了 Bitcode 后,接下來需要將 Bitcode 編譯成可執(zhí)行程序,分為兩個過程:將 Bitcode 編譯成 Object 文件;鏈接 Object 文件到可執(zhí)行程序。

將 Bitcode 編譯成 Object 文件

Makefile 片段如下:

COMPILE_iOS_BC_2_OBJ=$(CC_iOS) $(MIN_VER_iOS) $(SYS_ROOT) $(ARCH_iOS)
 
$(COMPILE_iOS_BC_2_OBJ) -c 1 -o 1.o
$(COMPILE_iOS_BC_2_OBJ) -c 2 -o 2.o
$(COMPILE_iOS_BC_2_OBJ) -c 3 -o 3.o
$(COMPILE_iOS_BC_2_OBJ) -c 4 -o 4.o

鏈接 Object 文件到可執(zhí)行程序

Makefile 片段如下:

LDFLAGS=$(SYS_ROOT) \
        -dead_strip \
        -fobjc-arc -fobjc-link-runtime
LINK_iOS_BIN=$(LD_iOS) $(ARCH_iOS) $(MIN_VER_iOS) $(LDFLAGS)
LDFLAGS_CUSTOM=-framework Foundation -framework UIKit
$(LINK_iOS_BIN) $(LDFLAGS_CUSTOM) 1.o 2.o 3.o 4.o -o XBCTest

如上我們已經(jīng)從 Bitcode 重新生成了可執(zhí)行程序 XBCTest。

攻擊面

我們先回顧下 Bitcode 在本地的工作流程:Xcode 將嵌入了 Bitcode 的 MachO 上傳到 AppStore。通過分析可以發(fā)現(xiàn)這里存在兩個問題:

1.MachO 與其中嵌入的 Bitcode 的一致性問題。即:能否把 程序B 的  Bitcode 嵌入到 程序A中。

2.AppStore 是否信任了 Xcode,而沒有檢查一致性問題,從而允許將   Malformed MachO 上傳到 AppStore。

在分析了可能存在的問題后,我們認(rèn)為如果 Bitcode 流程與功能存在缺陷,便可以對兩個目標(biāo)形成威脅:普通用戶、蘋果。

普通用戶

由于 Bitcode 對普通用戶是透明的,因此無法通過其弱點(diǎn)直接攻擊用戶。但是一致性問題是可能對普通用戶造成威脅的,試想:如果提交 AppStore 審核的 程序A 中嵌入了含有惡意代碼的Bitcode,普通用戶就有可能從AppStore 下載到含有惡意代碼的程序。

對于這種攻擊方式我們將其叫做 Bitcode Injection,下文會詳細(xì)介紹這種攻擊的實(shí)施方法,及我們的測試結(jié)果。

蘋果

如果 Malformed MachO 可以被上傳到蘋果的服務(wù)器,蘋果的服務(wù)器相較于之前,主要需要進(jìn)行兩個額外的操作:解開 xar;編譯 Bitcode。如果這兩個過程出現(xiàn)問題,輕則可以在蘋果的服務(wù)器上造成 DoS,重則可以在蘋果的服務(wù)器上造成任意代碼執(zhí)行。

另外,Bitcode 原本是 LLVM-IR 的一種序列化形式,而 LLVM-IR 是一種中間形式,之前沒有被直接暴露出來,現(xiàn)在完全開放了,而且又是二進(jìn)制格式,這是很容易出問題的。從 Bitcode 生成可執(zhí)行文件的過程主要由如下幾個子過程組成:

1.基于平臺無關(guān)的 IR的代碼優(yōu)化。

2.IR的平臺相關(guān)化、合法化。

3.平臺相關(guān)的優(yōu)化、代碼生成。

這些原本是編譯器的內(nèi)部過程,由于各種原因,傳統(tǒng)的對編譯器的測試主要集中在前端的 Parser 與 Lexer,現(xiàn)在借由 Bitcode 如上的一些中間或者后端過程也暴露了出來,如果如上的過程出現(xiàn)問題最糟糕的結(jié)果是可以控制編譯器的指令生成。

以上是關(guān)于攻擊面的分析,后文會介紹測試 xar 及 Bitcode 的思路,以及發(fā)現(xiàn)的問題。

Bitcode Injection

上文在介紹 Bitcode 工作流程時已經(jīng)介紹了實(shí)施 Bitcode Injection 的方法,但是上面提到的方法不夠簡練,這里我們再介紹一種更簡單的方法,主要的思路就是最大限度的利用 Xcode,這個方法的具體實(shí)施步驟為:

1. 用 Xcode 建立工程XBCTest:

Xcode 7 Bitcode的工作流程及安全性評估

2. 復(fù)制工程XBCTest,得到工程XBCTest2:

Xcode 7 Bitcode的工作流程及安全性評估

3. 修改工程XBCTest2的源碼,嵌入惡意代碼:

Xcode 7 Bitcode的工作流程及安全性評估

4. Archive 工程XBCTest2:

Xcode 7 Bitcode的工作流程及安全性評估

Xcode 7 Bitcode的工作流程及安全性評估

5. 獲得 MachO,利用 segedit 從 MachO 中提取出含有 Bitcode 的 xar:

Xcode 7 Bitcode的工作流程及安全性評估

提取xar: segedit ./XBCTest -extract "__LLVM" "__bundle" BC.xar

6. 修改工程XBCTest的鏈接標(biāo)記,將提取的 xar: BC.xar 嵌入到工程XBCTest的 MachO文件中。

7. 禁用工程XBCTest的 Bitcode 特性,Archive 并上傳 AppStore:

Xcode 7 Bitcode的工作流程及安全性評估

我們在測試的過程中并沒有嵌入惡意代碼,而是從網(wǎng)上找個兩個完全不同的應(yīng)用,將其中一個的 Bitcode 嵌入到另一個的 MachO 中,并提交到AppStore。

在將應(yīng)用提交到 AppStore 的過程中主要會進(jìn)行兩個方面的檢查:Xcode 在本地進(jìn)行靜態(tài)分析;提交后,蘋果的服務(wù)器還會進(jìn)行檢查。但是使用 Bitcode Injection 構(gòu)造的應(yīng)用可以通過這兩項(xiàng)檢查:

Xcode 7 Bitcode的工作流程及安全性評估

Xcode 7 Bitcode的工作流程及安全性評估

經(jīng)過漫長的審核后,我們的應(yīng)用被拒了,理由是:我們的應(yīng)用與描述不符。在描述中我們的應(yīng)用應(yīng)該長成如下樣子:

Xcode 7 Bitcode的工作流程及安全性評估

但是蘋果的審核人員安裝后,程序卻長成這個樣子:

Xcode 7 Bitcode的工作流程及安全性評估

這至少可以說明三個問題:

1.我們使用的 Bitcode Injection 方法沒有問題。
2.蘋果的審核人員審核的是從 Bitcode 編譯出來的程序。
3.一致性是靠人肉區(qū)分的。如果嵌入對 UI 沒有影響的惡意代碼,還是有可能繞過審核的。

測試xar

思路

對 xar 進(jìn)行模糊測試,生成數(shù)據(jù)的方法是基于標(biāo)準(zhǔn)的 xar 文檔進(jìn)行變異。

測試結(jié)果

目前主要 Fuzz 出一些空指針解引用問題。

測試 clang

思路

對 clang 中的Bitcode 到 Object 的功能進(jìn)行模糊測試,也是采用變異的方法生成測試數(shù)據(jù)。

測試結(jié)果

通過對 clang 的 Fuzz 我們發(fā)現(xiàn)了一些堆損壞相關(guān)的問題。

總結(jié)

1. The Xcode 7 bitcode feature has opened a huge attacking surface, Apple should do something to narrow it, for example: checking the bitcode is identical to the related MachO file.

2. 在上文中我們詳細(xì)介紹了所考慮到的攻擊面,及針對每個攻擊面的測試思路,希望這些對大家研究 Bitcode 相關(guān)的攻擊面及安全性有幫助。

參考資料

[1] What’s New in Xcode

[2] LLVM Bitcode File Format

*作者:360NirvanTeam(企業(yè)賬號),轉(zhuǎn)載請注明來自FreeBuf黑客與極客(FreeBuf.COM)

您還未登錄,請先登錄

熱門帖子

最新帖子

?