0x00 Sequence Ice refers to user state, and fire refers to kernel state. How to break through the user state sandbox like a refrigerator and finally reach and control the kernel burning like a flame is what this series of articles "iOS A Song of Ice and Fire" will tell you. This time, we will introduce how to use XPC to break through the app sandbox and control the pc (program counter) of other processes to execute system instructions. The catalog of the iOS A Song of Ice and Fire series is as follows:
0x01 What is XPC There are many IPC (inter-process communication) methods on iOS. The simplest and most common IPC is URL Schemes, which is a mechanism for apps to call each other and send simple characters. For example, I can use the [[UIApplication sharedApplication] openURL:url] api with " alipay:// ", " wechat:// " and other URLs to call Alipay or WeChat. The XPC we are going to talk about today is a little more complicated than URLScheme. XPC is also a type of iOS IPC. Through XPC, apps can communicate with some system services, and these system services are generally outside the sandbox. If we can control these services through IPC, we can successfully escape the sandbox. There are about 30 or 40 services that apps can access through XPC in the sandbox, which is still a lot. To communicate with these XPC services, we need to create an XPC client. The content transmitted must correspond to the content received by the XPC service. For example, a system service may open an XPC service like this:
If we have access to the sandbox, we can connect by creating an XPC client:
After running the above program, the server can receive messages from the client. We know that what xpc transmits is actually a piece of binary data. For example, the xpc_dictionary we transmit is like this: The actual data transmitted is indeed like this (through lldb, then
You can see it): It can be seen that all the transmitted data are converted into binary data through serialization, and then after the data is passed to the server of the system service, it is restored back to the original data through the deserialization function. We know that after normal installation, the app has mobile permissions, but is restricted to a small space by the sandbox. If a system service has problems when receiving XPC messages, such as an Object Dereference vulnerability, it is possible for the client to control the server's PC register and use ROP to execute arbitrary instructions. Although most system services also have mobile permissions, most system services are not sandboxed, so they can have permissions to read or modify most files or execute some APIs that can access the kernel, triggering a panic. Com.apple.networkd is an xpc system service accessible from the app sandbox. The binary corresponding to this service is /usr/libexec/networkd. We can see through ps that the permission of this service is _networkd: Although there is no root privilege, it is almost possible to read and write any file outside the sandbox. In iOS 8.1.3 and earlier versions, this XPC system service has an Object Dereference vulnerability, which was discovered by IanBeer of Google Project Zero, but the POC he provided was only on Mac OS X, and many addresses were hardcoded. This article will use iPhone 4s, arm32, 7.1.1 as a test machine, and explain step by step how to find these hardcoded addresses and gadgets, and use this vulnerability to escape the app's sandbox. The problem lies in the char *__fastcall sub_A878(int a1) function of the com.apple.networkd service. The passed "effective_audit_token" value is not type-checked and is directly parsed as xpc_data: However, if the value we pass is not an xpc_data, networkd will treat it as an xpc_data and pass it to _xpc_data_get_bytes_ptr() for parsing: After parsing, the program will call _dispatch_objc_release() to release the object, regardless of whether the object meets the service program's expectations. Therefore, we thought about whether we could forge an objective-C object and add the release() function of this object to the cache. In this way, when the program releases the object, we can control the pc pointer to point to the ROP instruction we want to execute. Yes, this idea is feasible. The first thing we need to do is to construct the corresponding xpc data according to the data transmission protocol (obtained by decompiling networkd):
We can then use NSLog(@"%@",dict); to print out the xpc data we have constructed: All data except effective_audit_token are normal. In order to attack this system service, we set the value of effective_audit_token to {0x0, 0x1fec000}; using xpc_dictionary_set_uuid. The address 0x1fec000 will store our fake Objective-C object. After constructing the xpc data, we can send the data to the networkd server to trigger the vulnerability. But how to construct a fake ObjectC object and how to save the fake object to this address? Please continue to the next chapter. First, we need to control the pc pointer by forging a fake Objective-C object and constructing a fake cache. We have already introduced this technology in "iOS Song of Ice and Fire - Objective-C Pwn and iOS arm64 ROP". Here is a brief idea: The first step is to find the address of the selector in memory. This problem can be solved using the system-provided API NSSelectorFromString(). For example, if we need the address of the selector "release", we can use NSSelectorFromString(@"release") to get it. In the second step, we need to build a fake receiver. The fake receiver has a pointer to a fake objc_class, and the fake objc_class stores the fake cache_buckets pointer and mask. The fake cache_buckets pointer eventually points to the address of the selector and selector function we are going to forge. This forged function address is the starting address of the ROP chain we are going to execute. The final code is as follows:
Since we have controlled the PC of the xpc service through fake Objective-C objects, we can do something outside the sandbox. But because of DEP, if we don't patch the kernel, we can't execute any shellcode. So we need to use ROP to achieve our goal. Although the program image, library, heap and stack are all random, the good news is that the address of the shared cache dyld_shared_cache is fixed after booting, and the dyld_shared_cache of each process is the same. This dyld_shared_cache is several hundred MB in size, which can basically meet our needs for gadgets. Therefore, we can calculate the location of the target process gadgets by simply obtaining the base address of dyld_shared_cache in our own process. The dyld_shared_cache file is usually saved in the directory /System/Library/Caches/com.apple.dyld/. After we download it, we can use jtool to extract the dylib inside. For example, if we want to extract the CoreFoundation framework, we can use: jtool -extract CoreFoundation ./dyld_shared_cache_armv7 Then you can use the ROPgadget tool to search for gadgets. If it is arm32-bit, remember to add thumb mode, otherwise it will search in arm mode by default, and there will be fewer gadgets: ROPgadget --binary ./dyld_shared_cache_armv7.CoreFoundation --rawArch=arm --rawMode=thumb Next, we need to find a gadget for stack pivot, because we only control a limited number of registers at the beginning, and the address pointed to by the stack pointer is not under our control. If we want to control more registers and continue to control the PC, we need to use the stack pivot gadget to point the stack pointer to a memory address that we can control, and then use the pop instruction to control more registers and PC. Another point to note is that if we want to use thumb instructions, we need to give the jump address 1, because the arm CPU uses the lowest bit to determine whether it is a thumb instruction or an arm instruction. The stack pivot gadgets we found on the iPhone 4s 7.1.2 are as follows:
Because stack pivot requires control of register r4, but we can only control r0 at the beginning, we first find a gadget to assign the value of r0 to r4, and then call the stack pivot gadget:
After stack pivoting, we have control over the stack and other registers. We can then call the desired function, for example, using the system command to execute "touch /tmp/iceandfire". Of course, we also need to find the corresponding gadget and put the corresponding register value at the correct address on the stack:
Finally, our fake Objective-C structure is constructed as follows:
0x04 Heap Spray Although we can use a fake Objective-C object to control networkd. But we need to save this object in the memory space of networkd, and because of ASLR (address randomization), even if we can transfer the fake object, it is difficult to calculate the specific location of the object in the memory. So what should we do? The method is heap spray. Although ASLR means that each time a service is started, the program image, library, heap and stack are all random. But in fact, this randomness is not completely random, but only random within a certain address range. Therefore, we can use heap spray to spray a part of the space in the memory (as large as possible, in order to cover the range of random addresses), and then fill it with n fake objects. The environment I tested the vulnerability in was iPhone4s (arm 32-bit) 7.1.2. We chose the address 0x1fec000 because after multiple heap spray tests, this address can achieve a nearly 100% hit rate. The heap spray code is as follows:
Then we compile and execute our app. The app will fill the fake ObjectiveC object into the networkd memory by heap spraying. Then the app will trigger the object dereference vulnerability to control the PC. Then the app will use rop to execute the system("touch /tmp/iceandfire") command. After running the app, we found that the iceandfire file has appeared in the /tmp/ directory, indicating that we have successfully broken through the sandbox and executed the system command: 0x05 Summary In this article, we introduce how to use XPC to break through the sandbox, perform heap spray, control the PC of the system service, and use ROP to perform stack pivot, and then execute system instructions. After breaking through the sandbox, although pirated apps cannot be installed, an app can add, delete, modify and check other apps' files and data at will, which is like root on Android. Although this vulnerability has been fixed in 8.1.3, it does not mean that similar vulnerabilities will not appear in the future. For example, the iOS 9.3 0day we found can easily break through the latest version of iOS sandbox to obtain files of other apps. |
<<: From variable declaration in C language to block syntax in Objective-C
>>: How to use Xcode's Targets to manage development and production builds
This article introduces 16 optimization strategie...
The US presidential election finally came to an e...
The Mid-Autumn Festival holiday is coming soon, f...
"Introduction": This is a long article....
In the hot summer, eating a piece of icy cold wat...
Make friends with e-commerce academy. Get started...
Today, Tesla launched the basic version of Model ...
Recently, many of my friends in the product field...
On November 21, the used car online auction platfo...
The Internet Architect 5.0 course of Kegongchang ...
On April 23, World Book Day, Alibaba became a dis...
Antarctica is a cold and mysterious continent, co...
□ Popular Science Times reporter Chen Jie and Hu ...
Generally, laptops equipped with Google's own ...
In 2019, the concept of private domain was propos...