macOS XPC
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for communication between processes on macOS and iOS. XPC provides a mechanism for making safe, asynchronous method calls between different processes on the system. It's a part of Apple's security paradigm, allowing for the creation of privilege-separated applications where each component runs with only the permissions it needs to do its job, thereby limiting the potential damage from a compromised process.
XPC uses a form of Inter-Process Communication (IPC), which is a set of methods for different programs running on the same system to send data back and forth.
The primary benefits of XPC include:
Security: By separating work into different processes, each process can be granted only the permissions it needs. This means that even if a process is compromised, it has limited ability to do harm.
Stability: XPC helps isolate crashes to the component where they occur. If a process crashes, it can be restarted without affecting the rest of the system.
Performance: XPC allows for easy concurrency, as different tasks can be run simultaneously in different processes.
The only drawback is that separating an application in several processes making them communicate via XPC is less efficient. But in todays systems this isn't almost noticeable and the benefits are better.
The XPC components of an application are inside the application itself. For example, in Safari you can find them in /Applications/Safari.app/Contents/XPCServices
. They have extension .xpc
(like com.apple.Safari.SandboxBroker.xpc
) and are also bundles with the main binary inside of it: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker
and an Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist
As you might be thinking a XPC component will have different entitlements and privileges than the other XPC components or the main app binary. EXCEPT if a XPC service is configured with JoinExistingSession set to “True” in its Info.plist file. In this case, the XPC service will run in the same security session as the application that called it.
XPC services are started by launchd when required and shut down once all tasks are complete to free system resources. Application-specific XPC components can only be utilized by the application, thereby reducing the risk associated with potential vulnerabilities.
System-wide XPC services are accessible to all users. These services, either launchd or Mach-type, need to be defined in plist files located in specified directories such as /System/Library/LaunchDaemons
, /Library/LaunchDaemons
, /System/Library/LaunchAgents
, or /Library/LaunchAgents
.
These plists files will have a key called MachServices
with the name of the service, and a key called Program
with the path to the binary:
The ones in LaunchDameons
are run by root. So if an unprivileged process can talk with one of these it could be able to escalate privileges.
xpc_object_t
Every XPC message is a dictionary object that simplifies the serialization and deserialization. Moreover, libxpc.dylib
declares most of the data types so it's possible to make that the received data is of the expected type. In the C API every object is a xpc_object_t
(and it's type can be checked using xpc_get_type(object)
).
Moreover, the function xpc_copy_description(object)
can be used to get a string representation of the object that can be useful for debugging purposes.
These objects also have some methods to call like xpc_<object>_copy
, xpc_<object>_equal
, xpc_<object>_hash
, xpc_<object>_serialize
, xpc_<object>_deserialize
...
The xpc_object_t
are created calling xpc_<objetType>_create
function, which internally calls _xpc_base_create(Class, Size)
where it's indicated the type of the class of the object (one of XPC_TYPE_*
) and the size of it (some extra 40B will be added to the size for metadata). Which means that the data of the object will start at the offset 40B.
Therefore, the xpc_<objectType>_t
is kind of a subclass of the xpc_object_t
which would be a subclass of os_object_t*
.
Note that it should be the developer who uses xpc_dictionary_[get/set]_<objectType>
to get or set the type and real value of a key.
xpc_pipe
A xpc_pipe
is a FIFO pipe that processes can use to communicate (the communication use Mach messages).
It's possible to create a XPC server calling xpc_pipe_create()
or xpc_pipe_create_from_port()
to create it using a specific Mach port. Then, to receive messages it's possible to call xpc_pipe_receive
and xpc_pipe_try_receive
.
Note that the xpc_pipe
object is a xpc_object_t
with information in its struct about the two Mach ports used and the name (if any). The name, for example, the daemon secinitd
in its plist /System/Library/LaunchDaemons/com.apple.secinitd.plist
configures the pipe called com.apple.secinitd
.
An example of a xpc_pipe
is the bootstrap pipe created by launchd
making possible sharing Mach ports.
NSXPC*
These are Objective-C high level objects which allows the abstraction of XPC connections. Moreover, it's easier to debug these objects with DTrace than the previous ones.
GCD Queues
XPC uses GCD to pass messages, moreover it generates certain dispatch queues like xpc.transactionq
, xpc.io
, xpc-events.add-listenerq
, xpc.service-instance
...
These are bundles with .xpc
extension located inside the XPCServices
folder of other projects and in the Info.plist
they have the CFBundlePackageType
set to XPC!
.
This file have other configuration keys like ServiceType
which can be Application, User, System or _SandboxProfile
which can define a sandbox or _AllowedClients
which might indicate entitlements or ID required to contact the ser. these and other configuration options will be useful to configure the service when being launched.
The app attempts to connect to a XPC service using xpc_connection_create_mach_service
, then launchd locates the daemon and starts xpcproxy
. xpcproxy
enforce configured restrictions and. spawns the service with the provided FDs and Mach ports.
In order to improve the speed of the search of the XPC service, a cache is used.
It's possible to trace the actions of xpcproxy
using:
The XPC library use kdebug
to log actions calling xpc_ktrace_pid0
and xpc_ktrace_pid1
. The codes it uses are undocumented so it's needed to add the into /usr/share/misc/trace.codes
. They have the prefix 0x29
and for example one is 0x29000004
: XPC_serializer_pack
.
The utility xpcproxy
uses the prefix 0x22
, for example: 0x2200001c: xpcproxy:will_do_preexec
.
Applications can subscribe to different event messages, enabling them to be initiated on-demand when such events happen. The setup for these services is done in launchd plist files, located in the same directories as the previous ones and containing an extra LaunchEvent
key.
When a process tries to call a method from via an XPC connection, the XPC service should check if that process is allowed to connect. Here are the common ways to check that and the common pitfalls:
macOS XPC Connecting Process CheckApple also allows apps to configure some rights and how to get them so if the calling process have them it would be allowed to call a method from the XPC service:
macOS XPC AuthorizationTo sniff the XPC messages you could use xpcspy which uses Frida.
Another possible tool to use is XPoCe2.
This functionality provided by RemoteXPC.framework
(from libxpc
) allows to communicate via XPC through different hosts.
The services that supports remote XPC will have in their plist the key UsesRemoteXPC like it's the case of /System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plist
. However, although the service will be registered with launchd
, it's UserEventAgent
with the plugins com.apple.remoted.plugin
and com.apple.remoteservicediscovery.events.plugin
which provides the functionality.
Moreover, the RemoteServiceDiscovery.framework
allows to get info from the com.apple.remoted.plugin
exposing functions such as get_device
, get_unique_device
, connect
...
Once connect is used and the socket fd
of the service is gathered, it's possible to use remote_xpc_connection_*
class.
It's possible to get information about remote services using the cli tool /usr/libexec/remotectl
using parameters as:
The communication between BridgeOS and the host occurs through a dedicated IPv6 interface. The MultiverseSupport.framework
allows to establish sockets whose fd
will be used for communicating.
It's possible to find thee communications using netstat
, nettop
or the open source option, netbottom
.
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)