iOS APP安全雜談之三【轉】

0x00 序

聽說最近IOS讓人操碎了心,不到三個月的時間里先后發生太極越獄被曝存在后門,22萬iCloud賬號及機密信息被多款內置后門越獄插件竊取并泄露,XcodeGhost事件多個知名企業中槍,嚇的大家紛紛改了密碼。當然像XcodeGhost這樣的事情用戶是沒辦法避免的,其編譯的APP畢竟是在AppStore上上架的,但是還有一大部分的安全問題其實是完全可以避免的,那就是用戶不要越獄,而廠商的APP在運行前檢查運行的設備是否安全。現在也有很多APP在使用前都會檢查設備是否越獄,如果越獄了則強制退出或者只是提醒一下。那么這次我們就來聊聊越獄之后的那點事。PS:由于本人能力有限,文章難免會有些錯誤,還望小伙伴們見諒。

0x01 越獄之后更美好?

越獄之后可以干什么,可以免費的玩各種付費的游戲,可以自動的搶紅包,可以在接聽電話時自動錄音,是的,想想還有點小激動。

但事實是越獄之后的風險是用戶和廠商都不想看到的,攻擊者可以在越獄環境下改變你的APP執行邏輯(例如通過更改函數的返回值來繞過手勢密碼驗證)、可以通過復制應用數據克隆用戶或者登陸任意用戶、可以通過打補丁的方式繞過目標APP的一些限制等,當然以上的這些危害都是需要一個前提條件:目標APP在越獄環境下能正常使用。

0x02 工具準備

越獄的iPhone或iPad(我這里演示的是IOS 8.4,低版本或高版本的可能會有些不同); IDA或者Hopper Disassembler; Xcode(主要使用其附帶的LLDB),當然對于低版本的IOS可以使用gdb,LLDB具體如何調試可以參考這里; OpenSSH,itools等其他工具。

檢測越獄的幾種方法(可參考《Hacking and Securing ios Application》):

(1)沙盒的完整性校驗一些越獄工具會移除沙盒的限制,使程序可以不受限制的運行,這里要說的是關于fork函數的限制。fork函數可以允許你的程序生成一個新的進程,如果沙盒被破壞或者程序在沙盒外運行,那么fork函數就會成功執行,如果沙盒沒有被篡改則fork函數執行失敗。這里我們通過fork()的返回值判斷子進程是否成功,程序代碼如下:

#!c 
#include <stdio.h> 
#include <stdlib.h> 
static inline int sandbox_integrity_compromised(void) __attribute__((always_inline)); 
int sandbox_integrity_compromised(void){ 
    int result = fork(); 
    if (!result) 
        exit(0); 
    if (result >= 0) 
        return 1; 
    return 0; 
} 
int main(int argc,char *argv[]){ 
    if(sandbox_integrity_compromised()) { 
        printf("Device is JailBroken\n"); 
    }else{ 
        printf("Device is not JailBroken\n"); 
    } 
    return 0; 
} 

(2)文件系統檢測

1)越獄文件是否存在越獄之后的小伙伴們一定對Cydia程序很熟悉吧,它是最受歡迎的第三方程序安裝器,大部分的越獄工具都會自動給設備進行安裝。所以我們可以檢測第三方程序文件是否存在來判斷設備是否越獄,例如檢測Cydia程序是否存在。

  1. /etc/fstab文件大小
    fstab文件通常被越獄工具替換使root分區有讀寫的權限,但是APP不允許查看該文件的內容,所以我們使用stat函數獲得此文件的大小,再根據文件的大小來判斷是否越獄。我這里顯示越獄后該文件的大小為67字節,有資料中說如果沒有越獄該文件的大小應該為80字節。


    使用itools查看fstab文件大小
  1. 符號鏈接檢測IOS的磁盤被劃分為兩個分區:容量較小的是系統分區,另一個是比較大的數據分區。IOS預裝的APP會安裝在系統分區下的/Applications文件夾下,但是系統分區在設備升級時會被覆蓋且容量太小,所以一些越獄工具會重定向這個目錄到一個大的用戶分區。通常情況下/Applications文件夾會被符號鏈接到/var/stash文件目錄下,APP可以通過lstat函數檢測/Applications的屬性,如若是目錄則代表未越獄,如若是符號鏈接則代表是越獄設備。


    使用itools查看符號鏈接
使用命令行查看符號鏈接

</br>

(3)檢測system( )函數的返回值等多種方法這個方法來源于一個網站,同時還介紹了其他幾種檢測越獄的方法。其中一個方法就是檢測system( )函數的返回值,調用System( )函數,不需要任何參數。在越獄設備上會返回1,在非越獄設備上會返回0。

0x04 可以動手了

我們使用文件檢測的第一種方法來動手測試一下我們的越獄設備。代碼如下:
#!c
#include <sys/stat.h>
#include <stdio.h>

int isJailBroken();
 
int main(){ 
  if(isJailBroken()){ 
    printf("Device is JailBroken\n"); 
    }else{ 
    printf("Device is not JailBroken\n"); 
  } 
  return 0; 
}
 
int isJailBroken(){ 
    struct stat buf; 
    int exist = 0; 
    char * jbFiles[] = {"/usr/sbin/sshd","/bin/bash","/Applications/Cydia.app","/private/var/lib/apt","/Libra ry/MobileSubstrate/MobileSubstrate.dylib"}; 
    for(int i=0;i < sizeof(jbFiles)/sizeof(char*);i++){ 
      exist = stat((jbFiles[i]),&buf); 
      if(exist == 0){ 
          return 1; 
      } 
    } 
    return 0; 
}

以上代碼主要檢測了/usr/sbin/sshd、/bin/bash、/Applications/Cydia.app、/private/var/lib/apt、/Library/MobileSubstrate/MobileSubstrate.dylib這幾個文件是否存在,如果存在則證明該設備已經越獄。

檢測越獄的POC

在Mac下使用以下命令編譯代碼:

clang -framework Foundation -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk ~/Desktop/jailbreak.c -o ~/Desktop/jailbreak64 -miphoneos-version-min=5.0

編譯之后將二進制文件拷貝到越獄設備當中并在命令行中運行它。下圖可看到運行程序后顯示設備已經越獄。


賦予文件權限并執行
0x05 道高一尺魔高一丈

一些APP正是使用了類似上面的方法來檢測自己所在設備是否進行了越獄,為了保證自身的安全性如果設備越獄則自動退出。是的這種做法很明智,但是這也只是增加了安全測試的難度,我們既然知道了檢測越獄的原理那么就可以見招拆招了。

(1) 配置好LLDB和debugserver,ssh連接到設備,在設備上使用命令行輸入debugserver -x backboard *:1234 /jailbreak64附加到jailbreak64,并開啟1234端口,等待LLDB的接入。


在iPad端使用debugserver附加到程序

(2) Mac端切換到~/Xcode.app/Contents/Developer/usr/bin/目錄下,輸入lldb,啟動后輸入process connect connect://iosip:1234

Mac端使用LLDB連接IOS設備

(3) 使用IDA分析程序邏輯結構,這里有一點需要注意:IDA分析的二進制文件必須與LLDB調試的二進制文件相同,這樣偏移前基地址、ASLR偏移、偏移后基地址才能對應得上。這里由于我的iPad對應的ARM是ARM64,所以之前在Mac上我使用arm64編譯的程序,將編譯的程序扔到IDA中分析,這里我使用的是IDAPro6.6中的idaq64.exe進行分析的。(這里所說的ASLR偏移指的是偏移后模塊基地址-偏移前模塊基地址,偏移后模塊基地址:二進制文件加載到內存模塊在內存中的首地址;偏移前模塊基地址:模塊在文件中的首地址)


ARM64編譯后的程序在IDA中的分析

根據上圖我們可以看到地址0x100007D1C下方有兩個分支,即左邊的判斷是設備已經越獄,而右邊的判斷是設備沒有越獄。

之所以說這里需要注意是因為我們在使用LLDB調試程序下斷點時需要偏移前基地址,而這個偏移前基地址不同場景下是不一樣的。下圖為ARMv7編譯后使用IDA分析的結果,可以看到我們關注的地址變為了0xBE28。


ARMv7編譯后的程序在IDA中的分析

(4) 使用LLDB查看ASLR偏移,此時需要知道幾個指令:ni執行下一條指令但不進入函數體,si執行下一條指令會進入函數體,image list列舉當前所以模塊。


使用LLDB查看ASLR偏移

上圖顯示jailbreak64還未啟動,現在調試還發生在dyld內部,接下來一直執行ni命令,直到輸出結果出現卡頓(大約3秒左右,可以明顯察覺到)。到這里不要再使用ni命令,dyld已經開始加載jailbreak64,我們使用image list -o -f查看jailbreak64的ASLR偏移。
多次執行ni命令后查看jailbreak64的偏移

根據上圖顯示jailbreak64的ASLR偏移為0x40000,所以可以確定斷點位置下在‘0x40000+0x100007D1C’處。

(5) 根據以上信息我們就可以安心的下斷點了,使用br s -a 地址用來下斷點,使用指令p可以打印某處的值,使用指令register write給指定的寄存器賦值。


下斷點

上圖是在‘0x40000+0x100007D1C’處設置斷點


查看x0的值并重新賦值來更改程序邏輯

我們使用p命令查看一下此時x0的值,發現x0為1,使用register write命令將該x0的值改為0,然后輸入命令c繼續,可以看到我們成功的繞過了越獄檢測,程序運行結果為設備未越獄。

在Cydia上可以安裝xCon軟件,據說是目前為止最強大的越獄檢測繞過工具,然而我安裝在我的iPad上并不適用,應該暫不支持IOS8.4吧。xCon可以繞過四種越獄檢測方法(根據特定的越獄文件及文件權限判斷設備是否越獄;根據沙盒完整性判斷設備是否越獄;根據文件系統的分區是否發生變化來檢查設備是否越獄;根據是否安裝ssh來判斷設備是否越獄)。用最近比較流行的一句話就是“你有一百種方式來檢測越獄”,而別人也有一百種方式來抵抗你的檢測。

0x06 魔高一尺道高一丈

剛才我們演示的是LLDB調試來繞過越獄檢測,那么如果我的程序阻止調試器附加怎么辦?我們來看下面的測試代碼。
#!c
#include <sys/stat.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <unistd.h>

inline int checkDebugger() __attribute__((always_inline)); 

int main(){ 
    if(checkDebugger()){ 
    printf("Debugger attached\n"); 
    return 0; 
    } 
    printf("Debug detection bypassed\n"); 
    return 0; 
} 

int checkDebugger() { 
    int name[4]; 
    struct kinfo_proc info; 
    size_t info_size = sizeof(info); 
    info.kp_proc.p_flag = 0; 
    name[0] = CTL_KERN; 
    name[1] = KERN_PROC; 
    name[2] = KERN_PROC_PID; 
    name[3] = getpid(); 
    if(sysctl(name,4,&info,&info_size,NULL,0) == -1){ 
       return 1; 
    } 
    return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0; 
}

當一個應用被調試的時候,會給進程設置一個標識(P_TRACED),可以檢測該進程是否有設置這個標識來檢測進程是否正在被調試來保護應用。如果該程序發現自己被調試器附加了進程,那么將會輸出Debugger attached,如果正常運行該程序則會輸出Debug detection bypassed。實驗結果如下:


正常運行的輸出結果

使用LLDB調試器附加后的輸出結果

在實際應用中可以在得知自己的程序被調試器附加后自動退出,當然這也是可以通過下斷點的方式繞過,但是可以通過多處調用該方法來增加攻擊的難度。同時上面的代碼采用聲明內聯函數的方法,使編譯器將函數功能插到每處代碼被調用的地方,而不至于某個特定的功能函數被劫持。

當然除了上述的這種反調試的方法,還可以通過優化標記、去除符號等方法使反匯編復雜化。

0x07 冤冤相報何時了

0x06中所說的反調試還可以通過打補丁的方式繞過,這樣就不需要每次都下斷點繞過,也會有更多的時間用LLDB來調試其他核心業務。是不是感覺有些亂?因為的確是攻擊和防御的方法都有很多,廠商采用多種防御手段來增加攻擊成本,而攻擊者為了利益或者抱有挑戰心理的態度來見招拆招,真是冤冤相報啊。

參考文獻:
[美]Jonathan Zdziarski《Hacking and Securing ios Application》 沙梓社,吳航《ios應用逆向工程》 http://blog.ioactive.com/2015/09/the-ios-get-out-of-jail-free-card.html

轉自iOS APP安全雜談之三

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容