Reflections on the Mac sandbox escape

April 30 2020 by Jeff Johnson

My previous blog post disclosed a Mac sandbox escape. To save myself time and effort, I simply copy and pasted my original email to Apple Product Security. (After all, I'm not getting paid a bug bounty for my work!) This probably wasn't ideal for public consumption, because my email presumed a high level of knowledge on the subject possessed by Apple Product Security but not necessarily possessed by the general public. So I'm writing now to clarify a number of points that I feel have not been broadly understood.

  1. The key to the escape is removing the quarantine.
    When a sandboxed app writes a file, the file is quarantined, i.e., given a com.apple.quarantine extended attribute. As long as my sample shell script is quarantined, it cannot be executed. A sandboxed app cannot normally remove the quarantine from a file. However, the special com.apple.security.files.user-selected.executable entitlement possessed by TextEdit, as well as some other apps such as BBEdit and Transmit (remember how Apple made a big deal about them coming to the Mac App Store?), allows removal of the quarantine, thus enabling execution.
  2. The so-called "com.apple.security.files.user-selected.executable" entitlement requires no user selection.
    A sandboxed app with the com.apple.security.files.user-selected.executable entitlement, such as TextEdit, can remove the quarantine from a file without user consent. The app simply has to open a document and save it. This can occur programmatically, and it doesn't even require any edits to the document. If you're a developer you can test this yourself by creating a sample document-based application with the entitlement. Needless to say, I've tested this.
  3. The quarantine is removed silently.
    Even when you intentionally, manually save a file in TextEdit, you're not told that TextEdit can and does remove the quarantine. Most users have no idea that TextEdit has this power or how it works. And there's no way for users to find out except via the command line, e.g., codesign --display --entitlements - /Applications/TextEdit.app.
  4. Specially entitled apps can escape their own sandbox.
    TextEdit is sandboxed. As are BBEdit, Transmit, and most other (except legacy) App Store apps. Yet the com.apple.security.files.user-selected.executable entitlement allows a sandboxed app to escape the sandbox and execute non-sandboxed code. Effectively, this entitlement negates the sandbox. Any apps so entitled may be nominally sandboxed, but practically speaking, they're not sandboxed. You might say the entitlement makes a mockery of the sandbox. Furthermore, any "maliciously crafted document" vulnerability in these specially entitled apps that causes arbitrary code execution would also be a sandbox escape. Arbitrary code execution means a maliciously crafted document could cause the app to save a document, which means removing the quarantine from the document and allowing execution outside the sandbox. I personally haven't found any such vulnerabilities (though other security researchers might have!), which is why I investigated the method of controlling the app with another app via AppleScript.
  5. The "Apple Events" dialog is unrelated to the sandbox.
    When you run my sample app on Catalina or Mojave, you see a dialog that requests permission to control TextEdit:
    SandboxEscape.app wants access to control TextEdit.app.
    This dialog has nothing to do with the sandbox or the com.apple.security.files.user-selected.executable entitlement. It's part of the so-called macOS privacy protections introduced in Mojave and expanded in Catalina, and it appears for both sandboxed and non-sandboxed apps. You can see which apps have Apple Events permission by looking in System Preferences:
    Catalina, System Preferences, Privacy, Automation
    On macOS 10.13 High Sierra, in contrast, the Apple Events dialog does not appear. I said in my previous blog post that I had tested on Mojave and Catalina. Today I tweaked my Xcode project to work on High Sierra, and the sandbox escape happens there too, this time without any user permission requests at all. You can see that on High Sierra, the Automation section doesn't exist in System Preferences:
    High Sierra, System Preferences, Privacy
    Some people have asserted that my sandbox escape is not an issue because the user has given consent, but this is a misinterpretation of the situation. It's true that the user gives consent for one sandboxed app to control another sandboxed app. However, the Apple Events dialog gives no indication at all that the user is granting permission for the app to remove quarantine from files. This is neither expressed nor implied by the dialog. As I said earlier, most users don't even know that TextEdit has the power to remove quarantine, and certainly the Apple Events dialog doesn't inform them of this fact. Especially in Catalina, these permissions dialogs have become pervasive, which many people have commented on and complained about. Since many apps require Apple Events permissions in order to function as expected, users are likely to have become overwhelmed and simply click through to get their work done.
  6. My choice of /Users/Shared is also unrelated to the sandbox.
    To demonstrate that I achieved sandbox escape, I created a file in the folder /Users/Shared, which is not generally accessible to a sandboxed app. The reason I choose /Users/Shared rather than the user's Desktop or Documents folder is again because of macOS "privacy protections" in Catalina that apply to both sandboxed and non-sandboxed apps. Every app (unfortunately) in Catalina is restricted from writing to Desktop and Documents (among other folders) unless given special permission. Whereas the folder /Users/Shared is unrestricted in this sense. It's always been the case that a sandboxed app can only write to certain folders, but it's only recently been the case that non-sandboxed apps have also been restricted.
Jeff Johnson (My apps, PayPal.Me)