How a single line of code could brick your iPhone by sashk
This is the story of how I found one of my favorite iOS vulnerabilities so far. It’s one of my favorites because of how simple it was to implement an exploit for it. There’s also the fact that it uses a legacy public API that’s still relied upon by many components of Apple’s operating systems, and that many developers have never heard of.
Darwin Notifications
Most iOS developers are likely used to NSNotificationCenter, and most Mac developers are also likely used to NSDistributedNotificationCenter. The former only works within a single process, the latter allows simple notifications to be exchanged between processes, with the option to include a string with additional data to be transmitted alongside the notification.
Darwin notifications are even simpler, as they’re a part of the CoreOS layer. They provide a low-level mechanism for simple message exchange between processes on Apple’s operating systems. Instead of objects or strings, each notification may have a state
associated with it, which is a UInt64
, and typically is only used to indicate a boolean true
or false
by specifying 0
or 1
.
A simple use case for the API would be for a process that just wants to notify other processes about a given event, in which case it can call the notify_post
function, which takes a string that’s usually a reverse DNS value like com.apple.springboard.toggleLockScreen
.
Processes interested in receiving such a notification can register by using the notify_register_dispatch
function, which will invoke a block on a given queue any time another process posts the notification with the specified name.
A process that’s interested in posting a Darwin notification with a state has to first register a handle for it, which can be done by calling the notify_register_check
function, which takes the name of the notification and the pointer to an Int32
, which is where the function returns a token that can be used to call notify_set_state
, which also takes a UInt64
value for the state.
Via the same notify_register_check
mechanism, a process that wants to get the state of a notification can call notify_get_state
to get its current state. This allows Darwin notifications to be used for certain types of events, but also hold some state that any process on the system can query at any given time.
The Vulnerability
Any process on Apple’s operating systems — including iOS — can register to be notified about any Darwin notification, from within its sandbox, without the need for special entitlements. This makes sense given that some system frameworks used by third-party apps rely on Darwin notifications for important functionality.
Given that the amount of data transferred through them is very limited, Darwin notifications are not a significant risk for sensitive data leaks, even though the API is public, and sandboxed apps can register for notifications.
However, just as any process on the system can register to receive Darwin notifications, the same is true for sending them.
To summarize, Darwin notifications:
- Require no special privileges for receiving
- Require no special privileges for sending
- Are available as public API
- Have no mechanism for verifying the sender
Considering these properties, I began to wonder if there were places on iOS using Darwin notifications for powerful operations that could potentially be exploited as a denial-of-service attack from within a sandboxed app.
You’re reading this blog post, so I’ve already spoiled it: the answer was “yes”.
Proof of Concept: EvilNotify
With that question in mind, I grabbed a fresh copy of the iOS root filesystem — one of the early iOS 18 betas at the time, I think — and began looking for processes that used notify_register_dispatch
and notify_check
.
I quickly found a bunch of them, and made a test app called “EvilNotify” that I could use for testing.
Unfortunately, I no longer have a vulnerable device I could use to rec
7 Comments
_rrnv
Great work! This is my favourite type of vulnerability, simple, effective and brutal. Reminds me of a time two decades ago when with a friend from uni we theorised about a perfect server vulnerability where you’d exploit a machine by pinging it. And of course, two years ago it was in fact discovered as CVE-2022-23093.
dado3212
Neat, $17,500 is pretty good, I’m so used to these blog posts being for peanuts, or where companies fix the vulnerability but don’t pay out at all. Apple’s gotten better about this since 2019.
shrx
> Looking into the binaries, SpringBoard was observing that notification to trigger the UI. The notification is triggered when the device is being restored from a local backup via a connected computer, but as established before, any process could send the notification and trick the system into entering that mode.
This should probably be reworked regardless if the patch described in the article was implemented.
jonplackett
Anyone know how long ago that system would have been introduced?
It seems like such an obvious security concern. Maybe it was pre-AppStore? And more assumed trust in other apps?
brcmthrowaway
Ultimately, does this require installing a sketchy app in the first place?
urbandw311er
Nice. I can only imagine what a crap day in the office it was when the iOS core team reviewed that one.
doesnt_know
I get that it's potentially lower priority since a user needs to actively install a malicious app, but that timeline doesn't exactly feel me with confidence…