CVE-2026-28845: A TCC Bypass via Custom Extension Ownership
Apple Security Advisory: macOS Tahoe 26.4
Introduction
Most of my previous research has dealt with memory corruption, digging through Apple’s open source repositories for the occasional overflow. This issue is different. CVE-2026-28845 is not a bug in a single line of C. It is a TCC bypass on macOS that abuses how LaunchServices decides which application owns a document type, and how that decision sidesteps the Transparency, Consent, and Control (TCC) framework meant to guard sensitive data. There is no crash trace or ASan output to share here. Tested on macOS Sequoia 15.4.1 (24E263).
TCC is the subsystem responsible for the familiar permission prompts: “App X would like to access files in your Downloads folder.” Certain locations on macOS, the Desktop, Documents, and Downloads folders among them, are gated behind TCC. The model Apple presents is straightforward: a location is protected, and an app must ask permission before reaching into it. If the app never got consent, it never gets the file. The rest of this writeup shows how that model breaks without ever requesting a single permission, by way of LaunchServices, the subsystem that maps files to the applications that open them.
The Vulnerability
When you open a file, macOS does not guess what to do with it. It looks at the file’s extension and its Uniform Type Identifier (UTI), consults a database of every installed application’s declared capabilities, and routes the file to whichever app claims to handle that type. Applications advertise what they can open through two keys in their Info.plist. CFBundleDocumentTypes declares the document types an app handles, along with an LSHandlerRank that signals how strongly the app wants to claim a type. UTExportedTypeDeclarations lets an app export a new UTI and bind it to a filename extension.
The LSHandlerRank field is the key detail. It accepts a handful of values, but the one that matters here is Owner. Declaring LSHandlerRank = Owner tells LaunchServices that your app is the authoritative handler for a type, as if your app defined that type in the first place. It outranks Default, Alternate, and None. And here is the problem: password managers and other sensitive applications identify their files with custom extensions. KeePass uses .kdbx. These extensions are not reserved, and nothing stops a second application from declaring the same extension and marking itself the Owner.
The attack follows directly. I built a plain Swift/Cocoa app whose only distinguishing feature is its Info.plist. It declares a UTExportedTypeDeclarations entry that exports a new UTI and ties it to the kdbx filename extension via public.filename-extension, alongside a CFBundleDocumentTypes entry that binds that same UTI to the app and sets LSHandlerRank to Owner. Once macOS indexes the bundle, LaunchServices registers the malicious app as an authoritative handler for .kdbx. No entitlements, no TCC prompt, no user consent beyond installing the app in the first place. The app tells LaunchServices it owns a file type, and LaunchServices accepts the claim. Because an app that handles a document type gets handed the file to open, opening a .kdbx then routes its bytes straight to the malicious app.
Root Cause
The reason this bypasses TCC is that file access here is routed through the document-type handler rather than performed by the app reaching into a folder on its own. Claiming ownership of the type first is what grants the malicious app the file, even when that file lives inside a TCC-protected location such as Downloads. When a user opens a .kdbx database, LaunchServices hands the URL to its declared owner, and the open is treated as a user-initiated action. TCC’s consent gate is never the thing standing between the app and the file, so the decision sidesteps the consent model entirely. The user, believing they are opening their password database, has instead handed it to whoever claimed the extension first.
There is a second aspect worth noting: deleting the malicious app does not close the door. After the bundle is removed from disk, macOS keeps it cached as a registered handler for .kdbx. Files with the targeted extension are still routed to the deleted app, and macOS will still launch it to process them. The registration outlives the bundle, so a victim who drags the offending app to the Trash and considers the matter resolved remains exposed, because LaunchServices remembers an owner that no longer visibly exists on the system.
Reproduction
Conceptually, the trigger is to install an app that claims the target extension as Owner, run it once and quit it to mimic an ordinary freshly installed app, then open a file of that type from inside a TCC-protected area such as Downloads. The database sits in a protected folder, and the malicious app has never been granted access to it. Under the model TCC presents, the read should be denied because no consent was ever given. In practice the malicious app opens the .kdbx file and reads its contents anyway, because the file was routed to it by LaunchServices as the declared owner and the access never travels through the path TCC watches. The result is that a completely unprivileged, low-privilege app with no special entitlements or TCC grants can silently read password-manager databases and any other sensitive files identified by a custom extension.