App Translocation

June 14, 2016

I was looking through the misnamed "What's New in OS X" document, and something caught my eye under "Security and Privacy Enhancements":

Starting in OS X v10.12, you can no longer provide external code or data alongside your code-signed app in a zip archive or unsigned disk image. An app distributed outside the Mac App Store runs from a randomized path when it is launched and so cannot access such external resources.

This is further explained in the equally misnamed "OS X Code Signing In Depth":

If using a disk image to ship an app, users should drag the app from the image to its desired installation location (usually /Applications) before launching it. This also applies to apps installed via ZIP or other archive formats or apps downloaded to the Downloads directory: ask the user to drag the app to /Applications and launch it from there.

This practice avoids an attack where a validly signed app launched from a disk image, ZIP archive, or ISO (CD/DVD) image can load malicious code or content from untrusted locations on the same image or archive. Starting with macOS Sierra, running a newly-downloaded app from a disk image, archive, or the Downloads directory will cause Gatekeeper to isolate that app at a unspecified read-only location in the filesystem. This will prevent the app from accessing code or content using relative paths.

The attack is not named, but presumably it's dylib hijacking.

My company distributes apps via zip archives, so I put the new OS behavior to the test. I downloaded a zip file to ~/Downloads, unarchived an app from the zip, and opened the app directly from ~/Downloads. The system mounted a read-only disk image in /private/var/folders/[yaddayadda]/AppTranslocation/ (i.e, DARWIN_USER_TEMPDIR/AppTranslocation/), copied the app bundle from ~/Downloads into a folder within the disk image, and launched the app from there. You can verify that the app has been "translocated" by looking under the Activity Monitor "Open Files and Ports" tab in the app process inspector window.

Under what circumstances does App Translocation occur? First, the app must have a extended attribute. If you delete the quarantine xattr, then App Translocation does not occur, and the app will launch from where it was unarchived, like normal. Second, the app must be opened by Launch Services. This usually means Finder, but it can also mean open from Terminal, for example. If you launch the app executable directly from bash, on the other hand, App Translocation does not occur. Third, the app must not have been moved — by Finder. If you move the app, using Finder, from the app's original unarchived location to another folder, even a subfolder, e.g., ~/Downloads/Test/, then App Translocation does not occur. However, if you move the app using mv from Terminal, then App Translocation will still occur. Normally you would move the app from ~/Downloads to /Applications, and that would cause the app to be launched from /Applications like normal, but the locations of the particular folders don't seem to matter. The mere act of moving the app using Finder stops App Translocation from happening. Indeed, once you've moved the app once, it will no longer experience App Translocation again, even if you then move it back to ~/Downloads.

It should be noted that it doesn't matter to App Translocation whether your downloads folder is ~/Downloads. It will still occur if you download a zip to a different folder. It will even occur if you move the downloaded zip to another folder before unarchiving. What matters is whether the unarchived app has been moved from its original archive location.

How does App Translocation affect us, as app developers? For the most part, the apps seem to work as before. After all, it shouldn't matter whether your app is launched from /Applications, from ~/Downloads, or from the Xcode build folder. However, when the app runs from a read-only volume, it cannot update itself to a newer version. App Translocation prevents software update mechanisms such as Sparkle from working.

My friend [redacted] informed me that there is actually a new API for App Translocation in the 10.12 SDK: <Security/SecTranslocate.h>. I haven't tried that API yet; I still haven't attempted to build our apps using Xcode 8. But hopefully it should be useful in determining whether an app has been translocated. Once that condition has been detected, what's the solution? One approach would be to simply ask the user to move the app in Finder. It's also conceivable that the app could copy itself to /Applications, without the quarantine xattr, and relaunch itself from there. Again, this isn't something I've tested yet. I haven't had much time to explore Sierra, because we just got it yesterday, and I do need to sleep.