hacktricks/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md

6.7 KiB
Raw Blame History

macOS XPC Connecting Process Check

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

XPC Connecting Process Check

When a connection is stablished to an XPC service, the server will check if the connection is allowed. These are the checks it would usually perform:

  1. Check if the connecting process is signed with an Apple-signed certificate (only given out by Apple).
    • If this isn't verified, an attacker could can create a fake certificate to match any other check.
  2. Check if the connecting process is signed with the organizations certificate, (team ID verification).
    • If this isn't verified, any developer certificate from Apple can be used for signing, and connect to the service.
  3. Check if the connecting process contains a proper bundle ID.
  4. Check if the connecting process has a proper software version number.
    • If this isn't verified, an old, insecure clients, vulnerable to process injection could be used to connect to the XPC service even with the other checks in place.
  5. Check if the connecting process has an entitlement that allows it to connect to the service. This is applicable for Apple binaries.
  6. The verification must be based on the connecting clients audit token instead of its process ID (PID) since the former prevents PID reuse attacks.
    • Developers rarely use the audit token API call since its private, so Apple could change at any time. Additionally, private API usage is not allowed in Mac App Store apps.

For more information about the PID reuse attack check:

{% content-ref url="macos-pid-reuse.md" %} macos-pid-reuse.md {% endcontent-ref %}

Code Examples

The server will implement this verification in a function called shouldAcceptNewConnection.

{% code overflow="wrap" %}

- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
    //Check connection
    return YES;
}

{% endcode %}

The object NSXPCConnection has a private property auditToken (the one that should be used but could change) and a the public property processIdentifier (the one that shouldn't be used).

The connecting process could be verified with something like:

{% code overflow="wrap" %}

[...]
SecRequirementRef requirementRef = NULL;
NSString requirementString = @"anchor apple generic and identifier \"xyz.hacktricks.service\" and certificate leaf [subject.CN] = \"TEAMID\" and info [CFBundleShortVersionString] >= \"1.0\"";
/* Check:
- Signed by a cert signed by Apple
- Check the bundle ID
- Check the TEAMID of the signing cert
- Check the version used
*/

// Check the requirements
SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &requirementRef);
SecCodeCheckValidity(code, kSecCSDefaultFlags, requirementRef);

{% endcode %}

If a developer doesn't want to check the version of the client, he could check that the client is not vulnerable to process injection at least:

{% code overflow="wrap" %}

[...]
CFDictionaryRef csInfo = NULL;
SecCodeCopySigningInformation(code, kSecCSDynamicInformation, &csInfo);
uint32_t csFlags = [((__bridge NSDictionary *)csInfo)[(__bridge NSString *)kSecCodeInfoStatus] intValue];
const uint32_t cs_hard = 0x100;        // don't load invalid page. 
const uint32_t cs_kill = 0x200;        // Kill process if page is invalid
const uint32_t cs_restrict = 0x800;    // Prevent debugging
const uint32_t cs_require_lv = 0x2000; // Library Validation
const uint32_t cs_runtime = 0x10000;   // hardened runtime
if ((csFlags & (cs_hard | cs_require_lv)) {
    return Yes; // Accept connection
}

{% endcode %}

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥