Friday, February 1, 2019

Examining Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone XS

Posted by Brandon Azad, Project Zero

In this post I examine Apple's implementation of Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 SoC used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone XS, with a focus on how Apple has improved over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM standard. I cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n demonstrate a way to use an arbitrary kernel read/write primitive to forge kernel PAC signatures for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys, which is sufficient to execute arbitrary code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel using JOP. The technique I discovered was (mostly) fixed in iOS 12.1.3. In fact, this fix first appeared in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 16D5032a beta while my research was still ongoing.

ARMv8.3-A Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication

Among cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most exciting security features introduced with ARMv8.3-A is Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication, a feature where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper bits of a pointer are used to store a Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication Code (PAC), which is essentially a cryptographic signature on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer value and some additional context. Special instructions have been introduced to add an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code to a pointer and to verify an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointer's PAC and restore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original pointer value. This gives cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system a way to make cryptographically strong guarantees about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 likelihood that certain pointers have been tampered with by attackers, which offers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 possibility of greatly improving application security.

(Proper terminology dictates that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 security feature is called Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cryptographic signature that is inserted into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unused bits of a pointer is called cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication Code, or PAC. However, popular usage has already confused cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se terms, and it is common to see Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication referred to as PAC. Usually this usage is unambiguous, so for brevity I will often refer to Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication as PAC as well.)

There are many great articles describing Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication, so I'll only go over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rough details here. Interested readers can refer to Qualcomm's whitepaper, Mark Rutland's slides from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 2017 Linux Security Summit, this LWN article by Jonathan Corbet, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM A64 Instruction Set Architecture for furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r details.

The key insight that makes Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication viable is that, although pointers are 64 bits, most systems have a virtual address space that is much smaller, which leaves unused bits in a pointer that can be used to store additional data. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case of Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bits will be used to store a short aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code over both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original 64-bit pointer value and a 64-bit context value.

Systems are allowed to use an implementation-defined algorithm to compute PACs, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard recommends cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use of a block cipher called QARMA. According to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whitepaper, QARMA is "a new family of lightweight tweakable block ciphers" designed specifically for pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication. QARMA-64, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 variant used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard, takes as input a secret 128-bit key, a 64-bit plaintext value (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer), and a 64-bit tweak (cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context), and produces as output a 64-bit ciphertext. The truncated ciphertext becomes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC that gets inserted into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unused extension bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer.

The architecture provides for 5 secret 128-bit Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication keys. Two of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se keys, APIAKey and APIBKey, are used for instruction pointers. Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r two, APDAKey and APDBKey, are used for data pointers. And cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 last key, APGAKey, is a special "general" key that is used for signing larger blocks of data with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACGA instruction. Providing multiple keys allows for some basic protection against pointer substitution attacks, in which one aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointer is substituted with anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r.

The values of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se keys are set by writing to special system registers. The registers containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication keys are inaccessible from EL0, meaning that a userspace process cannot read or change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hardware provides no ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r key management features: it's up to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code running at each exception level to manage cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next lower exception level.

ARMv8.3-A introduces three new categories of instructions for dealing with PACs:

  • PAC* instructions generate and insert cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension bits of a pointer. For example, PACIA X8, X9 will compute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer in register X8 under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-instruction key, APIAKey, using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value in X9 as context, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n write cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting PAC'd pointer back in X8. Similarly, PACIZA is like PACIA except cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 context value is fixed to 0.
  • AUT* instructions verify a pointer's PAC (along with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64-bit context value). If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC is valid, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC is replaced with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original extension bits. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC is invalid (indicating that this pointer was tampered with), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n an error code is placed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer's extension bits so that a fault is triggered if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer is dereferenced. For example, AUTIA X8, X9 will verify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC'd pointer in X8 under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-instruction key using X9 as context, writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 valid pointer back to X8 if successful and writing an invalid value ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise.
  • XPAC* instructions remove a pointer's PAC and restore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original value without performing verification.

In addition to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se general Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication instructions, a number of specialized variants were introduced to combine Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication with existing operations:

  • BLRA* instructions perform a combined aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate-and-branch operation: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer is validated and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n used as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 branch target for BLR. For example, BLRAA X8, X9 will aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC'd pointer in X8 under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-instruction key using X9 as context and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n branch to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting address.
  • LDRA* instructions perform a combined aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate-and-load operation: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer is validated and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n data is loaded from that address. For example, LDRAA X8, X9 will validate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC'd pointer X9 under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-data key using a context value of 0 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n load cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64-bit value at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting address into X8.
  • RETA* instructions perform a combined aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticate-and-return operation: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 link register LR is validated and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n RET is performed. For example, RETAB will verify LR using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-instruction key and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n return.

A known limitation: signing gadgets

Before we start our analysis of PAC, I should mention a known limitation: PAC can be bypassed if an attacker with read/write access can coerce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system into executing a signing gadget. Signing gadgets are instruction sequences that can be used to sign arbitrary pointers. For example, if an attacker can trigger cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution of a function that reads a pointer from memory, adds a PAC, and writes it back, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can use this function as a signing oracle to forge PACs for arbitrary pointers.

Weaknesses against kernel attackers

As discussed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Qualcomm whitepaper, ARMv8.3 Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication was designed to provide some protection even against attackers with arbitrary memory read or arbitrary memory write capabilities. But it's important to understand cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 limitations of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 design under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack model we're considering: a kernel attacker who already has read/write and is looking to execute arbitrary code by forging PACs on kernel pointers.

Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 specification, I identified three potential weaknesses in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 design when protecting against kernel attackers with read/write: reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys from memory, signing kernel pointers in userspace, and signing A-key pointers using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-key (or vice versa). We'll discuss each in turn.

Reading PAC keys from kernel memory

First let's consider what is perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most obvious type of attack: just reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys from kernel memory and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n manually computing PACs for arbitrary kernel pointers. Here's an excerpt from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subsection of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whitepaper on attackers who can read arbitrary memory:

Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication is designed to resist memory disclosure attacks. The PAC is computed using a cryptographically strong algorithm, so reading any number of aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers from memory would not make it easier to forge pointers.

The keys are stored in processor registers, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se registers are not accessible from usermode (EL0). Therefore, a memory disclosure vulnerability would not help extract cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys used for PAC generation.

While true, this description applies specifically to attacking a userspace program, not attacking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel itself. Recent iOS devices do not appear to be running a hypervisor (EL2) or secure monitor (EL3), meaning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel running at EL1 must manage its own PAC keys. And since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system registers that store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m during normal operation will be cleared when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core goes to sleep, this means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys must at some point be stored in kernel memory. Thus an attacker with kernel memory access could probably read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys and use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to manually compute aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication codes for arbitrary pointers.

Of course, this approach assumes that we know what algorithm is being used under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood to generate PACs so that we can implement it ourselves in userspace. Knowing Apple, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's a good chance cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're use a custom algorithm in place of QARMA. If that's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n knowing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys wouldn't be sufficient to forge PACs: eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r we'd have to reverse engineer cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 silicon and determine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 algorithm, or we'd have to find a way to reuse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing machinery to forge pointers on our behalf.

Cross-EL PAC forgeries

Along cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latter line of analysis, one possible way to do that would be to forge PACs for kernel pointers by executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 corresponding PAC* instructions in userspace. While this may sound naive, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are a few reasons this could work.

While unlikely, it's possible that Apple has decided to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same PAC keys for EL0 and EL1, in which case we could forge a kernel PACIA signature (for example) by literally executing a PACIA instruction on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel pointer from userspace. You can see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM pseudocode describing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of PAC* instructions makes no distinction between whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r this instruction was executed at EL0 or EL1.

Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pseudocode for AddPACIA(), which describes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of PACIA-like instructions:

// AddPACIA()
// ==========
// Returns a 64-bit value containing X, but replacing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer
// aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code field bits with a pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
// pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code is derived using a cryptographic algorithm as a
// combination of X, Y, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APIAKey_EL1.

bits(64) AddPACIA(bits(64) X, bits(64) Y)
   boolean TrapEL2;
   boolean TrapEL3;
   bits(1)  Enable;
   bits(128) APIAKey_EL1;

   APIAKey_EL1 = APIAKeyHi_EL1<63:0>:APIAKeyLo_EL1<63:0>;

   case PSTATE.EL of
       when EL0
           boolean IsEL1Regime = S1TranslationRegime() == EL1;
           Enable = if IsEL1Regime cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n SCTLR_EL1.EnIA else SCTLR_EL2.EnIA;
           TrapEL2 = (EL2Enabled() && HCR_EL2.API == '0' &&
                      (HCR_EL2.TGE == '0' || HCR_EL2.E2H == '0'));
           TrapEL3 = HaveEL(EL3) && SCR_EL3.API == '0';
       when EL1
           Enable = SCTLR_EL1.EnIA;
           TrapEL2 = EL2Enabled() && HCR_EL2.API == '0';
           TrapEL3 = HaveEL(EL3) && SCR_EL3.API == '0';
       ...

   if Enable == '0' cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n return X;
   elsif TrapEL2 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n TrapPACUse(EL2);
   elsif TrapEL3 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n TrapPACUse(EL3);
   else return AddPAC(X, Y, APIAKey_EL1, FALSE);

And here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pseudocode implementation of AddPAC():

// AddPAC()
// ========
// Calculates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code for a 64-bit quantity and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
// inserts that into pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code field of that 64-bit quantity.

bits(64) AddPAC(bits(64) ptr, bits(64) modifier, bits(128) K, boolean data)
   bits(64) PAC;
   bits(64) result;
   bits(64) ext_ptr;
   bits(64) extfield;
   bit selbit;
   boolean tbi = CalculateTBI(ptr, data);
   integer top_bit = if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n 55 else 63;

   // If tagged pointers are in use for a regime with two TTBRs, use bit<55> of
   // cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer to select between upper and lower ranges, and preserve this.
   // This handles cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 awkward case where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is apparently no correct
   // choice between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper and lower address range - ie an addr of
   // 1xxxxxxx0... with TBI0=0 and TBI1=1 and 0xxxxxxx1 with TBI1=0 and
   // TBI0=1:
   if PtrHasUpperAndLowerAddRanges() cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       ...
   else selbit = if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n ptr<55> else ptr<63>;

   integer bottom_PAC_bit = CalculateBottomPACBit(selbit);

   // The pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code field takes all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 available bits in
   // between
   extfield = Replicate(selbit, 64);

   // Compute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code for a ptr with good extension bits
   if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       ext_ptr = ptr<63:56>:extfield<(56-bottom_PAC_bit)-1:0>:ptr;
   else
       ext_ptr = extfield<(64-bottom_PAC_bit)-1:0>:ptr;

   PAC = ComputePAC(ext_ptr, modifier, K<127:64>, K<63:0>);

   // Check if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ptr has good extension bits and corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer
   // aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code if not;
   if !IsZero(ptr) && !IsOnes(ptr) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       PAC = NOT(PAC);

   // Preserve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 determination between upper and lower address at bit<55>
   // and insert PAC
   if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       result = ptr<63:56>:selbit:PAC<54:bottom_PAC_bit>:ptr;
   else
       result = PAC<63:56>:selbit:PAC<54:bottom_PAC_bit>:ptr;
   return result;

Operationally, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are no significant differences between executing PACIA at EL0 and EL1, which means that if Apple has used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same PAC keys for both exception levels, we can simply execute PACIA in userspace to sign kernel pointers.

Of course, it seems highly unlikely that Apple has left such an obvious hole in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir implementation. Even so, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 symmetry between EL0 and EL1 means that we could potentially forge kernel PACIA signatures by reading cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's PAC keys, replacing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace PAC keys for one thread in our process with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel PAC keys, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we could indeed forge kernel pointers by executing PACIA in userspace in that thread. This would be useful if Apple is using an unknown algorithm in place of QARMA, since we could reuse cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing signing machinery without having to reverse engineer it.

Cross-key PAC forgeries

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r symmetry that we could potentially leverage to produce PAC forgeries is between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 different PAC keys: PACIA, PACIB, PACDA, and PACDB all reduce to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same implementation under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood, just using different keys. Thus, if we can replace one PAC key with anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, we can turn signing gadgets for one key into signing gadgets for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r key.

This would be useful if, for example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC algorithm is unknown and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is something that prevents us from setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace PAC keys equal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel PAC keys so that we can perform cross-EL forgeries. While this forgery strategy is much less powerful, since we'd need to rely on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existence of PAC signing gadgets (which are a known limitation of PAC), this technique would free us from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 restriction that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signing gadget use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same key that we're trying to forge, potentially diversifying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 set of available gadgets.

Finding an entry point for kernel code execution

Now that we have some cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365oretical ideas of how we might try and defeat PAC on A12 devices, let's look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r end and figure out how we could use a PAC bypass to execute arbitrary code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel.

The traditional way to get kernel code execution via read/write is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iokit_user_client_trap() strategy described by Stefan Esser in Tales from iOS 6 Exploitation. This strategy involves patching cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable of an IOUserClient instance so that calling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace function IOConnectTrap6(), which invokes iokit_user_client_trap() in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, will call an arbitrary function with up to 7 arguments. To see why this works, here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 implementation of iokit_user_client_trap() from XNU 4903.221.2:

kern_return_t iokit_user_client_trap(struct iokit_user_client_trap_args *args)
{
   kern_return_t result = kIOReturnBadArgument;
   IOUserClient *userClient;

   if ((userClient = OSDynamicCast(IOUserClient,
           iokit_lookup_connect_ref_current_task((mach_port_name_t)
               (uintptr_t)args->userClientRef)))) {
       IOExternalTrap *trap;
       IOService *target = NULL;

       trap = userClient->getTargetAndTrapForIndex(&target, args->index);

       if (trap && target) {
           IOTrap func;

           func = trap->func;

           if (func) {
               result = (target->*func)(args->p1, args->p2, args->p3,
                                        args->p4, args->p5, args->p6);
           }
       }

       iokit_remove_connect_reference(userClient);
   }

   return result;
}

If we can patch cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOUserClient instance such that getTargetAndTrapForIndex() returns controlled values for trap and target, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 invocation of target->func below will call an arbitrary kernel function with up to 7 controlled arguments (target plus p1 through p6).

To see how this strategy would work on A12 devices, let's examine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 changes to this function introduced by PAC. This is easiest to understand by looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 disassembly:

iokit_user_client_trap
   PACIBSP
   ...        ;; Call iokit_lookup_connect_ref_current_task() on
   ...        ;; args->userClientRef and cast cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result to IOUserClient.

loc_FFFFFFF00808FF00
   STR        XZR, [SP,#0x30+var_28]  ;; target = NULL
   LDR        X8, [X19]               ;; x19 = userClient, x8 = ->vtable
   AUTDZA     X8                      ;; validate vtable's PAC
   ADD        X9, X8, #0x5C0          ;; x9 = pointer to vmethod in vtable
   LDR        X8, [X8,#0x5C0]         ;; x8 = vmethod getTargetAndTrapForIndex
   MOVK       X9, #0x2BCB,LSL#48      ;; x9 = 2BCB`vmethod_pointer
   LDR        W2, [X20,#8]            ;; w2 = args->index
   ADD        X1, SP, #0x30+var_28    ;; x1 = &target
   MOV        X0, X19                 ;; x0 = userClient
   BLRAA      X8, X9                  ;; PAC call ->getTargetAndTrapForIndex
   LDR        X9, [SP,#0x30+var_28]   ;; x9 = target
   CMP        X0, #0
   CCMP       X9, #0, #4, NE
   B.EQ       loc_FFFFFFF00808FF84    ;; if !trap || !target
   LDP        X8, X11, [X0,#8]        ;; x8 = trap->func, x11 = func virtual?
   AND        X10, X11, #1
   ORR        X12, X10, X8
   CBZ        X12, loc_FFFFFFF00808FF84       ;; if !func
   ADD        X0, X9, X11,ASR#1       ;; x0 = target
   CBNZ       X10, loc_FFFFFFF00808FF58
   MOV        X9, #0                  ;; Use context 0 for non-virtual func
   B          loc_FFFFFFF00808FF70

loc_FFFFFFF00808FF58
   ...        ;; Handle cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case where trap->func is a virtual method.

loc_FFFFFFF00808FF70
   LDP        X1, X2, [X20,#0x10]     ;; x1 = args->p1, x2 = args->p2
   LDP        X3, X4, [X20,#0x20]     ;; x3 = args->p3, x4 = args->p4
   LDP        X5, X6, [X20,#0x30]     ;; x5 = args->p5, x6 = args->p6
   BLRAA      X8, X9                  ;; PAC call func(target, p1, ..., p6)
   MOV        X21, X0

loc_FFFFFFF00808FF84
   ...        ;; Call iokit_remove_connect_reference().

loc_FFFFFFF00808FF8C
   ...        ;; Epilogue.
   RETAB

As you can see, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are several places where PACs are aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated. The first, which was omitted from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 assembly for brevity, happens when performing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dynamic cast to IOUserClient. Then userClient's vtable is validated and a PAC-protected call to getTargetAndTrapForIndex() is made. After that, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 trap->func field is read without validation, and finally cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value func is validated with context 0 and called.

This is actually about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 best case we could reasonably hope for as attackers. If we can find a legitimate user client that provides an implementation of getTargetAndTrapForIndex() that returns a pointer to an IOExternalTrap residing in writable memory, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n all we have to do is replace trap->func with a PACIZA'd function pointer (that is, a pointer signed under APIAKey with context 0). That means only a partial PAC bypass, such as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to forge just PACIZA pointers, would be sufficient.

A quick search through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache revealed a unique IOUserClient class, IOAudio2DeviceUserClient, that fit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se criteria. Here's a decompilation of its getTargetAndTrapForIndex() method:

IOExternalTrap *IOAudio2DeviceUserClient::getTargetAndTrapForIndex(
       IOAudio2DeviceUserClient *this, IOService **target, unsigned int index)
{
   ...
   *target = (IOService *)this;
   return &this->IOAudio2DeviceUserClient.traps[index];
}

The traps field is initialized in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method IOAudio2DeviceUserClient::initializeExternalTrapTable() to a heap-allocated IOExternalTrap object:

this->IOAudio2DeviceUserClient.trap_count = 1;
this->IOAudio2DeviceUserClient.traps = IOMalloc(sizeof(IOExternalTrap));

Thus, all we need to do to call an arbitrary kernel function is create our own IOAudio2DeviceUserClient connection, forge a PACIZA pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function we want to call, overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userClient->traps[0].func field with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA'd pointer, and invoke IOConnectTrap6() from userspace. This will give us control of all arguments except X0, which is explicitly set to this by IOAudio2DeviceUserClient's implementation of getTargetAndTrapForIndex().

To gain control of X0 alongside X1 through X6, we'll need to replace IOAudio2DeviceUserClient's implementation of getTargetAndTrapForIndex() in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vtable. This means that, in addition to forging cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function we want to call, we'll also need to create a fake vtable consisting of PACIA'd pointers to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual methods, and we'll need to replace cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing vtable pointer with a PACDZA'd pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake vtable. This requires a significantly broader PAC forgery capability.

However, even if we only manage to produce PACIZA forgeries, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's still a way to gain control of X0: JOP gadgets. A quick search through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache revealed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following gadget that sets X0:

MOV         X0, X4
BR          X5

This gives us a way to call arbitrary kernel functions with 4 fully controlled arguments using just a single forged pointer: use iokit_user_client_trap() to call a PACIZA'd pointer to this gadget with X1 through X3 set how we want cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function call, X4 set to our desired value for X0, and X5 set to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target function we want to call.

Analyzing PAC on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12

Now that we know how we can use PAC forgery to call arbitrary kernel functions, let's begin analyzing Apple's implementation of PAC on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 SoC for weaknesses. Ideally we'll find a way to perform both PACIA and PACDA forgeries, but as previously discussed, even cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to forge a single PACIZA pointer will be sufficient to call arbitrary kernel functions with up to 4 arguments.

To actually perform my analysis, I used cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 voucher_swap exploit to get kernel read/write on an iPhone XR running iOS 12.1.1 build 16C50.

Finding where PAC keys are set

My first step was to identify where in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's code cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys were being set. Unfortunately, IDA does not display names for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 special registers used to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys, so I had to do a bit of digging.

Searching for "APIAKey" in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 LLVM repository mirror on GitHub revealed that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registers used to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APIAKey are called APIAKeyLo_EL1 and APIAKeyHi_EL1, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registers for ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r keys are similarly named. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file AArch64SystemOperands.td declares cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 codes for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se registers. This allows us to easily search for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se registers in IDA. For example, to find where APIAKeyLo_EL1 is set, I searched for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 string "#0, c2, c1, #0". This brought me to what I identified as part of common_start, from osfmk/arm64/start.s:

_WriteStatusReg(TCR_EL1, sysreg_restore);               // 3, 0, 2, 0, 2
PPLTEXT__set__TTBR0_EL1(x25 & 0xFFFFFFFFFFFF);
_WriteStatusReg(TTBR1_EL1, (x25 + 0x4000) & 0xFFFFFFFFFFFF);    // 3, 0, 2, 0, 1
_WriteStatusReg(MAIR_EL1, 0x44F00BB44FF);               // 3, 0, 10, 2, 0
if ( x21 )
   _WriteStatusReg(TTBR1_EL1, cpu_ttep);               // 3, 0, 2, 0, 1
_WriteStatusReg(VBAR_EL1, ExceptionVectorsBase + x22 - x23);    // 3, 0, 12, 0, 0
do
   x0 = _ReadStatusReg(S3_4_C15_C0_4);                 // ????
while ( !(x0 & 2) );
_WriteStatusReg(S3_4_C15_C0_4, x0 | 5);                 // ????
__isb(0xF);
_WriteStatusReg(APIBKeyLo_EL1, 0xFEEDFACEFEEDFACF);     // 3, 0, 2, 1, 2
_WriteStatusReg(APIBKeyHi_EL1, 0xFEEDFACEFEEDFACF);     // 3, 0, 2, 1, 3
_WriteStatusReg(APDBKeyLo_EL1, 0xFEEDFACEFEEDFAD0);     // 3, 0, 2, 2, 2
_WriteStatusReg(APDBKeyHi_EL1, 0xFEEDFACEFEEDFAD0);     // 3, 0, 2, 2, 3
_WriteStatusReg(S3_4_C15_C1_0, 0xFEEDFACEFEEDFAD1);     // ????
_WriteStatusReg(S3_4_C15_C1_1, 0xFEEDFACEFEEDFAD1);     // ????
_WriteStatusReg(APIAKeyLo_EL1, 0xFEEDFACEFEEDFAD2);     // 3, 0, 2, 1, 0
_WriteStatusReg(APIAKeyHi_EL1, 0xFEEDFACEFEEDFAD2);     // 3, 0, 2, 1, 1
_WriteStatusReg(APDAKeyLo_EL1, 0xFEEDFACEFEEDFAD3);     // 3, 0, 2, 2, 0
_WriteStatusReg(APDAKeyHi_EL1, 0xFEEDFACEFEEDFAD3);     // 3, 0, 2, 2, 1
_WriteStatusReg(APGAKeyLo_EL1, 0xFEEDFACEFEEDFAD4);     // 3, 0, 2, 3, 0
_WriteStatusReg(APGAKeyHi_EL1, 0xFEEDFACEFEEDFAD4);     // 3, 0, 2, 3, 1
_WriteStatusReg(SCTLR_EL1, 0xFC54793D);                 // 3, 0, 1, 0, 0
__isb(0xF);
_WriteStatusReg(CPACR_EL1, 0x300000);                   // 3, 0, 1, 0, 2
_WriteStatusReg(TPIDR_EL1, 0);                          // 3, 0, 13, 0, 4

This is very interesting, since it looks like common_start sets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys to constant values every time a core starts up! Thinking that perhaps this was an artifact of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decompilation, I checked cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 disassembly:

common_start+A8
   LDR        X0, =0xFEEDFACEFEEDFACF ;; x0 = pac_key
   MSR        #0, c2, c1, #2, X0      ;; APIBKeyLo_EL1
   MSR        #0, c2, c1, #3, X0      ;; APIBKeyHi_EL1
   ADD        X0, X0, #1
   MSR        #0, c2, c2, #2, X0      ;; APDBKeyLo_EL1
   MSR        #0, c2, c2, #3, X0      ;; APDBKeyHi_EL1
   ADD        X0, X0, #1
   MSR        #4, c15, c1, #0, X0     ;; ????
   MSR        #4, c15, c1, #1, X0     ;; ????
   ADD        X0, X0, #1
   MSR        #0, c2, c1, #0, X0      ;; APIAKeyLo_EL1
   MSR        #0, c2, c1, #1, X0      ;; APIAKeyHi_EL1
   ADD        X0, X0, #1
   MSR        #0, c2, c2, #0, X0      ;; APDAKeyLo_EL1
   MSR        #0, c2, c2, #1, X0      ;; APDAKeyHi_EL1
...
pac_key
   DCQ 0xFEEDFACEFEEDFACF      ; DATA XREF: common_start+A8↑r

No, common_start really was initializing all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys to constant values. This was quite surprising: clearly Apple knows that using constant PAC keys breaks all of PAC's security guarantees. So I figured cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re must be some ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r place cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys were being initialized to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir true runtime values.

But after much searching, this appeared to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only location in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache that was setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 general key. Still, it did appear that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B keys were being set in a few more places:

machine_load_context+A8
   LDR        X1, [X0,#0x458]
   ...
   MSR        #0, c2, c1, #2, X1      ;; APIBKeyLo_EL1
   MSR        #0, c2, c1, #3, X1      ;; APIBKeyHi_EL1
   ADD        X1, X1, #1
   MSR        #0, c2, c2, #2, X1      ;; APDBKeyLo_EL1
   MSR        #0, c2, c2, #3, X1      ;; APDBKeyHi_EL1

Call_continuation+10
   LDR        X5, [X4,#0x458]
   ...
   MSR        #0, c2, c1, #2, X5      ;; APIBKeyLo_EL1
   MSR        #0, c2, c1, #3, X5      ;; APIBKeyHi_EL1
   ADD        X5, X5, #1
   MSR        #0, c2, c2, #2, X5      ;; APDBKeyLo_EL1
   MSR        #0, c2, c2, #3, X5      ;; APDBKeyHi_EL1

Switch_context+11C
   LDR        X3, [X2,#0x458]
   ...
   MSR        #0, c2, c1, #2, X3      ;; APIBKeyLo_EL1
   MSR        #0, c2, c1, #3, X3      ;; APIBKeyHi_EL1
   ADD        X3, X3, #1
   MSR        #0, c2, c2, #2, X3      ;; APDBKeyLo_EL1
   MSR        #0, c2, c2, #3, X3      ;; APDBKeyLo_EL1

Idle_load_context+88
   LDR        X1, [X0,#0x458]
   ...
   MSR        #0, c2, c1, #2, X1      ;; APIBKeyLo_EL1
   MSR        #0, c2, c1, #3, X1      ;; APIBKeyHi_EL1
   ADD        X1, X1, #1
   MSR        #0, c2, c2, #2, X1      ;; APDBKeyLo_EL1
   MSR        #0, c2, c2, #3, X1      ;; APDBKeyHi_EL1

These are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r places in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel that set PAC keys, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y all follow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same pattern: a 64-bit load from offset 0x458 into some data structure (later identified as struct thread), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APIBKey to that value concatenated with itself, and setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 APDBKey to that value plus one concatenated with itself.

Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, all of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se locations deal specifically with context switching between threads; conspicuously absent from this list is any indication that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys are changed when transitioning between exception levels, eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r on kernel entry (e.g. via a syscall) or on kernel exit (via ERET*). This would be a strong indication that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys are indeed shared between userspace and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel.

(I subsequently learned that @ProteasWang discovered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same thing I did: a GitHub gist called pac-set-key.md lists only cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previously mentioned locations.)

If my understanding was correct, this seemed to suggest three disturbing and, frankly, highly unlikely things. First, contrary to all rules of cryptography, it appeared that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel was using constant values for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 general key. Second, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys seemed to be effectively 64-bits, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first and second halves of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 128-bit key are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same. And third, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys appeared to be shared between userspace and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, meaning userspace could forge kernel PAC signatures. Could Apple's implementation really be that broken? Or was something else going on?

Observing runtime behavior

In order to find out, I conducted a simple experiment: I read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of a global PACIZA'd function pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __DATA_CONST.__const section over many different boots, recording cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kASLR slide each time. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 number of possible kernel slide values is relatively small, it shouldn't be too long before I get two separate boots with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exact same location in memory, meaning that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original, non-PAC'd value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer would be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same both times. Then, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys really are constant, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA'd pointer should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same in both boots, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signing algorithm is deterministic and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer and context values being signed are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same both times.

As a target, I chose to read sysclk_ops.c_gettime, which is a pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function rtclock_gettime(). The results of this experiment over 30 trials are listed below, with colliding runs highlighted:

slide = 000000000ce00000, c_gettime = b2902c70147f2050
slide = 0000000023200000, c_gettime = 61e2c2f02abf2050
slide = 0000000023000000, c_gettime = d98e57f02a9f2050
slide = 0000000006e00000, c_gettime = 0b9613700e7f2050
slide = 000000001ce00000, c_gettime = c3822bf0247f2050
slide = 0000000004600000, c_gettime = 00d248f00bff2050
slide = 000000001fe00000, c_gettime = 6aa61ef0277f2050
slide = 0000000013400000, c_gettime = fda847701adf2050
slide = 0000000015a00000, c_gettime = c5883b701d3f2050
slide = 000000000a200000, c_gettime = bbe37ef011bf2050
slide = 0000000014200000, c_gettime = a8ff9f701bbf2050
slide = 0000000014800000, c_gettime = 20e538701c1f2050
slide = 0000000019800000, c_gettime = 66f61b70211f2050
slide = 000000001c200000, c_gettime = 24aea37023bf2050
slide = 0000000006c00000, c_gettime = 5a9b42f00e5f2050
slide = 000000000e200000, c_gettime = 128526f015bf2050
slide = 000000001fa00000, c_gettime = 4cf2ad70273f2050
slide = 000000000a200000, c_gettime = 6ed3177011bf2050
slide = 000000000ea00000, c_gettime = 869d0f70163f2050
slide = 0000000015800000, c_gettime = 9898c2f01d1f2050
slide = 000000001d400000, c_gettime = 52a343f024df2050
slide = 000000001d600000, c_gettime = 7ea2337024ff2050
slide = 0000000023e00000, c_gettime = 31d3b3f02b7f2050
slide = 0000000008e00000, c_gettime = 27a72cf0107f2050
slide = 000000000fa00000, c_gettime = 2b988f70173f2050
slide = 0000000011000000, c_gettime = 86c7a670189f2050
slide = 0000000011a00000, c_gettime = 3d8103f0193f2050
slide = 000000001c200000, c_gettime = 56d444f023bf2050
slide = 000000001fe00000, c_gettime = 82fa3970277f2050
slide = 0000000008c00000, c_gettime = 89dcda70105f2050

As you can see, even though by all accounts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IA key is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same, PACIZAs for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same pointer generated across different boots are somehow different.

The most straightforward solution I could think of was that iBoot or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel might be overwriting pac_key with a random value each boot before common_start runs, so that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys really are different each boot. Even though pac_key resides in __TEXT_EXEC.__text, which is protected against writes by KTRR, it's still possible to modify __TEXT_EXEC.__text before KTRR lockdown is performed. However, reading pac_key at runtime showed it still contained cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 0xfeedfacefeedfacf, so something else must be going on.

I next performed an experiment to determine whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys really were shared between userspace and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code suggested. I executed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA instruction in userspace on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rtclock_gettime() function, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n compared against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA'd sysclk_ops.c_gettime pointer read from kernel memory. These two values differed despite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys should be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same in userspace and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel, so once again it appeared that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 was conjuring some sort of dark magic.

Still not quite believing that pac_key wasn't being modified at runtime, I tried enumerating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B-key values of all threads on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system to see whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y really matched cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 0xfeedfacefeedfacf value suggested by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code. Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code for Switch_context in osfmk/arm64/cswitch.s, I determined that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value used as a seed to compute cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 B keys was being loaded from offset 0x458 of struct thread, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Mach struct representing a thread. This field is not present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public XNU sources, so I decided to name it pac_key_seed. My experiment consisted of walking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global thread list and dumping each thread's pac_key_seed.

I found that all kernel threads were indeed using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 0xfeedfacefeedfacf PAC key seed, while threads for userspace processes were using different, random seeds:

pid   0  thread ffffffe00092c000  pac_seed feedfacefeedfacf
pid   0  thread ffffffe00092c550  pac_seed feedfacefeedfacf
pid   0  thread ffffffe00092caa0  pac_seed feedfacefeedfacf
...
pid 258  thread ffffffe003597520  pac_seed 51c6b449d9c6e7a3
pid 258  thread ffffffe003764aa0  pac_seed 51c6b449d9c6e7a3

Thus, it did seem like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC keys for kernel threads were being initialized cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same each boot, and yet cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC'd pointers were different across boots. Something fishy was going on.

Bypass attempts

I next turned my attention to bypassing PAC using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 weaknesses identified in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 section "Weaknesses against kernel attackers".

Since executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same PACIZA instruction on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same pointer value with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same PAC keys across different boots was producing different results, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re must be some unidentified source of per-boot randomness. This basically spelled doom for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "implement QARMA-64 in userspace and compute PACs manually" strategy, but I decided to try it anyway. Unsurprisingly, this did not work.

Next I looked at whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r I could set my own thread's PAC keys equal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel PAC keys and forge kernel pointers in userspace. Ideally this would mean I'd set my IA key equal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's IA key, namely 0xfeedfacefeedfad2. However, as previously discussed, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's only one place in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel that appears to set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys, common_start, and yet userspace and kernel PAC codes are different anyway.

So I decided to combine this approach with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC cross-key symmetry weakness and instead set my thread's IB key equal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's IA key, which should allow me to forge kernel PACIZA pointers by executing PACIZB in userspace.

Unfortunately, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 naive way of doing this, by overwriting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pac_key_seed field in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current thread, would probably crash or panic cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system, since changing PAC keys during a thread's lifetime will break cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread's existing PAC signatures. And PAC signatures are checked all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 time, most frequently when returning from a function via RETAB. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only way to guarantee that changing a thread's PAC keys doesn't crash it or trigger a panic is to ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread does not call or return from any functions while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys have been changed.

The easiest way to do this is to spawn a thread that infinite loops in userspace executing PACIZB and storing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result to a global variable. Then we can overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread's pac_key_seed and force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread off-core using contention; once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 looping thread is rescheduled, its B keys will be set via Switch_context and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 forgery will be executed.

However, once again, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of this experiment was unsuccessful:

gettime       = fffffff0161f2050
kPACIZA       = faef2270161f2050
uPACIZA       = 138a8670161f2050
uPACIZB forge = d7fd0ff0161f2050

It seemed that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 manages to break eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cross-EL PAC symmetry or cross-key PAC symmetry.

To gain a bit more insight, I devised a test specifically for cross-key PAC symmetry. This meant setting my thread's IB key equal to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DB key and checking whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 outputs of PACIZB and PACDZB looked similar, indicating that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same PAC was generated. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IB and DB keys are generated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same seed and cannot be set independently, this actually involved 2 trials: first with seed value 0x11223344, and next with seed value 0x11223345:

IB = 0x11223344  uPACIZB = 0028180100000000
DB = 0x11223345  uPACDZB = 00679e0100000000
IB = 0x11223345  uPACIZB = 003ea80100000000
DB = 0x11223346  uPACDZB = 0023c58100000000

The highlighted rows show cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of executing PACDZB and PACIZB on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same value from userspace with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same keys. On a standard ARMv8.3 implementation of Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication, we'd expect most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC to agree. However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 two PACs seem unrelated, suggesting that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 does indeed manage to break cross-key PAC symmetry.

Implementation cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ories

With all three weaknesses suggested by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original design demonstrably not applicable to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12, it was time to try and work out what was really going on here.

It's clear that Apple had considered cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fact that Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication as defined in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard would do little to protect against kernel attackers with read/write, and thus cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y decided to implement a more robust defense. It's impossible to know what exactly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y did without a concerted reverse engineering effort, but we can speculate based on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 observed behavior.

My first thought was that Apple had decided to implement a secure monitor again, like it had done on prior devices with Watchtower to protect against kernel patches. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 secure monitor could trap transitions between exception levels and trap writes to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC key registers, it could hide cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 true PAC keys from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel and implement ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r shenanigans to break PAC symmetries. However, I couldn't find evidence of a secure monitor inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache.

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r alternative is that Apple has decided to move cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 true PAC keys into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 itself, so that even cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most powerful software attacker doesn't have cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to read cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys. The keys could be generated randomly on boot or set via special registers by iBoot. Then, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys that are fed to QARMA-64 (or whatever algorithm is actually being used to generate PACs) would be some combination of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 random key, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard key set via special registers, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current exception level.

For example, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 could cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365oretically store 10 random 128-bit PAC keys, one for each pair of an exception level (EL0 or EL1) and a standard PAC key (IA, IB, DA, DB, or GA). Then cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC key used for any particular operation could be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XOR of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 random PAC key corresponding to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 operation (e.g. IB-EL0 for a PACIB instruction in userspace) with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard PAC key set via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard registers (e.g. APIBKey). Such a design wouldn't come without challenges (for example, you'd need a non-volatile place to store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 random keys for when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core sleeps), but it would cleanly break cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 cross-EL and cross-key symmetries and prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 keys from ever being disclosed, completely mitigating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 three previously identified weaknesses.

While I couldn't figure out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 true implementation, I decided to assume cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most robust design for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of my research: that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 true keys are random and stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 SoC itself. That way, any bypass strategy I found would be all but guaranteed to work regardless of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual implementation.

PAC EL-impersonation

With zero leads for systematic weaknesses, I decided it was time to investigate PAC signing gadgets.

The very first PACIA instruction occurs in a function I identified as vm_shared_region_slide_page(), and specifically as an inlined copy of vm_shared_region_slide_page_v3(). This function is present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XNU sources, and has cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following interesting comment in its main loop:

uint8_t* rebaseLocation = page_content;
uint64_t delta = page_entry;
do {
   rebaseLocation += delta;
   uint64_t value;
   memcpy(&value, rebaseLocation, sizeof(value));
   delta = ( (value & 0x3FF8000000000000) >> 51) * sizeof(uint64_t);

   // A pointer is one of :
   // {
   //     uint64_t pointerValue : 51;
   //     uint64_t offsetToNextPointer : 11;
   //     uint64_t isBind : 1 = 0;
   //     uint64_t aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated : 1 = 0;
   // }
   // {
   //     uint32_t offsetFromSharedCacheBase;
   //     uint16_t diversityData;
   //     uint16_t hasAddressDiversity : 1;
   //     uint16_t hasDKey : 1;
   //     uint16_t hasBKey : 1;
   //     uint16_t offsetToNextPointer : 11;
   //     uint16_t isBind : 1;
   //     uint16_t aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated : 1 = 1;
   // }

   bool isBind = (value & (1ULL << 62)) == 1;
   if (isBind) {
       return KERN_FAILURE;
   }

   bool isAucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated = (value & (1ULL << 63)) != 0;

   if (isAucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated) {
       // The new value for a rebase is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 low 32-bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threaded value
       // plus cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 slide.
       value = (value & 0xFFFFFFFF) + slide_amount;
       // Add in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 offset from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_header
       const uint64_t value_add = s_info->value_add;
       value += value_add;

   } else {
       // The new value for a rebase is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 low 51-bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threaded value
       // plus cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 slide. Regular pointer which needs to fit in 51-bits of
       // value. C++ RTTI uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top bit, so we'll allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole top-byte
       // and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bottom 43-bits to be fit in to 51-bits.
       ...
   }

   memcpy(rebaseLocation, &value, sizeof(value));
} while (delta != 0);

The part about cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "pointer" containing aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated, hasBKey, and hasDKey bits suggests that this code is dealing with aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers, although all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code that actually performs PAC operations has been removed from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public sources. Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r comment about C++ RTTI suggests that this code is specifically for rebasing userspace code. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel would have to be aware of, and maybe perform PAC operations on, userspace pointers.

Looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 decompilation of this loop in IDA, we can see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are many operations not present in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public source code:

slide_amount = si->slide;
offset = uservaddr - rebaseLocation;
do
{
   rebaseLocation += delta;
   value = *(uint64_t *)rebaseLocation;
   delta = (value >> 48) & 0x3FF8;
   if ( value & 0x8000000000000000 )       // isAucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated
   {
       value = slide_amount + (uint32_t)value + slide_info_entry->value_add;
       context = (value >> 32) & 0xFFFF;   // diversityData
       if ( value & 0x1000000000000 )      // hasAddressDiversity
           context = (offset + rebaseLocation) & 0xFFFFFFFFFFFF
                   | (context << 48);
       if ( si->UNKNOWN_FIELD && !(BootArgs->bootFlags & 0x4000000000000000) )
       {
           daif = _ReadStatusReg(ARM64_SYSREG(3, 3, 4, 2, 1));// DAIF
           if ( !(daif & 0x80) )
               __asm { MSR             #6, #3 }
           _WriteStatusReg(S3_4_C15_C0_4,
               _ReadStatusReg(S3_4_C15_C0_4) & 0xFFFFFFFFFFFFFFFB);
           __isb(0xFu);
           key_bits = (value >> 49) & 3;
           switch ( key_bits )
           {
               case 0:
                   value = ptrauth_sign...(value, ptrauth_key_asia, &context);
                   break;
               case 1:
                   value = ptrauth_sign...(value, ptrauth_key_asib, &context);
                   break;
               case 2:
                   value = ptrauth_sign...(value, ptrauth_key_asda, &context);
                   break;
               case 3:
                   value = ptrauth_sign...(value, ptrauth_key_asdb, &context);
                   break;
           }
           _WriteStatusReg(S3_4_C15_C0_4, _ReadStatusReg(S3_4_C15_C0_4) | 4);
           __isb(0xFu);
           ml_set_interrupts_enabled(~(daif >> 7) & 1);
       }
   }
   else
   {
       ...
   }
   memmove(rebaseLocation, &value, 8);
}
while ( delta );

It appears that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is attempting to sign pointers on behalf of userspace. This is interesting because, as previously discussed, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 breaks cross-EL symmetry, which should mean that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel's signatures on userspace pointers will be invalid in userspace.

It's unlikely that this freshly-introduced code is broken, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re must be some mechanism by which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel instructs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CPU to sign with userspace pointers instead. Searching for ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r instances of PAC* instructions like this, a pattern begins to emerge: whenever cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel signs pointers on behalf of userspace, it wraps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC instructions by clearing and setting a bit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 S3_4_C15_C0_4 system register:

MRS         X8, #4, c15, c0, #4 ; S3_4_C15_C0_4
AND         X8, X8, #0xFFFFFFFFFFFFFFFB
MSR         #4, c15, c0, #4, X8 ; S3_4_C15_C0_4
ISB

...         ;; PAC stuff for userspace

MRS         X8, #4, c15, c0, #4 ; S3_4_C15_C0_4
ORR         X8, X8, #4
MSR         #4, c15, c0, #4, X8 ; S3_4_C15_C0_4
ISB

Also, kernel code that sets/clears bit 0x4 of S3_4_C15_C0_4 is usually accompanied by code that disables interrupts and checks bit 0x4000000000000000 of BootArgs->bootFlags, as we see in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 excerpt from vm_shared_region_slide_page_v3() above.

We can infer that bit 0x4 of S3_4_C15_C0_4 controls whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r PAC* instructions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EL0 keys or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EL1 keys: when this bit is set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel keys are used, ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace keys are used. It makes sense that you'd need to disable interrupts while this bit is cleared, since ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 arrival of an interrupt may cause ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r kernel code to execute while cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EL0 PAC keys are still in use, causing PAC validation failures that would panic cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel.

PAC-enable bits in SCTLR_EL1

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r thing I noticed while investigating system registers was that previously reserved bits of SCTLR_EL1 were now being used to enable/disable PAC instructions for certain keys.

While looking at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exception vector for syscall entry, Lel0_synchronous_vector_64, I noticed some additional code referencing bootFlags and setting certain bits of SCTLR_EL1 that are marked as reserved in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM standard:

ADRP        X0, #const_boot_args@PAGE
ADD         X0, X0, #const_boot_args@PAGEOFF
LDR         X0, [X0,#(const_boot_args.bootFlags - 0xFFFFFFF0077A21B8)]
AND         X0, X0, #0x8000000000000000
CBNZ        X0, loc_FFFFFFF0079B3320
MRS         X0, #0, c1, c0, #0                  ;; SCTLR_EL1
TBNZ        W0, #0x1F, loc_FFFFFFF0079B3320
ORR         X0, X0, #0x80000000                 ;; set bit 31
ORR         X0, X0, #0x8000000                  ;; set bit 27
ORR         X0, X0, #0x2000                     ;; set bit 13
MSR         #0, c1, c0, #0, X0                  ;; SCTLR_EL1

Also, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bits are conditionally cleared on exception return:

TBNZ        W1, #2, loc_FFFFFFF0079B3AE8        ;; SPSR_EL1.M[3:0] & 0x4
...
LDR         X2, [X2,#thread.field_460]
CBZ         X2, loc_FFFFFFF0079B3AE8
...
MRS         X0, #0, c1, c0, #0                  ;; SCTLR_EL1
AND         X0, X0, #0xFFFFFFFF7FFFFFFF         ;; clear bit 31
AND         X0, X0, #0xFFFFFFFFF7FFFFFF         ;; clear bit 27
AND         X0, X0, #0xFFFFFFFFFFFFDFFF         ;; clear bit 13
MSR         #0, c1, c0, #0, X0                  ;; SCTLR_EL1

While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bits are documented as reserved (with value 0) by ARM, I did find a reference to one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XNU 4903.221.2 sources, in osfmk/arm64/proc_reg.h:

// 13           PACDB_ENABLED            AddPACDB and AuthDB functions enabled
#define SCTLR_PACDB_ENABLED             (1 << 13)

This suggested that bit 13 at least is related to enabling PAC for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 DB key. Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only SCTLR_EL1 bits that are both (a) not mentioned in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 file and (b) not set automatically via SCTLR_RESERVED are 31, 30, and 27, I speculated that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bits controlled cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r PAC keys. (Presumably, leaving cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 reference to SCTLR_PACDB_ENABLED in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code was an oversight.) My guess is that bit 31 controls PACIA, bit 30 controls PACIB, bit 27 controls PACDA, and bit 13 controls PACDB.

To test this cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory, I executed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following sequence of PAC instructions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 debugger, both before and after setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 field at offset 0x460 of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current thread:

pacia  x0, x1
pacib  x2, x3
pacda  x4, x5
pacdb  x6, x7

Before executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se instructions, I set each register Xn to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value 0x11223300 | n. Here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result before setting field_460, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACs highlighted:

x0 = 0x001d498011223300    # PACIA
x1 = 0x0000000011223301
x2 = 0x0035778011223302    # PACIB
x3 = 0x0000000011223303
x4 = 0x0062860011223304    # PACDA
x5 = 0x0000000011223305
x6 = 0x001e6c8011223306    # PACDB
x7 = 0x0000000011223307

And here's cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result after:

x0 = 0x0000000011223300    # PACIA
x1 = 0x0000000011223301
x2 = 0x0035778011223302    # PACIB
x3 = 0x0000000011223303
x4 = 0x0000000011223304    # PACDA
x5 = 0x0000000011223305
x6 = 0x0000000011223306    # PACDB
x7 = 0x0000000011223307

This seems to confirm our cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ory: before setting field_460, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC instructions worked as expected, but after setting field_460, all except PACIB have been effectively turned into NOPs. Using this fact for exploitation is tricky, since overwriting field_460 in a kernel thread does not seem to disable PAC in that thread due to additional checks. Nonecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365less, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existence of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se PAC-enable bits in SCTLR_EL1 was interesting in its own right.

The (non-)existence of signing gadgets

At this point, since we have no systematic weaknesses against Apple's more robust design, we're looking for a signing gadget usable only via read/write. That means we're looking for a sequence of code that will read a pointer from memory, sign it, and write it back to memory. But we can't yet call arbitrary kernel addresses, so we also need to ensure that this code path is actually triggerable, eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 course of normal kernel operation, or by using our iokit_user_client_trap() call primitive to call a kernel function to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re already exists a PACIZA'd pointer.

Apple has clearly tried to scrub cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache of any obvious signing gadgets. All occurrences of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIA instruction are eicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r unusable or wrapped by code that switches to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace PAC keys (via S3_4_C15_C0_4), so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re's no way we can convince cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel to perform a PACIA forgery using only read/write.

This left just PACIZA. While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were many more occurrences of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIZA instruction, most of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m were useless since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result wasn't written to memory. Additionally, gadgets that actually did load and store cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer were almost always preceded by AUTIA, which would fail if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer we were signing didn't already have a valid PAC:

LDR         X10, [X9,#0x30]!
CBNZ        X19, loc_FFFFFFF007EBD330
CBZ         X10, loc_FFFFFFF007EBD330
MOV         X19, #0
MOV         X11, X9
MOVK        X11, #0x14EF,LSL#48
AUTIA       X10, X11
PACIZA      X10
STR         X10, [X9]

Thus, it appeared I was out of luck.

The fourth weakness

After giving up on signing gadgets and pursuing a few ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r dead ends, I eventually wondered: What would actually happen if PACIZA was used to sign an invalid pointer validated by AUTIA? I'd assumed that such a pointer would be useless, but I decided to look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM pseudocode to see what would actually happen.

To my surprise, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 standard revealed a funny interaction between AUTIA and PACIZA. When AUTIA finds that an aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointer's PAC doesn't match cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 expected value, it corrupts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer by inserting an error code into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer's extension bits:

// Auth()
// ======
// Restores cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 upper bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address to be all zeros or all ones (based on
// cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of bit[55]) and computes and checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication
// code. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 check passes, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 restored address is returned. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
// check fails, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second-top and third-top bits of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension bits in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
// pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code field are corrupted to ensure that accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
// address will give a translation fault.

bits(64) Auth(bits(64) ptr, bits(64) modifier, bits(128) K, boolean data,
             bit keynumber)
   bits(64) PAC;
   bits(64) result;
   bits(64) original_ptr;
   bits(2) error_code;
   bits(64) extfield;

   // Reconstruct cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension field used of adding cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer
   boolean tbi = CalculateTBI(ptr, data);
   integer bottom_PAC_bit = CalculateBottomPACBit(ptr<55>);
   extfield = Replicate(ptr<55>, 64);

   if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       ...
   else
       original_ptr = extfield<64-bottom_PAC_bit-1:0>:ptr;

   PAC = ComputePAC(original_ptr, modifier, K<127:64>, K<63:0>);
   // Check pointer aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code
   if tbi cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
       ...
   else
       if ((PAC<54:bottom_PAC_bit> == ptr<54:bottom_PAC_bit>) &&
           (PAC<63:56> == ptr<63:56>)) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
           result = original_ptr;
       else
           error_code = keynumber:NOT(keynumber);
           result = original_ptr<63>:error_code:original_ptr<60:0>;
   return result;

Meanwhile, when PACIZA is adding a PAC to a pointer, it actually signs cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer with corrected extension bits, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n corrupts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension bits were originally invalid. From cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pseudocode for AddPAC() above:

   ext_ptr = extfield<(64-bottom_PAC_bit)-1:0>:ptr;

PAC = ComputePAC(ext_ptr, modifier, K<127:64>, K<63:0>);

// Check if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ptr has good extension bits and corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer
// aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication code if not;
if !IsZero(ptr)
       && !IsOnes(ptr) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
   PAC = NOT(PAC);

Critically, PAC* instructions will corrupt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC of a pointer with invalid extension bits by flipping a single bit of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC. While this will certainly invalidate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC, this also means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 true PAC can be reconstructed if we can read out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of a PAC*-forgery on a pointer produced by an AUT* instruction! So sequences like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 one above that consist of an AUTIA followed by a PACIZA can be used as signing gadgets even if we don't have a validly signed pointer to begin with: we just have to flip a single bit in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 forged PAC.

A complete A-key forgery strategy for 16C50

With cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existence of a single PACIZA signing gadget, we can begin our construction of a complete forgery strategy for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys on A12 devices running build 16C50.

Stage 1: PACIZA-forgery

A bit of sleuthing reveals that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gadget we found is part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function sysctl_unregister_oid(), which is responsible for unregistering a sysctl_oid struct from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global sysctl tree. (Once again, this function does not have any PAC-related code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 public sources, but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se operations are present on PAC-enabled devices.) Here's a listing of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant parts of this function from IDA:

void sysctl_unregister_oid(sysctl_oid *oidp)
{
   sysctl_oid *removed_oidp = NULL;
   sysctl_oid *old_oidp = NULL;
   BOOL have_old_oidp;
   void **handler_field;
   void *handler;
   uint64_t context;
   ...
   if ( !(oidp->oid_kind & 0x400000) )         // Don't enter this if
   {
       ...
   }
   if ( oidp->oid_version != 1 )               // Don't enter this if
   {
       ...
   }
   sysctl_oid *first_sibling = oidp->oid_parent->first;
   if ( first_sibling == oidp )                // Enter this if
   {
       removed_oidp = NULL;
       old_oidp = oidp;
       oidp->oid_parent->first = old_oidp->oid_link;
       have_old_oidp = 1;
   }
   else
   {
       ...
   }
   handler_field = &old_oidp->oid_handler;
   handler = old_oidp->oid_handler;
   if ( removed_oidp || !handler )             // Take cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 else
   {
       ...
   }
   else
   {
       removed_oidp = NULL;
       context = (0x14EF << 48) | ((uint64_t)handler_field & 0xFFFFFFFFFFFF);
       *handler_field = ptrauth_sign_unaucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated(
               ptrauth_auth_function(handler, ptrauth_key_asia, &context),
               ptrauth_key_asia,
               0);
       ...
   }
   ...
}

If we can get this function called with a crafted sysctl_oid that causes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 indicated path to be taken, we should be able to forge arbitrary PACIZA pointers.

There aren't any existing global PACIZA'd pointers to this function, so we can't call it directly using our iokit_user_client_trap() primitive, but as luck would have it, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are several global PACIZA'd function pointers that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves call into it. This is because several kernel extensions register sysctls that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y need to unregister before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y're unloaded; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se kexts often have a module termination function that calls sysctl_unregister_oid(), and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kmod_info struct describing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kext contains a PACIZA'd pointer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 module termination function.

The best candidate I could find was l2tp_domain_module_stop(), which is part of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 com.apple.nke.lttp kext. This function will perform some deinitialization work before calling sysctl_unregister_oid() on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 global sysctl__net_ppp_l2tp object. Thus, we can PACIZA-sign an arbitrary pointer by overwriting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 contents of sysctl__net_ppp_l2tp, calling l2tp_domain_module_stop() via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing global PACIZA'd pointer, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n reading out sysctl__net_ppp_l2tp's oid_handler field and flipping bit 62.

Stage 2: PACIA/PACDA forgery

While this lets us PACIZA-forge any pointer we want, it'd be nice to be able to perform PACIA/PACDA forgeries as well, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we could implement cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 full bypass described in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 section "Finding an entry point for kernel code execution". To do that, I next looked into whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r our PACIZA primitive could turn any of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIA instructions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache into viable signing gadgets.

The most likely candidate for both PACIA and PACDA was an unknown function sub_FFFFFFF007B66C48, which contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following instruction sequence:

MRS         X9, #4, c15, c0, #4 ; S3_4_C15_C0_4
AND         X9, X9, #0xFFFFFFFFFFFFFFFB
MSR         #4, c15, c0, #4, X9 ; S3_4_C15_C0_4
ISB
LDR         X9, [X2,#0x100]
CBZ         X9, loc_FFFFFFF007B66D24
MOV         W10, #0x7481
PACIA       X9, X10
STR         X9, [X2,#0x100]
...
LDR         X9, [X2,#0xF8]
CBZ         X9, loc_FFFFFFF007B66D54
MOV         W10, #0xCBED
PACDA       X9, X10
STR         X9, [X2,#0xF8]
...
MRS         X9, #4, c15, c0, #4 ; S3_4_C15_C0_4
ORR         X9, X9, #4
MSR         #4, c15, c0, #4, X9 ; S3_4_C15_C0_4
ISB
...
PACIBSP
STP         X20, X19, [SP,#var_20]!
...         ;; Function body (mostly harmless)
LDP         X20, X19, [SP+0x20+var_20],#0x20
AUTIBSP
MOV         W0, #0
RET

What makes sub_FFFFFFF007B66C48 a good candidate is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIA/PACDA instructions occur before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack frame is set up. Ordinarily, calling into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 middle of a function will cause problems when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function returns, since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function's epilogue will tear down a frame that was never set up. But since this function's stack frame is set up after our desired entry points, we can use our kernel call primitive to jump directly to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se instructions without causing any problems.

Of course, we still have anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r issue: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PACIA and PACDA instructions use registers X9 and X10, while our kernel call primitive based on iokit_user_client_trap() only gives us control of registers X1 through X6. We'll need to figure out how to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 values we want into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 appropriate registers.

In fact, we already found a solution to this very problem earlier: JOP gadgets.

Searching through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache, just three kexts seem to hold cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vast majority of non-PAC'd indirect branches: FairPlayIOKit, LSKDIOKit, and LSKDIOKitMSE. These kexts even stand out in IDA's navigator bar as islands of red in a sea of blue, since IDA cannot create functions out of many of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 instructions in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se kexts:


It seems that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se kexts use some sort of obfuscation to hide control flow and make reverse engineering more difficult. Many jumps in this code are performed indirectly through registers. Unfortunately, in this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 obfuscation actually makes our job as attackers easier, since it gives us a plethora of useful JOP gadgets not protected by PAC.

For our specific use case, we have control of PC and X1 through X6, and we're trying to set X2 to some writable memory region, X9 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer we want to sign, and X10 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signing context, before jumping to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 signing gadget. I eventually settled on executing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following JOP program to accomplish this:

X1 = MOV_X10_X3__BR_X6
X2 = KERNEL_BUFFER
X3 = CONTEXT
X4 = POINTER
X5 = MOV_X9_X0__BR_X1
X6 = PACIA_X9_X10__STR_X9_X2_100

MOV         X0, X4
BR          X5

MOV         X9, X0
BR          X1

MOV         X10, X3
BR          X6

PACIA       X9, X10
STR         X9, [X2,#0x100]
...

And with that, we now have a complete bypass strategy that allows us to forge arbitrary PAC signatures using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A keys.

Timeline

After sharing my original kernel read/write exploit on December 18, 2018, I reported cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 proof-of-concept PAC bypass built on top of voucher_swap on December 30. This POC could produce arbitrary A-key PAC forgeries and call arbitrary kernel functions with 7 arguments, just like on non-PAC devices.

Apple quickly responded suggesting that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 latest iOS 12.1.3 beta, build 16D5032a, should mitigate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue. As this build also fixed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 voucher_swap bug, I couldn't test this directly, but I did inspect cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernelcache manually and found that Apple had mitigated cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sysctl_unregister_oid() gadget used to produce cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first PACIZA forgery.

This build was released on December 19, near cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 beginning of my research into PAC and long before I reported cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bypass to Apple. Thus, like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 voucher_swap bug, I suspect that anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r researcher found and reported this issue first.

Apple's fix

In order to fix cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sysctl_unregister_oid() gadget (and ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r AUTIA-PACIA gadgets), Apple has added a few instructions to ensure that if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AUTIA fails, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resulting invalid pointer will be used instead of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of PACIZA:

LDR         X10, [X9,#0x30]!            ;; X10 = old_oidp->oid_handler
CBNZ        X19, loc_FFFFFFF007EBD4A0
CBZ         X10, loc_FFFFFFF007EBD4A0
MOV         X19, #0
MOV         X11, X9                     ;; X11 = &old_oidp->oid_handler
MOVK        X11, #0x14EF,LSL#48         ;; X11 = 14EF`&oid_handler
MOV         X12, X10                    ;; X12 = oid_handler
AUTIA       X12, X11                    ;; X12 = AUTIA(handler, 14EF`&handler)
XPACI       X10                         ;; X10 = XPAC(handler)
CMP         X12, X10
PACIZA      X10                         ;; X10 = PACIZA(XPAC(handler))
CSEL        X10, X10, X12, EQ           ;; X10 = (PAC_valid ? PACIZA : AUTIA)
STR         X10, [X9]

With this change, we can no longer PACIZA-forge a pointer unless we already have a PACIA forgery with a specific context.

Brute-force strategies

While this does mitigate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fast, straightforward strategy outlined above, with enough time it is still susceptible to brute forcing. Now, I couldn't test this explicitly without an exploit for iOS 12.1.3, but I was able to simulate how long it might take using my exploit on iOS 12.1.2.

The problem is that even though we don't have an existing PACIA-forgery for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointer we want to PACIZA-forge, we can use our kernel call primitive to execute this gadget repeatedly with different guesses for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 valid PAC. Unlike most ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r instances in which aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365nticated pointers are used, guessing incorrectly here won't actually trigger a panic: we can just read out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result to see whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r we guessed correctly (in which case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 oid_handler field will have a PAC added) or incorrectly (in which case oid_handler will look like cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 result of a failed AUTIA).

Looking back at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of PAC'd pointers generated in my very first experiment in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 subsection "Observing runtime behavior", I compared cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 extension bits of all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pointers to determine that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 PAC was masked into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bits 0xff7fff8000000000. This means cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 is using a 24-bit PAC, or about 16 million possibilities.

In my experiments, I found that invoking l2tp_domain_module_stop() and l2tp_domain_module_start() 256 times took about 13.2 milliseconds. Thus, exhaustively checking all 16 million possible PACs should take around 15 minutes. And unless cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re were ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r changes I didn't notice, once a single PACIZA forgery is produced, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A-key bypass strategy should still be possible.

(Initializing/deinitializing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 module more than about 4096 times started to produce noticeable slowdowns; I didn't identify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 source of this slowness, but I do suspect that with effort it should be possible to work around it.)

Conclusion

In this post we put Apple's implementation of Pointer Aucá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ntication on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 SoC used in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iPhone XS under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 microscope, describing observed behavior, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365orizing about how deviations from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ARM reference might be implemented under cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hood, and analyzing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system for weaknesses that would allow a kernel attacker with read/write capabilities to forge PACs for arbitrary pointers. This analysis culminated with a complete bypass strategy and proof-of-concept implementation that allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker to perform arbitrary A-key forgeries on an iPhone XS running iOS 12.1.2. Such a bypass is sufficient for achieving arbitrary kernel code execution through JOP. This strategy was partially mitigated with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 release of iOS 12.1.3 beta 16D5032a, although cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are indications that it might still be possible to bypass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mitigation via a brute-force approach.

Despite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se flaws, PAC remains a solid and worthwhile mitigation. Apple's hardening of PAC in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 A12 SoC, which was clearly designed to protect against kernel attackers with read/write, meant that I did not find a systematic break in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 design and had to rely on signing gadgets, which are easy to patch via software. As with any complex new mitigation, loopholes are not uncommon in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first few iterations. However, given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fragility of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current bypass technique (relying on, among ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r things, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 single IOUserClient class that allows us to overwrite its IOExternalTrap, one of a very small number of usable PACIZA gadgets, and a handful of non-PAC'd JOP gadgets introduced by obfuscation), I believe it's possible for Apple to harden cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir implementation to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 point that strong forgery bypasses become rare.

Furcá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rmore, PAC shows promise as a tool to make data-only kernel attacks trickier and less powerful. For example, I could see Apple adding something akin to a __security_critical attribute that enables PAC for C pointers that are especially prone to being hijacked during exploits, such as ipc_port's ip_kobject field. Such a mitigation wouldn't end any bug classes, since sophisticated attackers could find ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r ways of leveraging vulnerabilities into kernel read/write primitives, but it would raise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bar and make simple exploit strategies like those used in voucher_swap much harder (and hopefully less reliable) to pull off.