Safari Flaw Can Expose iPhone Users in the EU to Tracking

By Talal Haj Bakry and Tommy Mysk

If you enjoyed this work, you can support us by checking out our apps:

tl;dr

Apple has introduced a new URI scheme in iOS 17.4 to allow EU users to download and install alternative marketplace apps from websites. Once an authorized browser invokes the special URI scheme marketplace-kit, it hands off the installation request to a MarketplaceKit process that starts communicating with the marketplace back-end servers to finally install the app. As part of the installation flow, the MarketplaceKit process sends a unique client_id identifier to the marketplace back-end. Both Safari and the MarketplaceKit process allow any website to make a call to the marketplace-kit URI scheme of a particular marketplace. As a result, multiple websites can trigger the MarketplaceKit process to send the same unique identifier client_id to the same marketplace back-end. This way a malicious marketplace can track users across different websites.

Video

Background

To comply with the European Digital Market Act (DMA), Apple had to introduce a new method that allows EU users to download and install alternative marketplace apps from the developers’ websites. The marketplace developer needs to add a call to a special URI scheme to their website. The call must be triggered by an HTML button, i.e. a click event. According to Apple, this is a security measure to prevent triggering the installation process without the user’s consent.

Apple must have forgotten that this is the web, and developers can actually style HTML buttons to virtually look like anything. It’s not clear what value this security measure brings. Anyhow, the new URI scheme looks like this:

marketplace-kit://install
    ?alternativeDistributionPackage=<url>
    &installVerificationToken=<install verification token>    
    &token=<authentication token>
    &account=<user id>;

Apple documentation describe the fields as follows:

URL ParameterDescription
alternative
Distribution
Package
Your marketplace app’s alternative distribution package in the assembled format described in Ingesting an alternative distribution package.
install
VerificationToken
A required JSON web signature (JWS). For more information, see Supplying an install verification token.
tokenAn optional authentication token to include if downloads require authorization. iOS sends the token back to your token endpoint to reference this request. The value is free-form, and can contain any information at your discretion.
accountAn optional user ID for the page visitor. iOS groups apps in restore requests based on account. iOS also provides the account as login_hint for the authorization call during interactive re-authentication; for more information, see Reauthenticating a person to manage apps.

When this scheme is invoked by an authorized browser, it hands off the installation request to MarketplaceKit. Then MarketplaceKit starts an internal process that receives all the URL parameters and kick-starts the installation process. It starts by retrieving the following .well-known resource from the marketplace website:

https://<fully-qualified-domain>/.well-known/oauth-authorization-server

MarketplaceKit constructs this URL by replacing the base URL with the base URL passed in the alternativeDistributionPackage parameter. Once downloaded, MarketplaceKit extracts the token_endpoint URL from the JSON structure and sends the following request to it:

POST /oauth/token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded


grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&issued_token_type=urn:ietf:params:oauth:token-type:access_token
&subject_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjIyIn0
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&client_id=6ef01a168f14431d98a0626cf5c89104

As Apple documentation explains, client_id is “a value that iOS randomly generates once per marketplace, device, and account combination.” This means that client_id remains unique as long as the combination of device, Apple ID account, and marketplace remains the same. It remains the same every time the marketplace-kit scheme is invoked, even after a device restart or clearing the browser cache. In addition, MarketplaceKit relays whatever token passed to the scheme in the parameter token to the subject_token parameter in the POST request to the /oauth/token endpoint.

It is worth noting that only browsers authorized by Apple can invoke the marketplace-kit URI scheme. Browsers willing to support the new scheme have to apply for a special entitlement. At the moment, only Brave, Ecosia, and Safari support the marketplace-kit URI scheme.

Implementation Flaws

Our testing shows that Apple delivered this feature with catastrophic security and privacy flaws. First, Safari invokes the marketplace-kit URI scheme without checking the origin of the website containing the URI scheme and the URL passed in the alternativeDistributionPackage input parameter. This allows cross-site tracking as we’ll show in the next section.

Second, MarketplaceKit would accept any parameters once invoked. It doesn’t read or validate the JWT tokens passed in the argument. We are sure that Marketplace doesn’t read the tokens because we sent text that doesn’t conform to a valid JWT structure and MarketplaceKit accepted it. Worse, it blindly relayed the invalid JWT token when calling the /oauth/token endpoint. This opens the door to various injection attacks to target either the MarketplaceKit process or the marketplace back-end.

Third, certificate pinning is not deployed in the entire process. This makes it easy to intercept and manipulate requests between the MarketplaceKit process and the marketplace back-end. It might be tricky to support certificate pinning here because MarketplaceKit might communicate with many servers that can dynamically be changed by the marketplace developer in the .well-known resources. But this also has potential issues. In our testing, we overwrote the .well-known resources through intercepting the calls and we fed our own endpoints. As a result, MarketplaceKit called our endpoints.

Flaws in software are not uncommon. However, the severity of these flaws in both the design and implementation raise concerns about Apple’s entire approach to app sideloading.

Secretly Tracking Users

Our observation shows that MarketplaceKit always reacts to the input parameters passed in the scheme and send the client_id identifier to any website. It doesn’t check if the information matches a registered marketplace or not. However, we realized that when the information doesn’t match a registered marketplace, client_id keeps changing every time the URI scheme is invoked. But as long as the base URL of alternativeDistributionPackage and account input parameters match a registered app marketplace, then MarketplaceKit would always send a fixed client_id to the /oauth/token endpoint of the registered marketplace.

This makes the perfect recipe for a malicious marketplace to be able to track users across different websites. All the malicious marketplace has to do is get approved by Apple. History shows that Apple’s review process is very flawed as many scam apps continue to find their way to Apple’s App Store.

The release of the first alternative marketplace run by altstore.io has made the process clearer for us and provided a good example for experimentation.

We built a couple websites to prove our theory. Since the AltStore has already been approved by Apple, we “borrowed” their alternativeDistributionPackage URL and account name. We added the following code to an HTML button and deployed it on three different websites, namely mysk.ca, mysk.app, and mysk.io:

        async function downloadAltStore()
        {
            const adpURL = "https://altstore.io/";
            const storeAccountName = "altstore";

            const installURL = "marketplace-kit://install?alternativeDistributionPackage=" + 
                  adpURL + "&installVerificationToken=" + "some token" + 
                  "&token="+ "thisIsMyskCa" +
                  "&account=" + storeAccountName;
            window.location.href = installURL;
        }

Now when a user visits these three websites, each website will trigger MarketplaceKit to call the marketplace endpoint and hand it the unique client_id and any custom payload passed in the token parameter. The unique client_id will enable the marketplace developer to trace all three visits to the same user. It can also share this information with the websites to personalize ads, for example.

For the script above, MarketplaceKit sent the following request to the /oauth/token endpoint of AltStore:

POST /oauth/token HTTP/1.1
Host: 8b70f8gea.execute-api.eu-central-1.amazonaws.com
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Accept: */*
User-Agent: ManagedAppDistribution/1.0 iOS/17.4.1 model/iPhone 12,8 build/21E236
Content-Length: 262
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
client_id=BF66B736-AC4B-4E04-A8C7-2628AD577E55
&issued_token_type=urn:ietf:params:oauth:token-
type:access_token&grant_type=urn:ietf:params:oauth:grant-type:token-
exchange&subject_token=thislsMyskCa&subject_token_type=urn:ietf:params:oauth:token-type:access_token

We used Safari on iOS 17.4.1 in private browsing mode during the test.

The sample script shown above breaks right after exchanging the unique identifier. It doesn’t run the entire flow to eventually install the app. Apple documentation states that the installation can only be started once invoked from the developer’s registered website. But the check for the website happens at a very later stage of the process.

What makes this attack perfect for trackers is that MarketplaceKit runs once the user taps on a button. It could really be any button. And it sends the unique client_id silently without the user being aware of that. And when it fails for some networking reason, it fails silently without presenting any error to the user.

This attack only works on EU iPhones. Other iPhones don’t support the marketplace-kit URI scheme.

Final Words

The flaw of exposing users in the EU to tracking is the result of Apple insisting on inserting itself between marketplaces and their users. This is why Apple needs to pass an identifier to the marketplaces so they can identify installs and perhaps better calculate the due Core Technology Fee (CTF).

Safari should protect users against cross-site tracking. It should do what Brave has done and check the origin of the website and match it against the URL passed in the alternativeDistributionPackage parameter. It shouldn’t invoke the URI scheme if the URLs don’t match. Surprisingly, Apple finds it more important to check if the scheme call came from an HTML button event than checking for cross-site invocation of the marketplace-kit URI scheme. Very Puzzling.

Moreover, we always advise developers dealing with JWT tokens to verify them before using them. Sadly, we can’t even give this advice to Apple because they don’t even try to parse the JWT tokens. So, please read the JWT tokens and make sure they are parsable, then validate them before working on the request.

Finally, EU users who want to avoid being tracked should use Brave. It’s currently the only authorized browser that blocks this type of cross-site tracking.

Technical details

  • iOS version: 17.4.1
  • iPhone country: Germany
  • Tested Brave version: 1.65
  • Tested Ecosia version: 9.4.0