EDR Freeze on macOS | North Pole Security
AI agents: see /llms.txt for a full index of this site, or /llms-full.txt for concatenated documentation.
Get a demo
Back<br>Research • June 18, 2026 • By Pete Markowsky
Last year Picus Security unveiled EDR Freeze, a technique that can silently pause a security product on Windows so it stops alerting or responding to anything. The attack doesn’t crash the product or uninstall it. The process is still there, still looks healthy in every monitoring tool, but it just isn’t doing its job anymore. It’s a clever bit of work, and it doesn’t require exploiting a vulnerability in the operating system’s kernel.
While EDR Freeze is a Windows-specific attack, the underlying idea of suspending a security process to create a blind spot translates directly to macOS. Products built on Apple’s Endpoint Security library face a similar risk if they don’t take one specific precaution: subscribing to the ES_EVENT_TYPE_AUTH_PROC_SUSPEND_RESUME event and denying pid_suspend calls that target their own process.
A note on prerequisites: A user must already have root on macOS to call pid_suspend against a system extension running as root. This is a post-exploitation technique, not an initial access vector. But if an attacker already has root and your Endpoint Security client isn’t defending itself against suspension, the consequences are significant. Also, pid_suspend has been part of macOS since Snow Leopard (10.6) and long predates the Endpoint Security library, so it’s not going anywhere.
We presented this research at NYC Sprawl 0x5. The slides are available here.
Why pid_suspend Is So Effective
To understand why pid_suspend is such a potent primitive, it helps to know a little about how macOS is put together under the hood.
The macOS kernel (XNU) is a hybrid of two distinct layers. At the bottom sits the Mach microkernel, which manages the lowest-level abstractions: tasks, threads, ports, and IPC. On top of Mach sits the BSD layer, which provides the POSIX-compatible process model, file systems, networking, and the system call interface that most programs interact with. When you think of a “process” on macOS, you’re thinking of a BSD-level concept. Every BSD process is backed by an underlying Mach task that actually owns the threads and address space.
Simplified Anatomy of a XNU Process
pid_suspend operates at the Mach task level, not the BSD process level. When called, the kernel suspends the underlying Mach task directly, freezing all of its threads and pausing scheduling until pid_resume is called. Because this happens below the layer where user-land code runs, a suspended client can’t dequeue or respond to Endpoint Security events since its threads simply aren’t executing.
There’s another subtlety that makes this particularly reliable for an attacker: Mach task suspension is reference-counted. Each call to pid_suspend increments the task’s suspend count, and each call to pid_resume decrements it. A task only resumes execution when its suspend count drops back to zero. An attacker can call pid_suspend multiple times to ensure the target stays frozen, and a single errant pid_resume from elsewhere in the system won’t accidentally wake it up.
Crucially, a suspended task produces no visible side effects. Parent processes using the wait() family of syscalls won’t be notified that the target has stopped.
Because the Mach task is still technically alive, just not running, process accounting looks normal. Tools like ps and Activity Monitor will still show the process as present. From the outside, the security tool appears to be running, but it isn’t really doing anything.
Bypassing Authorization Controls
When an Endpoint Security client subscribes to authorization events (AUTH events), macOS offloads the decision of whether to allow or deny an action to the client. The action is held up in the kernel until the client responds. If multiple clients are subscribed to the same AUTH event, all of them must respond before the action can proceed and a single “deny” result from any client is enough to block it.
An Endpoint Security client is supposed to respond with either ES_AUTH_RESULT_ALLOW or ES_AUTH_RESULT_DENY, and the Endpoint Security subsystem gives each client a deadline by which it must respond. If the client fails to respond in time, the OS kills it to prevent the system from deadlocking. Once the client is killed, and if there are no other clients that would deny the action, that action is allowed to proceed.
How Endpoint Security events flow between the kernel and a user-space client. If the client misses its deadline, the kernel terminates it.
Given these conditions, the “EDR Freeze” on macOS is straightforward:
Call pid_suspend against the Endpoint Security client process.
Perform the action that the client would normally block. The kernel dispatches the AUTH event to the now-suspended client, which sits there unable to process it.
Wait for the deadline...