macOS Security Bugs Exposed Safari History and Device Location to Unauthorized Apps

Apr 4 2023 3:00 PM

If you’ve been reading my previous posts about security vulnerabilities that I discovered on Apple’s operating systems, you’ve probably noticed a pattern of bugs being caused by improper validation of clients by XPC services. So it’s probably not a surprise that the latest CVEs also fall into that category, but the first one that I'm going to talk about has a slight twist.

CVE-2023-23506/28192: Expectation vs. Reality

Broken assumptions are the cause of many security issues found in shipping software. The developers behind a given piece of software expect a certain behavior from the operating system and surounding environment, those expectations feed into their decision making, and then if the expectations are broken, things start to fall apart, and that includes security.

An assumption I’m sure many Mac developers have had for a long time has to do with how XPC services on macOS are isolated to a given namespace.

There are basically three types of XPC service on macOS:

Note: this is not an official categorization by Apple, it’s just how I like to view them.

Let’s focus on the local service type. That's the type of XPC service you usually find bundled within an application, in the Contents/XPCServices directory. It may also be embedded in other types of bundles, such as frameworks, where executables linked against that framework will be able to access its bundled XPC service.

Local XPC services don’t tend to have a lot of privilege, they’re usually responsible for small tasks that must be isolated from the app or some other client. However, some local XPC services Apple ships with their operating systems are used for tasks that can be considered privileged, because they include entitlements that grant access to sensitive data regular apps can’t access.

That is also the case for many global mach services in Apple’s OSes, but those tend to have very strong and effective authentication for connecting clients, preventing them from being used by malicious processes trying to exfiltrate user data.

However, when it comes to these local XPC services, the assumption that their scope is limited -- both in terms of functionality as well as which processes can even look them up to initiate a connection in the first place — means that not all local XPC services on macOS have strong authentication for clients.

So I started to wonder: what would happen if that assumption were to be broken? 🤔

It didn’t take me long to find out that it could in fact be broken, and using a feature that’s played a part in many security vulnerabilities in the past: symlinks.

When creating a connection to a bundled XPC service in the Contents/XPCServices directory, an app will use the initWithServiceName: initializer on XPCConnection. The system will then look up a matching service bundle within the client’s own bundle, spawing the XPC service process and completing the connection. If a corresponding XPC service bundle can't be found in the namespace, the connection will be invalidated.

But if I want to connect to some local XPC service that’s not a part of my app bundle, but is installed on the macOS filesystem somewhere, what can I do?

Well, turns out you could just symlink another bundle’s Contents/XPCServices directory into your own app’s Contents/XPCServices, and launchd would happily follow that symlink and allow your app to lookup and connect to a local XPC service embedded in a completely unrelated bundle.

After figuring this out, I wanted to find a fun practical example on macOS where a local XPC service was not doing proper validation of connecting clients. I found quite a few, but most of them didn't have more privilege than any other regular app. Then I actually made a little tool that would scan my macOS installation for local XPC bundles, collecting the list of entitlements for each one in a searchable fashion, so that I could focus my testing on services that included interesting capabilities.

I quickly found one.

TimeZoneService.xpc

That service grabbed my attention because of its name, and because it included the com.apple.locationd.effective_bundle entitlement. That gives a process the ability to access the device's location via CoreLocation without having the access attributed to the process itself, but to a separate bundle that's only used for that purpose.

This service is embedded in /System/Library/PreferencePanes/DateAndTime.prefPane/Contents/Resources/TimeZone.prefPane, which is responsible for the time zone features in System Settings > General > Date & Time.

One of the things this service handles is the “Set timezone automatically using your current location” option. When enabled, the preference pane uses the bundled XPC service in order to obtain the current device location. Because the location request goes through TimeZoneService and it has the effective bundle entitlement, what the location icon in the Menu Bar shows is just "Setting Time Zone”.

That is, if the option to show that icon is enabled at all. By default, macOS won’t show the location icon when a “system service” is accessing the device’s location, so a malicious process that exploited TimeZoneService would likely go completely undetected by the user.

For my proof of concept, all I had to do was symlink /System/Library/PreferencePanes/DateAndTime.prefPane/Contents/Resources/TimeZone.prefPane/Contents/XPCServices into my app’s Contents/XPCServices, which I did as a run script build phase in Xcode. I then implemented a simple client that would open the XPC connection and call the location-gathering method exposed by TimeZoneService in its interface. The result was a sandboxed app that could access the device's location without the user's permission or knowledge. You can see it in action in the video below:

Timeline:

📣 Audit Your XPC Bundles

Apple fixed the issue by preventing this symlink trick from working. It’ll still work if launching the app from Xcode, but not on regular user app launches. TimeZoneService also received an update and will now validate connections against an entitlement, mitigating the issue completely.

However, it’s possible that the underlying trick of using a symlink to access another bundle’s local XPC services was not the only way to achieve this. What this means is that developers of Mac apps that bundle local XPC services should audit their apps to make sure that malicious processes can’t take advantage of this trick to access sensitive user data or compromise app functionality.

The best way to do it is to get the audit token for the process on the other end of the connection then validate it against certain criteria, such as app identifier or development team ID. For apps targeting macOS Ventura and later, there’s new API to facilitate that.

CVE-2023-23510: Safari History Access

This vulnerability is a more “classic” one where a global system service is not validating connecting clients appropriately, exposing private user data.

The browsing history database that's created and maintained by Safari can contain very sensitive data about a user's browsing habits, including search queries, sensitive private URLs, and URLs to file sharing services containing confidential information that's secured by the obscurity of the URL itself.

Because of that, the directory where Safari stores its history database is one of the locations that System Integrity Protection (SIP) guards, preventing any random process on the system from accessing the database.

Directories with that protection are considered so sensitive that not even root can read the contents of the directory where Safari stores the user's browsing history. If you have SIP enabled on your Mac (you should), you can verify this by running sudo ls ~/Library/Safari in Terminal:

Screenshot of a Terminal window on macOS showing the invocation sudo ls ~/Library/Safari with an operation not permitted error as the result

As you can see, a simple attempt to list the contents of Safari’s directory in the user’s Library fails with "Operation not permitted”.

But of course Safari itself needs to access that directory, so how is that achieved?

If your answer was “a private entitlement”, you are correct!

Safari uses the com.apple.Safari.History agent. The agent has the com.apple.private.security.storage.Safari entitlement, which grants it access the Safari folder in the user's Library. That agent vends an XPC interface that Safari can then use in order to access and manipulate the user's browsing history.

You can probably see where this is going…

Safari’s history agent was not validating client processes that connected to it, which meant that any process running on the system could access the user’s Safari browsing history.

Here’s the proof of concept in action:

This issue also affected Safari Technology Preview, which has its own com.apple.Safari.History agent. Interestingly, the version of the agent that ships with Safari Technology Preview is a local XPC bundle, not a global mach service like the one used by regular Safari, so that one required the use of CVE-2023-23506 in order to be exploited.

Timeline: