In January, Barak Gabai of the X-Force Application Security Research Team discovered an interesting information leak vulnerability in iOS IOKit IOMobileFramebuffer (CVE-2015-1097), which can be used to defeat the kernel address obfuscation mechanism available since iOS 6. The vulnerability was disclosed to Apple and has been fixed in iOS 8.3.

In this blog post, I will provide a brief overview of the kernel address obfuscation technique employed by iOS and show how Gabai’s vulnerability renders it ineffective, as is the case with similar memory disclosures. I will also briefly touch on what I believe makes this particular vulnerability uniquely interesting.

The iOS kernel address obfuscation is excellently described in full in the “mach_port_kobject() and the kernel address obfuscation” blog post by Stefan Esser. Readers unfamiliar with this security mitigation mechanism and techniques for defeating it are encouraged to first read Esser’s post.

Background

On iOS, the kernel address space is randomized. C++ objects on the kernel heap are juicy targets for exploiters, and they can often be used for arbitrary code execution.

In some circumstances, the kernel — such as via mach_port_kobject() prior to iOS 8.1.3 — can provide an address of a kernel object to a user-space caller. Should the address of these objects be provided directly to user-space, exploitation would be assisted because an attacker — such as a malware author or jailbreak developer — could know where exploitable heap objects were situated in the kernel heap. Therefore, in recent versions of the kernel, these addresses are not passed to user-space in the clear and are instead obfuscated.

In order to perform this obfuscation of kernel-space addresses prior to being exposed to user-space, the addresses are passed through the VM_KERNEL_ADDRPERM(addr) macro, which is defined as follows:

#define VM_KERNEL_ADDRPERM(_v) \
 (((vm_offset_t)(_v) == 0) ? \
  (vm_offset_t)(0) : \
  (vm_offset_t)(_v) + vm_kernel_addrperm)

As we can see, the code takes the kernel-space address and adds a vm_kernel_addrperm value to it. This permutation constant is a random value that is generated on boot. Prior work has been published on the strength of this permutation constant, and I’m not going to go into how the vm_kernel_addrperm value is generated in this blog post. However, for the purposes of this discussion, let’s assume Apple has managed to generate a permutation constant of sufficient entropy.

If all the above worked as it should, it would be difficult to attack the interesting objects situated on the kernel heap. However, in order to defeat this mechanism, we can make use of the invertible nature of the obfuscation function in order to deobfuscate the kernel address. The kernel is able to do so because it already knows vm_kernel_addrperm. So if we would like to do the same, we would ideally like to discover vm_kernel_addrperm.

For the function itself, we can see the following:

vm_kernel_addrperm = addr_obfuscated - addr_clear

So if we manage to find a leak of addr_clear (or any address from which we can reliably calculate the offset), we can then render the entire obfuscation mechanism useless. Such leaks have been demonstrated in the past.

The Vulnerability

Gabai discovered that the (unobfuscated) address of the IOMobileFramebuffer object on the kernel heap is inadvertently being leaked to user-space by a member function. In my opinion, what makes this particular leak interesting is the mechanism of the leak itself.

The vulnerability can basically be summarized as follows: The ‘get_framebuffer_id’ method (selector 7) of IOMobileFramebufferUserClient calls a member method of IOMobileFramebuffer and passes the return value of the call to user-space.

In the standard ARM calling convention, the R0 register is used both to pass the first argument to the function and to hold the return value on the function return.

In our case, the first argument (placed in R0) is the pointer of the IOMobileFramebuffer object itself. In this instance, the called method is, in fact, ’empty’ — a nullsub. Therefore, as the caller expects the return value to be in R0 — and the nullsub simply doesn’t touch R0 — the return value is the persisted value of R0, the object pointer. This is then further persisted to user-space, and hence, we have our information leak.

How Can These Nullsubs Occur?

The most likely reason in the case of C++ is that an empty virtual function of a base class has been called instead of the derived class function. In such a case, the return register, as defined by the calling convention, will persist in its value. In C++, the first argument is the pointer of the object itself (the ‘this’ pointer). Depending on the calling convention, such as in the case of the standard ARM calling convention, it’s even possible that this object pointer is what will be present in the return register, then the function returns.

Another reason could be that code may have been #if{,def}’d out. This pattern is sometimes seen with debug code, which is to be removed in release builds. In such a case, it is possible that compiler optimizations could remove the function altogether. Based on some minor experimentation I performed with both GCC and Clang at varying optimization levels, it’s entirely possible nullsubs could be left in the code. Also, by default, GCC doesn’t warn about control reaching the end of a non-void function, whereas Clang does.

Any instance where control from a non-void function can return without a return value is potentially dangerous, and developers should take care to eliminate such instances from their code.

While developers need to be aware of how the code looks in a high-level language, they should think about what’s happening under the hood, as well.

More from Software Vulnerabilities

FYSA – Adobe Cold Fusion Path Traversal Vulnerability

2 min read - Summary Adobe has released a security bulletin (APSB24-107) addressing an arbitrary file system read vulnerability in ColdFusion, a web application server. The vulnerability, identified as CVE-2024-53961, can be exploited to read arbitrary files on the system, potentially leading to unauthorized access and data exposure. Threat Topography Threat Type: Arbitrary File System Read Industries Impacted: Technology, Software, and Web Development Geolocation: Global Environment Impact: Web servers running ColdFusion 2021 and 2023 are vulnerable Overview X-Force Incident Command is monitoring the disclosure…

FYSA – Critical RCE Flaw in GNU-Linux Systems

2 min read - Summary The first of a series of blog posts has been published detailing a vulnerability in the Common Unix Printing System (CUPS), which purportedly allows attackers to gain remote access to UNIX-based systems. The vulnerability, which affects various UNIX-based operating systems, can be exploited by sending a specially crafted HTTP request to the CUPS service. Threat Topography Threat Type: Remote code execution vulnerability in CUPS service Industries Impacted: UNIX-based systems across various industries, including but not limited to, finance, healthcare,…

X-Force discovers new vulnerabilities in smart treadmill

7 min read - This research was made possible thanks to contributions from Joshua Merrill. Smart gym equipment is seeing rapid growth in the fitness industry, enabling users to follow customized workouts, stream entertainment on the built-in display, and conveniently track their progress. With the multitude of features available on these internet-connected machines, a group of researchers at IBM X-Force Red considered whether user data was secure and, more importantly, whether there was any risk to the physical safety of users. One of the most…

Topic updates

Get email updates and stay ahead of the latest threats to the security landscape, thought leadership and research.
Subscribe today