Full Report
This report is about an information leak that was discovered by accident. In some cases, the userland processes on an XNU system could crash with a kernel pointer in the far register. This was a great information leak that could be done over and over again. However, the cause of the bug is very complicated. Exceptions happen when the userland program has done something illegal, such as access unmapped memory. On arm64, the Vector Base Address Register holds a table where the kernel should jump to. There are many different exception types that are all handled by specific functions. The addresses we see in GDB aren't real - they're virtual. The CPUs create an abstraction layer between the physical memory and the virtual memory using page tables. This allows for the reuse of the same page and separate permission settings on that. When a fault occurs, the address will be put into the FAR_ELn register for some exception types. However, if the register is not used on the exception, it's also NOT cleared. This creates some ambiguity on what to do with it. Within XNU, the entire core state is copied into the threads data structure. This is done in order to optimize the hot code, as opposed to only copying in things conditionally. Finally, we have enough to understand the bug! Consider the following scenarios: A write triggers a fault on a unmapped page, resulting in a data abort exception. The adress of data is copied to FAR_EL1. The exception is handled by XNU. The core switches to executing in userland once again. Another exception occurs, such as a breakpoint debug instruction. FAR_EL1 is NOT updated because of the specific exception type but other data is. XNU copies the cores state. Using the scenario above, step 6 gets stale data copied in. This is a typically uninitialized memory issue. This isn't just a standard memory leak though! This pointer is a pointer to the thread information that can be used to query information about the crashing state of a task. Since this is using the wrong task, we can get leaked from various other tasks and leak kernel pointers. Sick! To make this more reliable, they found that forcing crashesat a specific memory offset within an unmapped page made it better. An additional way was relying on the object having a pre-known size, which would leak a bunch of information after that. The bug is fascinating. I do not fully understand where the information is truly leaked at bug enjoyed it regardless. Sometimes, observing strange behavior can lead you to crazy bugs.
Analysis Summary
# Vulnerability: XNU Kernel Information Leak via Stale FAR_EL1 Register
## CVE Details
- **CVE ID**: CVE-2023-41992 (Note: Based on the patch timeline and description in iOS 17.1)
- **CVSS Score**: 5.5 (Medium) - *Estimated based on local information disclosure*
- **CWE**: CWE-200: Exposure of Sensitive Information to an Unauthorized Actor / CWE-457: Use of Uninitialized Variable
## Affected Systems
- **Products**: Apple iOS, iPadOS, macOS, tvOS, and watchOS (XNU Kernel).
- **Versions**: Versions prior to iOS 17.1, macOS 14.1. Specifically identified as fixed in XNU source release `xnu-100002.41.9`.
- **Configurations**: Systems running on ARM64 architecture.
## Vulnerability Description
The vulnerability is a kernel-level information leak caused by the improper handling of the `FAR_EL1` (Fault Address Register) during CPU exceptions.
On ARM64, when a synchronous exception (like a data abort/page fault) occurs, the CPU populates `FAR_EL1` with the faulting address. However, for other exception types (like breakpoints or syscalls), the register is **not cleared** and retains its previous value.
Within the XNU kernel, the exception handler unconditionally copies the entire core state—including the potentially stale `FAR_EL1`—into the thread's data structure to optimize performance. A userland attacker can trigger a sequence where:
1. A legitimate fault stores a sensitive address (e.g., a kernel pointer or data from another task) in `FAR_EL1`.
2. A subsequent exception occurs that does not update `FAR_EL1`.
3. The kernel copies the stale `FAR_EL1` value into the thread state.
4. The userland process queries its own (or a hijacked) thread state to retrieve the leaked pointer.
## Exploitation
- **Status**: PoC available/detailed in research.
- **Complexity**: Medium.
- **Attack Vector**: Local. The attacker must be able to execute code on the device to trigger exceptions and query thread states.
## Impact
- **Confidentiality**: High. Allows for the leakage of kernel pointers, which assists in bypassing KASLR (Kernel Address Space Layout Randomization). It can also leak memory addresses from other tasks/processes.
- **Integrity**: None.
- **Availability**: None.
## Remediation
### Patches
- **iOS/iPadOS 17.1**: Updates the kernel to sanitize the `far` register.
- **XNU Source**: Fixed in version `xnu-100002.41.9`.
- The fix involves explicitly clearing the `far` value in the `saved_state` structure for exception types where `FAR_EL1` is not considered valid.
### Workarounds
- No known non-patch workarounds. Users should update to the latest OS versions.
## Detection
- **Indicators of Compromise**: Presence of kernel pointers in userland crash logs (specifically within the `far` register field).
- **Detection Methods**: Monitoring for unusual frequencies of `thread_get_state` calls specifically requesting `ARM_EXCEPTION_STATE64`.
## References
- **Original Research**: DFSEC Research - "That's FAR-out, Man"
- **XNU Source**: [https://github.com/apple-oss-distributions/xnu](https://github.com/apple-oss-distributions/xnu)
- **Apple Security Advisory**: [https://support.apple.com/en-us/HT213982](https://support.apple.com/en-us/HT213982)