macOS Stash Proxy Unauthorized Manipulation of System Network Preferences via PID Reuse Attack

Jan 22, 2025

CVE Number

CVE-2025-4961

Credits

Carlos Garrido of Pentraze Cybersecurity

Description

A new security issue was identified in a privileged XPC service following recent changes intended to fix a previously reported vulnerability (CVE-2024-7457). While the original issue was addressed, the updated implementation still allows unauthorized processes to establish a valid XPC connection under specific timing conditions.

The service relies on process-related information for client validation, which is not suitable for security decisions. As a result, untrusted clients may interact with the service as if they were legitimate, leading to unintended privileged behavior.

Vulnerability - PID Reuse Attack

The vulnerability is caused by relying on the process identifier (PID) to authenticate XPC clients. Since PIDs can be reused, an attacker can exploit a race condition to send malicious XPC messages and then spawn a trusted process that assumes the same PID.

If the service validates the client after this transition, it may incorrectly treat the malicious messages as originating from a trusted process. This allows unauthorized clients to perform privileged actions, including modifying system network settings.

listener:shouldAcceptNewConnection:

When reviewing the listener:shouldAcceptNewConnection: function, the connectionIsValid: function is invoked. At this point, it becomes evident that connection validity is determined based on the process identifier (PID) rather than the auditToken. This design choice introduces a PID Reuse Attack vulnerability.

This race condition is possible due to the following behavior: when invoking posix_spawn to create a new process, the POSIX_SPAWN_SETEXEC flag is used, causing posix_spawn to effectively behave like an execv call and reuse the existing process identifier (PID). In addition, the POSIX_SPAWN_START_SUSPENDED flag is specified to ensure the process is created in a suspended state.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

char -[ProxyHelper listener:shouldAcceptNewConnection:](struct ProxyHelper* self, SEL sel, id listener, 
id shouldAcceptNewConnection)

    id obj_2 = _objc_retain(obj: listener)
    id rax_1 = _objc_retain(obj: shouldAcceptNewConnection)
    char result
        
     if (-[ProxyHelper connectionIsVaild:](self, sel: "connectionIsVaild:", 
            connectionIsVaild: rax_1) == 0)
    result = 0
    .
    .
    <SNIP>
    .
    .

connectionIsValid:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14

char -[ProxyHelper connectionIsVaild:](struct ProxyHelper* self, SEL sel, id connectionIsVaild)

    int64_t rax = *___stack_chk_guard
    id obj = _objc_retainAutoreleasedReturnValue(obj: _objc_msgSend(
    self: _OBJC_CLASS_$_NSRunningApplication, 
    cmd: "runningApplicationWithProcessIde…", 
    zx.q(_objc_msgSend(self: connectionIsVaild, cmd: "processIdentifier"))))
    char result
    .
    .
    <SNIP>
    .
    .

Remediation

  1. Terminate the connection upon receiving an unrecognized message.

  2. Perform authorization checks before accepting a connection whenever possible.

    • If authorization checks are not feasible at that stage, use xpc_dictionary_get_audit_token.

    • Alternatively, save the audit token during the accept handler for later use (this method is also effective for NSXPCConnection).

  3. Leverage new APIs for automatic code signing verification before accepting a connection:

    • [NSXPCConnection setCodeSigningRequirement:] (available since macOS 13.0)

    • xpc_connection_set_peer_code_signing_requirement (available since macOS 12.0)

¿Ver el sitio en español?