Let's go apple picking: Scaling macOS bug discovery
Conducting macOS security research at scale is hard. Useful telemetry is strictly limited to entitled applications, and taking ad hoc approaches to interrogate system binaries entails disabling OS protections, which can potentially taint observable events.
Background
A comment from Gergely Kalman, one of the first macOS security bloggers I was ever introduced to, often comes to mind; "Do dumb analysis, but do it at scale".
My first macOS exploits were the product of technical write-ups and blog posts. I'd read and replicate the attacks while attempting to introduce novel ways to circumvent the applied fix. Rarely did I identify a vulnerable component of macOS based solely on my own observations and research.
As it turns out, from time to time, researchers whom I admire also find inspiration from their peers.
With a desire to contribute something of my own to the community, I began this project.
The Idea
To get serious about scaling my analysis, I need telemetry. A serious amount of it.
But why stop there? What about Apple's opaque web of elaborately named binary entitlements that support its auth model? You can Google hundreds of unique binary entitlements with barely a single page of results detailing their relevance, meaning or impact to security (if any).
Therefore, I decided to contextualise that stream of system data with other sources; binary entitlements and launch constraints came to mind. Enriching this data, as well as running correlation rules on it at a regular cadence (much like any EDR tool, but for detecting potential macOS platform logic bugs), will be necessary. I especially wanted untainted, unmuted system data.
The best source of broad telemetry is undoubtedly Endpoint Security (ES). Introduced in 2019 to replace the Kauth KPI, the unsupported MAC kernel framework, and the OpenBSM audit trail, ES has evolved to supply 104 distinct NOTIFY events (as at macOS 26 beta 2).
However, there exists a barrier to entry: the coveted com.apple.developer.endpoint-security.client entitlement.
"Given the power of Endpoint Security, Apple is understandably cautious about granting requests for the client entitlement, even to renowned security companies. That said, you can take several measures to improve your chances of receiving one. First, register as a company, such as an LLC or equivalent. Iām aware of only one instance in which Apple granted the Endpoint Security client entitlement to an individual."
Patrick Wardle, The Art of Mac Malware - Volume II: Detection
There are several community tools available that assist those of us ("the un-entitled") who are interested in plundering ES for its insights:
- Objective-See / Patrick Wardle: Process, and File Monitor
- Brandon Dalton:
Red CanaryMac Monitor - TheMittenMac / Jaron Bradley: ESFPlayground
These have been created for different objectives, and as such, the number and types of ES_EVENTS that they are subscribed to align to their primary purpose and the derived value of those EVENT sources:
| Tool | ES Event Coverage |
|---|---|
| Process, and File Monitor | 10 |
| Red Canary Mac Monitor | 41 |
| ESFPlayground | 51 |
While I could self-prescribe the ES client entitlement on a device with SIP and AMFI disabled, I want to avoid that scenario for a few distinct reasons:
- My telemetry is being produced by my daily-driver Mac,
- Obtaining the best and most realistic data set involves leveraging an array of applications and services on macOS, many of which require authentication (I don't log into iCloud within my VMs),
- Some enhanced Apple services are simply not available within an unregistered VM (E.g., Apple Intelligence), and
- Generally, disabling OS level protections is a bad idea.
There is a time and place for lldb and dtrace, but it is not here.
It occurred to me that the above requirements can be overcome, and a simple low-rent solution is available: /usr/bin/eslogger.
Apple's eslogger is the bundled ES interface for macOS. It allows user-specified event subscriptions and logs all events to standard output in a handy JSON format. While its man page suggests it should not be considered a stable or reliable source for interfacing with the ES API, a thorough review has shown it to be quite comprehensive in its output. Regarding stability? Well, it hasn't failed me yet.
But does eslogger output represent all data structures defined in the Endpoint Security header files? Yes.
Does eslogger implement all modifications / new additions to Endpoint Security at each release? A review of macOS 26 beta's enhancements shows that they are, in fact, supported immediately (no surprise).
Finally, eslogger leverages the API functions for packed environmental variables and file descriptors related to ES_EVENT_TYPE_NOTIFY_EXEC, processes all audit_tokens, and presents time values in an ISO 8601-compatible format. This will assist when we eventually consume it into the Elastic Stack.
So, I took output from eslogger and used jq, the command-line JSON processor, to parse only the fields relevant to my research, then normalised it using ECS. This data can be enriched from a range of other sources via a Logstash Enrichment Pipeline. As a test case, I chose binary entitlements.
Proof of Concept
To establish a proof of concept, I decided to consume a baseline number of ES events most relevant to macOS logic bugs, resulting in the common bug classes: TCC and SIP bypass, local privilege escalation, and sandbox escapes.
| Event | Description |
|---|---|
| ES_EVENT_TYPE_NOTIFY_EXEC | An identifier for a process that notifies endpoint security that it is executing an image |
| ES_EVENT_TYPE_NOTIFY_FORK | An identifier for a process that notifies endpoint security that it is forking another process |
| ES_EVENT_TYPE_NOTIFY_CREATE | An identifier for a process that notifies endpoint security that it is creating a file |
| ES_EVENT_TYPE_NOTIFY_OPEN | An identifier for a process that notifies endpoint security that it is opening a file |
| ES_EVENT_TYPE_NOTIFY_WRITE | An identifier for a process that notifies endpoint security that it is writing a file |
| ES_EVENT_TYPE_NOTIFY_RENAME | An identifier for a process that notifies endpoint security that it is renaming a file |
| ES_EVENT_TYPE_NOTIFY_UNLINK | An identifier for a process that notifies endpoint security that it is deleting a file |
As an aside, I can't overstate the value of manually detailing ES EVENT message structures and mapping them to the Elastic Common Schema (ECS). For example, clearly distinguishing between the process (pre-execve(2)) and target (post-exec image) structs in ES_EVENT_TYPE_NOTIFY_EXEC, which represent different states of the same PID, was crucial for accurate context alignment and understanding es_message_t semantics.
Behold. Spreadsheets!
For data enrichment, a simple bash script designed to run codesign across all executable macOS binaries, filtering for a pre-defined list of entitlements (E.g., "com.apple.private.tcc.allow", "com.apple.private.tcc.manager.access.delete", "com.apple.rootless.install", and any "-heritable" counterparts). Once again, jq was used to format this output in JSON, pairing the "process.executable" with the relevant entitlement(s), nested with either boolean, array or string values.
Logstash consumed the output with ease.
Once ingested into Logstash, this "entitlement" index became the base for my ingest pipeline associated with ES event data, which results in all es_process_t _Nonnull process data having associated binary entitlements attached and stored into our "eslogger" index.
The data is flowing in, and while familiarising myself with EQL and ES|QL, I run a few spot checks to ensure that process lineage and binary context are as expected.
I was certain that to get a broad spectrum of telemetry, I'd have to become the greatest Apple fanboy imaginable.
Goodbye 1Password, hello Passwords.
Goodbye Spotify, hello Apple Music.
Goodbye human intelligence, hello Apple Intelligence.
The data is rolling in, and I gain familiarity with EQL and EQ|QL queries - both in an effort to confirm the fidelity of the data but also to familiarise myself with the nuances of each query language. I didn't expect my first finding, a SIP bypass, to come from this early stage of data validation. Simple logic comparable to "show me all process binaries with SIP privileges that wrote to non-protected system locations"? One symlink is all it took.
In a future blog post, I will discuss CVE-2025-43191, which Apple plans to patch in macOS Sequoia 15.6, and hopefully many more.
For now, REAP: refine - enrich - add - process.
My Sincere Thanks
This post formed part of an earlier application to speak at OBTS v8.0 in October, 2025. While my application was ultimately unsuccessful, this project proved highly fruitful for my Apple Security Bounty endeavours in 2025.
I'd like to thank the following people who assisted me from early concept to POC in many, many DMs, Signal Messages, and Zoom calls (in order of most swear words used):
- Gergely Kalman (@gergely_kalman) for many hours on Zoom, so many cool ideas, and many fruitful discoveries
- Colson Wilhoit (@DefSecSentinel) for an exceptional walkthrough of ELK, Elastic modules / visualisations, and Endpoint Security
- Csaba Fitzl (@theevilbit) for so much knowledge validation and careful direction
- Brandon Dalton (@PartyD0lphin) for keen insights into Mac Monitor, and Endpoint Security







