Skip to content

Instantly share code, notes, and snippets.

@osy
Created May 19, 2022 04:49
Show Gist options
  • Save osy/3c39b535ae8fe41ef268e85ea82c1aac to your computer and use it in GitHub Desktop.
Save osy/3c39b535ae8fe41ef268e85ea82c1aac to your computer and use it in GitHub Desktop.
Secure jailbreak infrastructure

Secure jailbreak infrastructure

  • Proposal: JBX-0001
  • Authors: osy
  • Status: draft

Introduction

The ecosystem of iOS jailbreaks has remained largely unchanged since iOS 1.0 even though the system internals of iOS has undergone several evolutions, enhancing security and enabling new features. iOS jailbreaks traditionally involves neutering a significant portion of the OS's security in order to enable unsigned code execution ("apps") and system modifications ("tweaks"). This allows malware to target jailbroken users and for misbehaving apps to corrupt data.

The traditional implementation of jailbreak (henceforth referred to as "legacy jailbreak") relies on modifications to the root partition, injecting code in all processes without discrimination, and managing installs through the Debian Package system (which is mostly used in the GNU/Linux ecosystem and was not designed for Darwin/XNU).

In iOS 15, the addition of System Signed Volume (SSV) necessitates jailbreaks to be "rootless". This change will break compatibility with many tweaks that depend on being installed in root with no sandbox. This gives us an opening to design a better jailbreak infrastructure. The purpose of this document is to lay down the groundwork for such a endeavor.

Motivation

The goals of this new jailbreak infrastructure is as follows:

  • Design a set of system level modifications and frameworks for the versatility and creativity of jailbreak apps and tweaks while also extending and complementing the security contract of iOS in order to maintain system security and stability.
  • Provide a way for users to verify and consent to access of system assets.
  • Extend the root of trust from Apple to Apple plus the user of the device. The user should be able to run anything they want on their own device. (As a corollary, code not explicitly trusted by the user should not run.)
  • The amount of modification done as part of the jailbreak install (as described by this document) should be minimal and only enough to provide a way to install, validate, and run tweaks and apps.

Note that we only define the programmer visible API for the infrastructure and leave out details for the implementation as that may vary from jailbreak to jailbreak.

Design

To the extent possible, we will reuse and modify existing system features in lieu of implementing new features. This means that we are largely relying on patching undocumented and private system interfaces that are known to change from version to version. The advantage though, is that we are better able to utilize existing (tested) code.

We mainly rely on two system level features:

  1. Entitlements allow us to define fine grained access control for an executable running in a sandbox environment.
  2. App Extensions allows for pre-defined extension points to execute third-party code in a sandbox. We design a new extension point at process activation time to allow third-party tweaks to inject code/data into the target process.

Code signing

In a stock iOS installation, Apple is the root of trust for all code running on the device. In a jailbreak environment, we add a new root (the "user signing certificate") that is controlled by the user of the device.

User signing certificate generation

To reduce the chance of user error, man-in-the-middle attacks, and other risks, the user signing certificate shall be generated on device. If the device is A7 or higher, the certificate should be generated in the Secure Enclave. The mechanism can be with the SecKeyCreateRandomKey function using the kSecAttrTokenIDSecureEnclave attribute (see here for more details). This will entangle the signing certificate with the per-device hardware key as well as the user passcode and measurements of the currently running OS. This will ensure that the signed executable extracted from one device cannot be used to attack another user's device.

The generation of the user signing certificate shall be done as part of the initial jailbreak installation process. Once the jailbreak installation is completed, the installer should block attempts to reinstall in an already jailbroken state in order to mitigate against an attacker from using the jailbreak installer as an attack vector to bypass user-signing.

Installer application

The installer application is an iOS application that manages jailbreak tweaks and apps. It shall hold two entitlements (details in the following section): jailbreak.installer.user-code-sign (set to true) and com.apple.private.mobileinstall.allowedSPI (set to Install).

Packages are in the standard Apple IPA format.

The installer application shall display any entitlements used by the app (and its embedded bundles) outside of a predefined allow-list to the user before proceeding. The GUI should attempt to provide a user-readable description of each entitlement used. The allow-list should be defined by the developer of the installer application.

The installer application shall block, provide an alert separate from the entitlement verification, or otherwise require some extra action taken by the user to consent to the installation of any app that has the com.apple.private.mobileinstall.allowedSPI entitlement with the exception of an update to the installer application itself which should be validated in an implementation defined means. This is to detect malware from masquerading as an installer application.

The installer application should optionally perform some sort of verification of packages in an implementation defined method. This can be, for example, a GPG system where each IPA has an associated signature file.

After user consent, the installer application shall sign or resign the executable in the IPA along with any bundles by interfacing with Jailbreak.framework. The installer application should keep the entitlements when re-signing.

The installer application shall use the MobileInstallation.framework interfaces to install the resigned IPA package.

Uninstallation can be performed either by the installation application or by the user via standard system means.

Installer bootstrapping

The installer application must be signed with the user signing certificate. This can be done at jailbreak installation time (after the certificate generation) or through a "sign on first launch" of the installer application. In the second option, care must be taken to ensure that unsigned code execution happens exactly once and only for the installer application.

Additional Entitlements

In addition to system defined entitlements, the following entitlements are defined:

jailbreak.installer.user-code-sign
  • Key: jailbreak.installer.user-code-sign
  • Type: Boolean

If true, this process can use Jailbreak.framework to sign code with the user signing certificate.

jailbreak.extension.install-process-hook
  • Key: jailbreak.extension.install-process-hook
  • Type: Boolean

If true, this process can use Jailbreak.framework to install a new process hook App Extension.

jailbreak.process-modify-data.allowed-identifiers
  • Key: jailbreak.process-modify-data.allowed-identifiers
  • Type: Array of strings

An array of bundle identifiers whose address space is allowed by this process to modify read/write data.

jailbreak.process-modify-code.allowed-identifiers
  • Key: jailbreak.process-modify-code.allowed-identifiers
  • Type: Array of strings

An array of bundle identifiers whose address space is allowed by this process to modify read-only data and code. Note: not all jailbreaks may support this entitlement.

Jailbreak Framework

A new system framework, Jailbreak.framework, shall be created to provide an interface for user code-signing and implementing modifications of system and third party applications (tweaks). The framework shall work through an XPC interface that can be considered as privileged as any system daemon. The jailbreak installation and/or activation shall ensure that the user signing certificate private key can only be used by this XPC process and that the framework must be immutable. It is part of the root-of-trust in a jailbroken device.

JBProcessHookInstaller

This class shall provide an XPC interface to register and enable the App Extension bundle enclosed in the calling application as a Process Hook Extension. The jailbreak.extension.install-process-hook entitlement shall be checked.

JBProcessHookExtension

This protocol should be implemented by the App Extension code to handle the tweak's activation points. The NSExtensionPointIdentifier shall be jailbreak.extension.process-hook. Additional details of this interface is TBD but it should provide an activation function that returns a reference to an object that can be used to inject code/data into a target process that it is allowed to hook as defined by the jailbreak.process-modify-data.allowed-identifiers and jailbreak.process-modify-code.allowed-identifiers entitlements.

A compatibility layer can be implemented to support Substrate or libhooker with the limitation that only the processes defined in the entitlement can be hooked.

Note that data in the extension can be shared with the application containing the extension through group containers.

JBCodeSigning

This class shall provide an XPC interface to use the user code-signing certificate to sign an executable. It shall verify that the jailbreak.installer.user-code-sign entitlement is held by the caller process.

jailbreakd

This should not be confused with any existing jailbreakd. In this implementation of jailbreakd, an XPC interface shall be defined to implement the functionality defined in the preceding sections. Additionally, it shall activate process hooks installed by the JBProcessExtension interface.

Upon process creation, jailbreakd shall identify any Process Hook Extensions that are currently registered and launch those extensions as separate processes. The activation point of those executable should call back into jailbreakd (through an XPC interface abstracted by Jailbreak.framework) to perform the actual process patching.

When a process crashes with a process hook enabled, any App Extension that hooks the process should be disabled. There may be a user configurable option to change the behavior to only work on platform executables. This removes the need for a "safe mode."

When the enclosing application of a Process Hook Extension is deleted by the user, the hook should automatically be unregistered and not be loaded the next time the target process is launched.

It is also jailbreakd's responsibility to patch MISValidateSignatureAndCopyInfo (or a similar function) to accept the user code signing certificate in addition to the Apple code signing certificate for purposes of verifying an executable.

Acknowledgments

Thanks to the nouveau proposal for inspirations.

@saagarjha
Copy link

(As discussed externally) Some issues I see is that most jailbreaks don't provide the necessary primitives to be able to support this kind of thing; they're mostly rushed to be released as as fast as possible, with as few bypasses as possible, held together with duct tape and glue. Designing a good system on top of this is going to be difficult, especially as the limitations change from jailbreak to jailbreak (some give you a kernel R/W primitive, some let you grab an arbitrary task port, etc.). In addition, there's really no incentive for a jailbreak developer to support your solution over the one they designed themselves because they don't trust you or want to integrate with your code.

More fundamentally, though, the whole notion of entitlements as used here fails to really take into account what entitlements are really not particularly appropriate for the kind of code injection usecase you're presenting here. Injecting into a process means you get at least the privileges it has, assuming the jailbreak is designed well (many are not and patch out all security checks, see above–but this is outside of the scope of this particular point). A tweak is really like a library (well, it literally is a library) so it doesn't make sense to give it entitlements any more than it makes sense to give a library entitlements, and in fact on Darwin if you codesign a library with entitlements they're just discarded when you inject into a process. The only things you'd actually want is "this tweak should only be loaded into these processes" and "once loaded into those processes the tweak should not be able to take over anything else". The former is already solved by Substrate's plist file, which indicates which processes should be targeted, and the latter ideally by the jailbreak not messing with the platform sandbox.

@NSAntoine
Copy link

NSAntoine commented May 21, 2022

I'd like to add on to the idea of user consent in this context: Root Authorization

Many applications related to, or reliant on Jailbreaking will use what's often known as a "Root Helper", such as Sileo's GiveMeRoot, or Zebra's Supersling, basically helpers that elevate themselves to Root status by setting their UID and GID to 0 using setuid and getuid, which are then usually used to run commands which require root for the client application.

Behind the scenes, how this works is: jailbreakd will usually listen for setuid and setgid calls, and when one is encountered, jailbreakd overwrites the process' credentials structure in kernel memory (cc @jamiebishop), this is done without the user knowing at all, all that's needed is simply to chmod 6755 the executable of the helper, then a setuid(0) and a setgid(0) call.

on macOS, Security.framework has Authorization Services, which allows processes to gain certain privileges by asking the user to enter in their information, ie:
image

I think a better way for jailbreakd to intercept setuid(0) / setgid(0) calls would be to instead prompt an alert to the user asking if they want to allow the client to gain UID / GID 0 status, then, if the user chose yes, grant the process the UID / GID 0, otherwise, deny the process access to root and set errno to EPERM (that's how setuid and setgid fail, usually)

For users to avoid redundancy, I think it would also be best to include options of "Always authorize" or "Always deny", which I hope are self explanatory. Of course, I think this should just be a feature that could be toggled on & off, though I think it would be better to have a feature like this toggled on by default, considering who most of the jailbreak crowd are

Though some issues may arise with this, ie, malicious processes may start using other ways to obtain root (injecting, for example), which.. I don't know how such an issue would be circumvented, or if it would be practical to do so at all, with that being said, I still think the idea above would be for the better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment