 Hello everyone, another year to see you in its special mode. Welcome to our talk, Cauchu Reveal and Exploit IPC Logic Box inside Apple. First, a self-introduction. DuPontmore is a senior security researcher. He is a member of EcoSec Team at Tencent Security Sherm Lab. His research focuses on microS, iOS and Windows platform security. He has found and reported many vulnerabilities to Apple and Microsoft. He is a speaker of Black Hat Europe 2018 and Defconn28. Reven Sun is a co-author of this presentation. He is a senior security researcher of EcoSec Team. He focuses on microS and iOS platform security. Jonathan is also a co-author of this presentation. He leads the EcoSec Team and is a speaker of Black Hat Europe 2018, Defconn China 2018 and Defconn28. This is the agenda of our talk. First, an introduction to IPC and Logic Vulnerability. Then we talk about the IPC mechanisms on Apple platforms. After that, we will share some interesting logic vulnerabilities we found in preferences and App Store. Finally, we will give a conclusion on IPC logic vulnerabilities on Apple platform. Let's talk about some background knowledge. What is IPC? IPC means inter-process communication. It's a set of techniques provided by the operating system. It allows standalone processes to communicate with each other. The communication could be about the process notifying an under-process about some event or transferring of data from one process to another. The processes involved in IPC could have two roles, client and server. The client requests the server and server may respond to the client if needed. The web kernel provides the IPC channel to allow the client and server processes to send or reply to messages. There are three advantages that IPC provides. First, modularity. Modern software is more and more complex. With the help of IPC, developers could divide the complex system into separated modules. It reduced the complexity of software and avoids reinventing the wheel. Second, stability. If all data encodes are inside one process, a little error would probably crash the entire system. By dividing a complex system into separated modules, a module crash would not crash the entire system, which makes the system more stable. Third, privilege separation. With IPC, developers not just separate functionality, but also separate the privilege. Isolating sensitive operations into separated processes, keeping the processes the least privileged. Even if part of the system is hacked, it would not compromise the entire system and increase the security and protection from the attacks. Let's see an example of IPC usage. For web browser, it's nearly impossible to build a rendering engine that never crashes on hands. It's also nearly impossible to build a rendering engine that's perfectly secure. To solve this issue, modern browsers mostly use multi-process architecture. For example, it may divide the components into rendering process and networking process. The different processes of browsers would communicate with each other through IPC and often also needs to request the offering system system service. You may not feel it, but IPC is everywhere. The using of IPC divides the entire system into separated processes. Different processes have different privileges. Process may have low privilege or high privilege. Process may be sandboxed or non-sandboxed. There is a security boundary between them and IPC may break it. IPC is a bridge between different processes. So it is also a window between different privileges. IPC vulnerability is a key to high privilege. So it is one of the most valuable targets for privilege escalation. Logic vulnerability is different from memory corruption vulnerability. We do not want to play with memory corruption since they are boring to us. We like to find logic flaws. There are two kinds of logic flaws that is introduced during design phase and one is introduced during implementation phase. In fact, most of the time, abusing existing features are enough for us to compromise the entire system. Apple's new MacBook and iPad Pro have equipped with the new Apple M1 chip. The new chip brings many additional security features such as system integrity, data protection and pointer authentication code. The pointer authentication code or PAC is a hardware level security mechanism against memory bug which makes memory games much harder. Logic vulnerability is not playing with memory corruption and may not be affected. So it is the spring of logic vulnerability finally coming. Before introducing the IPC logic vulnerabilities, let's start with some fundamental knowledge of IPC on Apple platforms. Apple's IPC has different specific implementation methods. This includes shared files, shared memory and sockets which other systems support as well. There are also some that are unique to Apple such as marker messages, Apple events, distributed notifications and so on. However, the latest and most advanced IPC methods on Apple platform currently are XPC and NSXPC. Apple implements them on top of mark message. Next we will walk you through the principle and usages of them. Markport is an endpoint of unidirectional communication channel which is one of the fundamental primitives of XNU kernel for messaging. Messages can be sent or received from it. Users of markport never actually sees the port itself but accesses through a type of indirection called port writes. The sender of the message can send the message to markport through the send write. The receiver of the message can get the received message from the markport through the receive write. The message transmitted through the markport is called mark message. The system trap level API mark message and mark message overwrite can be used to send or receive mark messages. The structure contains a header, an optional complex data and message buffer. The header contains the sender and receiver of the message. The optional complex data part can transfer complex data such as file handle, shared memory and markport. Message buffer is used to send binary data. Mark message is low level and powerful, but it is also ancient and poorly documented. Developers need to construct entire structure to transmit data and handle different data type by themselves. It is difficult to use and Apple also does not recommend developers to use it directly. Instead, Apple provides high level IPC mechanisms that are easier to use. On top of mark messages, Apple built another communication mechanism called XPC. XPC is managed by LaunchD process. LaunchD is a naming server. The XPC server registers with LaunchD and declares that it will handle message sent to its endpoint. The client looks up an endpoint name via LaunchD and sends a message request. The server will receive the request, handle it and reply to the client if needed. Behind the scene, LaunchD starts and terminates the target server process on demand. The message sent through XPC is called XPC message, which is a more structural dictionary format. XPC users do not need to pay attention to the details of the underlying mark message processing. Through the XPC dictionary set APIs, we can easily construct an XPC dictionary message. Besides supporting the transmission of basic types of data such as typical string, integer and boolean, XPC message also supports complex data types such as file descriptor and shared memory. XPC message is serialized into mark message and transmitted to the other end of the IPC via the XNU kernel. The received mark messages is all serialized to XPC message and then can be used through the XPC dictionary get APIs. At API level, the XPC server calls XPC connection create mark service API with the XPC connection mark service listener flag set to register itself to LaunchD as an XPC service and then the client can connect to the XPC service through the same API. The message sending is completed by the XPC connection set message. After the process received the XPC message, the registered message handler via XPC connection set event handler will be called to process the message. The mainstream languages for app developers are the object-oriented objectives C and Swift. So Apple has encapsulated a layer of object-oriented implementation on top of XPC called NSXPC. NSXPC provides a set of remote procedural interface implementations instead of caring about the underlying message like XPC. After establishing the connection, the client can directly call the open interface of the NSXPC server across processes just like calling local methods. Apple provides many NSXPC classes for developers. The server registers the service with LaunchD through the NSXPC listener and specifies a NSXPC listener delegate to handle the connection request. The connection between a client and server is managed by NSXPC connection. What method can a client call in a server? The NSXPC server defines this through the Objective C protocol which defines programmatic interface between the calling application and service. An interface's definitions, common arithmetic types and basic types such as strings and arrays are directly supported as parameters. The interface also supports custom defined class objects to be passed as parameters. The custom object needs to implement the NS secure coding protocol. Here is an example. Here's an architectural diagram from Apple. The server registers the service through NSXPC listener. The app establishes a connection with the service through the NSXPC connection. In this process, both parties can directly call remote methods across process boundary without caring about the underlying implementation details. We will now share some interesting logic vulnerabilities we found and exploited on Apple platforms. We found and reported three logic bugs in preferences components. With these vulnerabilities, a local user may be able to modify protected parts of the file system. What are preferences? Preferences are user-defined settings. They are persistent data stored in preferences file. Its format is property list, also called plist. The service, CFCrefsD, has the responsibility to manage preferences. Each from or right to preferences file according to client requests. Apple provides two kinds of high-level preferences API. Using the foundation APIs, apps could use the NS user-defaults class to access its preferences. Each app has a single instance of this class, accessible from the standard user-defaults class method. Through the shared user-defaults object, apps could get and set individual preferences values. Apps can also use many of the underlying core foundation APIs. For example, CFCrefsD setAppValue and CFCrefsCopyAppValue could be used to get or set the preferences data. After reverse engineering CFCrefsD, we found it creates the XPC service named com.apple.cfrefsd.demon, which runs with root privilege and without sandbox. When a client requests CFCrefsD, even handler will be scheduled to handle the request. As a foundational service, preferences could be accessed from almost everywhere. Even the most restricted process needs to access preferences. Here is a sandbox profile for Safari web process. It allows the sandbox process to access com.apple.cfrefsd.demon. Because CFCrefsD is an XPC service, we could also request it by sending XPC message directly. It is a low-level method that could control the operation more precisely. Here is a sample request. First, we use XPCConnectionCreateMarkService to create a service connection. Then we create the XPC dictionary and set the key value that is used to control the operation. Finally, we send the XPC message through the XPCConnectionSetMessage API. Where does CFCrefsD save the preferences data? The preferences file path is constructed from multiple components. Part of it is the fixed value in CFCrefsD. Parts of it come from the client through XPC message. First, let's see the preferences directory where the preferences file is stored in. There are some predefined locations to store the file. PreferencesDomain is the value that is passed through the CFCrefsDomain key. By default, the file path is composed of preferences directory, preferences domain, and the service string.plist. How is the file path constructed? There are two main components. First, format the file path with CFStringCreateWisFormat, which connect the preferences domain with .plist, then using CFURLCreateWisFileSystemPathRelativeToBase to generate the full path. The base URL is the preferences directory, and the file path is the path returned above. Logic vulnerabilities always combine some features, and we found that CFURLCreateWisFileSystemPathRelativeToBase have two features that may be abused. First, this function has pass traversal feature. If file path contains ..slash, the returned file path could traverse to any path we want. The second feature is that if the file path is an absolute path, it will return the file path no matter what the base URL is. We could use the pass traversal feature or absolute path feature to control the preferences file path and return any file path we want. What if the controllable file path does not exist on the file system? The function named cacheActualPathCreatingIfNecessary is very interesting. Seems you will create the file path if it does not exist. First, it will try to open a file. If the file path exists, it will return the path. But if the file path does not exist, it will get the directory part of the file path, and it will create the directory. After creating the directory, it tries to open the file path again, and then return the file path. The CFPrefCreatePreferences directory is the function used to create the non-existed directories. It first split the path into multiple sub-items by slash, and then creates them recursively with make directory add. After the directory is created, its ownership will be modified through a file change owner. But where does the user ID and group ID came from? The owner of the newly created directories is determined by several factors. By default, the owner is the identity of the XPC client, and CFPrefsD gets the identity of the client user through XPCConnectionGetEffectiveUserID and XPCConnectionGetEffectiveGroupID. However, the XPC client can also specify the expected user. Therefore, the ownership of the newly created directory is also under our control. We can let CFPrefsD help us create any directory with controlled owner. There are many ways to convert arbitrary directory creation to code execution with root privilege. Here we share a method mentioned by Shubhub Fizzle through periodic script. Periodic script is a mechanism to schedule script execution. The daily directory does not exist by default, but the operating system will periodically scan and execute the files in it. By exploiting the vulnerability, we can create the daily directory and set the owner of the directory to the current user. And then we can write any script to the daily directory. Wait a day, and the script will execute with root privilege. The patch for the vulnerability is simple and straightforward. It can be explained even by looking at the function name. The cache actual path creating if necessary function has been replaced with cache file info for writing. There is no creating anymore. In fact, CFPrefsD will no longer help the user create the non-existing prefaces directory. If client passing a directory which does not exist, CFPrefsD will ask the client to create it. How does CFPrefsD read the prefaces data? When the client wants to get the data, the client needs to request CFPrefsD. By default, you will read the data and return them in the reply directly. But if the size of the file is too large to fit in a XPC message, you will just return the file descriptor. To avoid subsequent changes to the file, you will clone a copy before you return the file descriptor. This function is the implementation of CFPrefsD to process large prefaces file. First, it gets the file path, then it determines whether the file size exceeds one megabyte. Then it generates a random temporal file path according to the rules and then clone the original file to a temporary file. Finally, open the temporary file and return its file descriptor. It calls clone file to clone the file to a temporary file with a random name. The temporary file and the original file are in the same directory. The path of the temporary file is generated by make temp according to a rule. The rule is that the file path is spliced with .cfp and 7 random characters. So the final temporary file name is random. But can it guaranteed to be random? The make temp will replace the 7x at the end of the rule with random characters. The space for random characters is very large. Is there any other way to make make temp generate a faked file name? One key point is that snprintf specifies the maximum length of 0x400 when generating rules. If plist path is very long, what happens if the splicing rule exceeds 0x400? snprintf will overflow and the characters exceeding 0x400 will not be written into the written buffer. To be more precise, if the length of the file path plus .cfp string is exactly 0x400-1, the rule generated by snprintf will not contain x at the end. And make temp will generate a fixed temporary file path. The full clone file is called there is a file check. In exploit code, we can first pass in a normal file path. This will ensure that .cfprefst can successfully pass this check and enter the subsequent clone file process. After the file check and before the clone file, there is a window for its condition. If we replace the controllable file with the symbolic link during this time, .cfprefst will call clone file to help us copy arbitrary file to the temporary file path we control. We got a fixed temporary file name, but can we link it to enter the place ahead of make temp function? No. This file name is guaranteed not to exist at the time of function invocation. But after the make temp return successfully and before the clone file, this is also a window for its condition. We could replace the temporary file with a symbolic link. In this way, we can achieve arbitrary file write. In the patch, Apple added an overflow checking and the subsequent clone file will not be executed if overflow occurs. The previous method of forcing fixed file name no longer works. How does .cfprefst write preferences data? When .cfprefst saves preferences data for the client, first a extract key and value from xpc message, then it reads the original data from the target file and generates new data based on the incoming key and value data, writes to the new data to the temporary file and then you rename the temporary file back to the target file. When saving data, .cfprefst verifies the client has write permission to target file. The client needs to pass in a file descriptor with write permission. After the previous check, .cfprefst would generate a temporary file and write data to it. Then we'll rename this temporary file back to the target file. Can a normal user replace the source file of the rename with a symbolic link? No, this temporary file has a root-owned directory and normal user does not have permission to write to this directory. Can a normal user replace the rename target with a symbolic link? Normal user has write permission for the target but the target file will be deleted first when the rename is called. So even if it can be replaced with a symbolic link it will not work. Regarding this feature, the rename API documentation says if the final component of the target is a symbolic link, the symbolic link is renamed not the file or directory to a tree points. But wait, the document says that if the final component of the target is a symbolic link rename will delete it first. What if it's not the final component? What if the middle component of PListPath is a symbolic link? Suppose the path of preferences is slash temp slash test slash hello.plist If we replace the directory with a symbolic link pointing to the launchDemons directory then the hello.plist is used as the target path of rename. What will happen? The symbolic link will be followed and the temporary file will be moved to launchDemons directory. Before renaming the temporary file, it verifies the caller has right permission. However, after the file check and before the rename function, there is a time window to replace the middle component of file path with symbolic link. When renaming files it will move the temporary file with controllable content to arbitrary path. The patch for the rename vulnerability is very simple. Rename is replaced with rename.at. Rename.at locates the target file according to the directory descriptor to ensure that the final move target must be in a certain directory. So even if we replace the directory with a symbolic link it will not work anymore. Here is a demo. We use the vulnerabilities to achieve arbitrary file read and write and we could gain root privileges on macOS and read privacy data on iOS. First, we use prefixes vulnerabilities to achieve root privilege. We could see the system integrity protection is enabled the current user is a normal user, not root. We could get the root privileges very quickly. Next, we use prefixes probabilities to read privacy data on iOS. You could see our demo application is not allowed to access photos and contacts. However, when we take a photo, then open the demo application or demo application can steal the photo and send it to our server. Last year, we found and reported an interesting logic vulnerabilities in macOS app store component. An application could abuse it to gain elevated privilege. The vulnerability exists in NSXPC server com.apple.storedownloadd.demon. The server is implemented in store downloadd application which runs with root privilege. Fortunately, this process is running in sandbox. But unfortunately, as an app download service, it must have the ability to write to some sensitive locations. For example, it is allowed to write to applications and keychains directory. As an NSXPC server, it provides many interfaces for client. Here we listed the set store client and perform download interface. The perform download interface is very interesting. This interface performs downloading jobs for client. But what can it download and what is the SS download parameter? For NSXPC, the objective CU object that implements NS secure coding protocol could be used as parameters. SS download is such an object. This object only has one property, assets. And it is an array. The element in the assets array is SS download asset object. It is also an objective C object and it has three properties. The URL, download path dashes. The SS download asset object is passed from client to server. The properties of SS download asset will be serialized in client, the function encode with coder will be called to serialize the specified properties to XPC message. At the server side, the XPC message would be on serialized with any twist coder and construct the SS download asset object with specified properties. The properties of the object would be fully controllable. How does stored download D perform downloading tasks? You will perform download tasks according to the URL. Then it verifies the response contents based on the hashes. Finally, you write the contents to the specified download path. There is a hash verification in the download logic. This function will calculate the hash of the response contents and then compare it with the hashes the client specified. But it's not a security check just a data integrity verification. Because we can control the download URL, the content hash and download path we could write arbitrary file path with D stored download D's privilege. Attackers could say hi still download D please help me download the file from this URL. Its hash is this and then write the contents to this download path. Thanks. Then the stored download D would do all the jobs well for the attacker. How did Apple fix this vulnerability? The issue was adjusted by removing the vulnerable code they said. To be more precise they completely removed the service. There is no com.apple. the stored download D.demon anymore. Here is the demo of App Store vulnerability. We just downloaded the test.keychain intent directory to keychains directory with the help of stored download D. There are other interesting logic vulnerabilities we found and detailed in our blog. An XPC service implementation flaw which is a logic bug inside launch D for managing the XPC service. An NSXPC vulnerability in Adobe Acrobat reader for macOS. There are many security mechanisms on Apple platform that tries to make vulnerabilities harder to exploit. Like DEP, ASLR, PAC and so on. But you probably noticed that logic bugs are not affected by these security features. That's just awesome. Logic vulnerabilities has many advantages. Though it is hard to find it is easy to exploit. The exploitation is always stable. The logic vulnerabilities exist across platforms often so one exploit could roll them all. Logic bugs in core frameworks like preferences let us rule all Apple platforms Intel and Apple Silicon alike without changing one line of our exploit. Apple is also working hard to try to reduce the IPC attack services. For example, by adding more restrictive sandbox rules it reduced the IPC services accessible to applications. They keep deleting the unnecessary high privilege services and they are adding more and more private entitlements to make many high privilege services only accessible to Apple applications. It is hard to make sure everything is perfectly secure so Apple is also trying to limit the damage. For example they are putting IPC services in sandboxes and give them the least privilege. They are also using rootlist to limit the root privilege. In this presentation we talked about the latest IPC mechanism on Apple platforms XPC and NSXPC We walked you through some of the interesting IPC logic vulnerabilities three logic vulnerabilities in preferences and one in App Store. We detailed the design logic and implementation of these components the flaws inside them and how we exploit them to elevate privilege. And we also talked about the advantage of IPC logic vulnerability and the state of Apple IPC security. Logic bugs are always found too harmful we think you will love it just as we do. We would like to thank Shaba Spizzle, Ian Beane and Jujo for their previous work and shares.