The staging window

On com.apple.amfi.nsxpc, six methods are gated by verifyBoolEntitlement — except getStagedProfileWithReply. An architectural curiosity with a narrow exploitation window. AMFID-02 disclosed to Apple.


The pattern is small. Six methods on an XPC interface check an entitlement before they do anything. The seventh doesn’t. The seventh only returns useful data during about ninety seconds while an MDM profile is being staged — and ninety-second windows are genuinely hard to manufacture in a controlled test. So this took three weeks of checking before I was prepared to write it up.

I had static evidence, a DTrace probe showing dispatch, an XPC reachability test from an unprivileged process. What I didn’t have was the staged profile data itself, because the only times the window is open in real life are during an active MDM operation on a real device. Apple closed the case on the basis that the timing dependency makes the attack surface too narrow. I think that reasoning is sound. Filing it anyway, because the architectural asymmetry — six methods gated, one not — is worth a note on the record.

The finding

Technical Finding — AMFID-02

Component: amfid, com.apple.amfi.nsxpc XPC service, macOS 15 / 26

Finding: getStagedProfileWithReply: dispatches without calling verifyBoolEntitlement:. Its six sibling methods on the same interface all call the entitlement check before dispatch.

Window: The method returns profileData and profileAuxSig only while an MDM profile is being staged (installed but not yet committed). Outside this window it returns an NSError.

Evidence: Static analysis; DTrace showing dispatch; XPC probe confirming reachability from uid=501 with no entitlements (returns NSError outside staging window, confirming method body executed).

Status: Disclosed to Apple. Closed: timing-window and MDM staging dependency deemed insufficient attack surface.

The com.apple.amfi.nsxpc service is how other system components communicate with amfid, the Apple Mobile File Integrity daemon. The interface exposes several methods for working with MDM profiles and integrity data. Every method I could find on the interface calls verifyBoolEntitlement: before doing anything with the caller's request. Every method except one.

getStagedProfileWithReply: dispatches directly. The entitlement check is absent. An unprivileged process — uid=501, no entitlements, no MDM enrolment — can call the method. Outside a staging window, the daemon executes the method body and returns an NSError indicating the profile is not available. The code ran. The path is reachable. What it returns depends entirely on timing.

The sibling comparison

Method verifyBoolEntitlement called
invalidateProfile:withReply: Yes — gated
commitStagedProfile:withReply: Yes — gated
removeStagedProfile:withReply: Yes — gated
stageProfile:withReply: Yes — gated
validateProfile:withReply: Yes — gated
getProfileDataWithReply: Yes — gated
getStagedProfileWithReply: No — not gated

The pattern is architectural rather than operational. Six of seven methods are gated. Whether the omission in the seventh is intentional — perhaps the staged profile was considered lower sensitivity during the brief staging window — or is an oversight is not something I can determine from the binary alone.

Proof of concept

amfid_staging_probe.sh Own hardware only — MDM staging window required for full PoC
#!/bin/bash
# Probe com.apple.amfi.nsxpc for getStagedProfileWithReply reachability
# Requirements: macOS 15+, arm64e, own hardware
# To retrieve profileData: must run during active MDM profile staging window

# ── DTrace reachability probe (requires SIP partial disable or SRDP hardware):
# sudo dtrace -n '
#   objc$target:AMFIServer:-getStagedProfileWithReply*:entry
#   { printf("dispatch reached, caller uid=%d\n", uid); }' \
#   -p $(pgrep amfid)
# Expected: entry fires for unprivileged callers — entitlement not checked.

# ── XPC reachability test (uid=501, no entitlements, Swift):
# import Foundation
# let conn = NSXPCConnection(machServiceName: "com.apple.amfi.nsxpc")
# conn.remoteObjectInterface = NSXPCInterface(with: NSObject.self as! Protocol)
# conn.resume()
# // Outside staging window: NSError returned (filesystem error or "no staged profile")
# // Method body executes — the path is reachable.
# // During active MDM staging: returns profileData + profileAuxSig.

# ── To construct a full PoC:
# 1. Initiate MDM profile installation on device under test.
# 2. During the staging window (profile written, not yet committed):
#    call getStagedProfileWithReply via XPC from an unprivileged process.
# 3. profileData and profileAuxSig returned without entitlement check.

echo "Run XPC reachability test — returns NSError outside staging window"
echo "For full PoC: execute during active MDM profile installation"

Apple's response and honest assessment

Apple closed this finding with a standard acknowledgement and the assessment that the timing-window and MDM staging dependency were insufficient attack surface for further investigation. The staging window is brief. An attacker who can trigger MDM profile installation on a target device — and who can race the polling loop to hit the window — already has a meaningful level of access to that device's management plane.

Opinion

In my view, Apple's reasoning here is sound, and I arrived at that conclusion myself before the closure arrived. The combination of requirements — MDM administrative access to push a profile, plus a process that can poll the XPC endpoint fast enough to hit the staging window, plus a device that is actively staging at the moment of the probe — is a narrow and setup-dependent attack path. The architectural observation is valid and interesting: one ungated method among six gated siblings is a pattern worth noting. But the practical exploitation path is constrained enough that "insufficient attack surface" is a defensible assessment rather than a dismissal. I think it is a genuine finding. I also think Apple closed it reasonably. Both things can be true.

The finding is published here because the architecture is worth understanding by anyone working on MDM security on macOS. The ungated method is reachable; what you get from it during the staging window is profile data that the gated siblings would require an entitlement to access. Whether that data is actionable in a real attack scenario is a question that requires the full staging-window PoC I was not able to complete.


Disclosure note

This finding was submitted to Apple through the Apple Security Research Framework. Apple confirmed closure. This post discloses the finding in accordance with responsible disclosure practice. The PoC above requires your own hardware. No third-party systems were accessed or tested. DTrace evidence and XPC reachability results are described accurately.

Are you there? The method ran. The window was closed. Something that was reachable only briefly still was reachable — that much is not in question.