How I "hacked" ChatGPT Atlas... and why it wasn't patched
Introduction
Right at the beginning here, I want to make two things abundantly clear. First, the bug I found is arguably not a "hack". In fact, that's part of why I'm writing this article. Secondly, I harbor no ill will towards the OpenAI team (sidenote: thank you Theo for quickly connecting me with them). They've been extremely kind and communicative throughout this entire process. The reason why my bug wasn't patched is much larger than them, and it's one I've talked about before: Chrome's exclusion of local attacks in their threat model.
This article is about a lot more than just Atlas. In fact, I'm going to be front-loading a lot of information before I talk about the Atlas bug. After interacting with aspects of the Chrome ecosystem from many angles, and from researching macOS as a whole, I've come to the same conclusion as some of my fellow researchers have: that Chrome's threat model often leaves users on macOS less secure. A lot of what's in this article is repeated from my previous work, but I wanted to try and centralize my thoughts into a single article.
A platform disconnect
As mentioned, Chrome excludes local attacks in their threat model. Their listed reasons for this can be summarized down to the assertion that a local attacker can hook or modify other executing apps, so local access is already game over. This argument does not hold on macOS because, as I've discussed previously, code injection is essentially dead on macOS. What's worse is that, by ignoring local attacks, Chrome opens up users on macOS to attacks that undermine fundamental security features the OS itself.
Breaking macOS
TCC (which stands for Transparency, Consent, and Control
, as confirmed by Quinn, Developer Technical Support for Apple), is essentially the permission system on both macOS and iOS. I've already explained more about how TCC works in a previous article, so to put it briefly: TCC introduces the concept of "services" which are, conceptually, different collections of resources a user must give an app explicit consent to before that app can access those resources. These user consent responses are collected and stored in a database that is checked against at runtime when an app wants to access TCC-protected resources.
Additionally, macOS includes a private internal API to determine what process is responsible for a given process. The main process for an app is often responsible for itself and its subprocesses (although there is a private API that allows a process to disclaim a subprocess when launching it). TCC uses this API, among other things, to check against the stored database of user consent responses. In most cases where a process attempts to access TCC-protected resources behind a service, the access will be granted if its responsible process (or app) has an affirmative consent response to that service stored in the TCC database.
If a local vulnerability was found in an app which allowed an attacker to get the main app process (or one of its subprocesses) to access some TCC-protected resources for them, that action would be evaluated based on the user consent given to that target app (not any malicious attacking one). This opens up the possibility for a malicious app to "steal" the consent given to another app. While an attack like this often does not put the app itself (or it's data) at risk, is does fundamentally breaking how TCC is supposed to and expected to work.
To be clear, this sort of thing isn't unique to Chrome. It's part of a whole class of vulnerability: confused deputy attacks. For a non-Chrome example: Last year, I found and reported to Ghostty that their terminal would immediately open any executable that was passed to it, opening the door for such attacks. In response, Ghostty added a prompt before running files passed to it. The also matched the behavior of other terminals. The point here is that Ghostty responded to and defended against a local attack. That's something it likely wouldn't have done had it had been operating under Chrome's threat model.
Who operates under Chrome's threat model?
In 2024, the Electron team responded to a series of Critical
-scored CVE's that were filed against prominent Electron apps. In a blog post, the Electron team themselves assessed these bugs as not critical
, citing Chrome's threat model. This makes sense, as Electron itself is based on Chromium. In private conversations I've had with members of the Electron team, it's clear that this is, indeed, an example of the threat model being inherited downstream. But it's not the only one. As I've written about before, prominent Electron apps like Discord have likewise rejected reports of local-only attacks, citing Chrome's threat model.
The CVE's the Electron team were responding to abused certain obscure features of Electron to achieve JS code injection into Electron apps. Electron's recommended mitigations were to use their Fuses API to disable those features. However, outside of those obscure features, JavaScript code injection can also be achieved in Electron apps using a much simpler method: simply editing the app's JavaScript code files. To Electron's credit, they do offer features like ASAR Integrity to help developers secure their code. However, it's up to the developers of the apps to actually use it. The aforementioned Discord does not, allowing attackers with local access to hijack Discord and use it as a keylogger (if the user granted it permission to monitor keystrokes).
Down another path, Chromium forks like ChatGPT Atlas have to decide if and how they want to inherit Chrome's threat model. And, despite me believing they shouldn't outright ignore local attacks, it's a more complicated situation than that. If they choose to accept reports of local attacks, what should they do when people report local bugs that originate upstream and were ignored under the upstream threat model? They could try to limit themselves by only accepting reports of local attacks that are inherent to their fork, but the line of what is and isn't inherent to their fork
can get blurry and might be an infeasible boundary in actual practice. Ultimately, the team at OpenAI decided against patching my reported bug. But what is my bug?
Stealing Atlas's TCC privileges
You may have seen OpenAI's blog post about OWL, their architecture for Atlas. You may have also seen Theo's video covering that article. I too share Theo's frightening fascination with OWL, although he probably finds it more troubling than I do. I like abstractions. It's why I wrote a whole set of Swift wrappers around C-level macOS API's (both as a learning experience and to help me in my reverse-engineering efforts). It's also why I was excited while digging through the code of Atlas, stumbling upon things such as the Swift Mojo bindings mentioned in OpenAI's article. It was fascinating to reverse-engineer and search through.
Quite quickly upon digging through Atlas, I found that Atlas is two actually apps. As explained in their article, OpenAI wrote a custom SwiftUI app to act as the OWL Client
while a headless version of Chromium is used as the OWL Host
. It's in this unique architecture where I found my bug. OWL includes a mechanism to point to a different host app than the one Atlas shipped with. This allows an attacker with local access to plant a fake host app and point to it. That fake app then becomes a subprocess of the client app, allowing it to act with the TCC consent granted to Atlas since macOS believes Atlas is responsible for it.
In some respects, this is better than a remote bug originating from a website. You need to download and run something malicious in order to be exploited through this, not just visit a bad website. It's also worse in some respects. For example, if you downloaded Atlas, tried it for a bit (maybe gave it access to your microphone for a quick thing, and then never opened it again), a malicious app can still hijack Atlas and use that fact to access your microphone. You'd still be notified in the menu bar, but it would show that the Atlas app itself is doing the accessing. Additionally, through mechanics I won't reveal here to avoid encouraging abuse, it's possible to exploit this bug without ever showing an Atlas window, nor having it's icon appear in the dock.
The current reality
As of writing, this bug remains unpatched. It's unclear to me what use a mechanism to point to a different OWL Host has in production builds of Atlas, but it does seem to be important enough for OpenAI to keep it in the app. Again, I harbor no ill-will towards OpenAI in any of this. While I am disappointed such a bug was not patched, I completely understand their reasoning. Running a bug bounty program is hard, especially at their scale, and deciding what's in scope or not is often an exercise in workload management.
On one hand, I have to thank Chrome. Despite my issues with their threat model, it should also be noted that the Chrome team has put a lot of work into making the web what it is today. It's powerful and capable enough to where most people probably don't download and run things anymore, they just use web apps. Wether or not something like a rise in popularity of installable AI apps will lead to users downloading and running more things remains to be seen (cryptomining malware has recently been spotted purporting to be a macOS app for Grok AI). It's possible users may remain resistant to anything that's not a web app. It's possible they won't.
Looking to the future
If Chrome were to change their stance on local attacks, their updated threat model would likely be adopted downstream just as their current one already is. In my conversations with those downstream, that has been made very clear. Now, I'm not naïve. I know I'm just one person with one small perspective (albeit one shared with my fellow researchers). I know there are many legitimate arguments for Chrome's current exclusion of local attacks from their threat model. I know this situation is more complex than simply this inclusion or exclusion of local attacks. I know there's more to it. I don't expect things to shift in just a day or two. But none of that changes the fact that the current threat model has real harmful downstream effects.
Apple brought TCC to macOS in WWDC 2018, showing it off while announcing macOS Mojave. Days later, Patrick Wardle of Objective-See wrote a blog post about hijacking apps to steal their TCC privileges. In late 2019, a fellow researcher of mine, Wojciech Reguła, wrote about the ability to steal TCC privileges from Electron apps by injecting JS. The threat of these kinds of local attacks has been know about for several years. It's beyond time. The Chrome / Electron ecosystem (as a whole) needs to rethink how their security posture interplays with macOS and needs to do more to protect their users from things like this.
If you liked this article, remember to watch this space.