Disabling ASLR on 64-bit iOS

Billy Ellis
6 min readOct 10, 2021

Tl;dr: There’s a special flag you can add to a process’s proc structure. Any child process then spawned by the process will be loaded into memory without ASLR.

In my last blog post, where I discussed the ASLR implementation in the iOS kernel, you may remember me writing about this important detail regarding 64-bit iPhones:

…in fact, 64-bit iOS enforces it. If you’ve ever dealt with ASLR on 64-bit iOS, you may have noticed that compiling a binary with -fno-pie (to disable position independent code) has no effect — all processes are launched with ASLR regardless.

(Read the original blog here — https://bellis1000.medium.com/aslr-the-ios-kernel-how-virtual-address-spaces-are-randomised-d76d14dc7ebb)

I wrote this implying that there is no way to disable ASLR for user-space processes on 64-bit iOS, without statically patching the kernel and booting with a bootROM exploit (which is a bit of a hassle).

This is actually not true, as I’ve only recently discovered. So I’m writing this short follow-up post to discuss further :)

32-bit vs 64-bit approach

As a recap, in the last post I demonstrated a method of completely disabling ASLR on iOS by patching the kernel code in a running 32-bit iOS kernel. On 64-bit iOS, patching kernel code is not as straightforward as on 32-bit due to extra restrictions such as KPP/KTRR.

There was also another method I touched on in the previous post — to compile binaries with the -fno-pie flag. While not disabling ASLR system-wide, this flag will stop the compiled binary from launching with ASLR in effect. This does not work on 64-bit iOS however. For whatever reason, this flag is ignored on 64-bit iOS and so any binary compiled with it will still have ASLR enabled.

At the time the last blog post was published, I had only considered these two methods for disabling ASLR.

However, as it turns out there is a third way to disable ASLR that actually works on 64-bit iOS.

This third method does not require any modification to kernel code, nor does it require any special compile flags. All we need is a jailbroken device with task-for-pid-zero access.

The 64-bit method

It might sound like I’m ‘bigging’ this up a bit. I shouldn’t be. After all, this is nothing crazy, not like a new iOS 0day or some ground-breaking discovery. It’s possible people have already been using this method. I just haven’t seen anything online about it, and all other researchers I’ve spoken with didn’t suggest it.

It’s technically already public knowledge, and that’s actually how I first found out about it.

While browsing XNU source code one day I stumbled upon the following comment in the function posix_spawn() in the file kern_exec.c.

The comment here is describing the meaning of a specific flag value that processes can have present on their proc structure.

The last part of the comment details how we can disable ASLR.

… or if P_DISABLE_ASLR was inherited from the parent process.

During the early stages of process creation on iOS, new processes inherit the entire proc structure from the process that initiated its execution (the ‘parent process’).

The majority of processes’ parent on iOS will be launchd. So, if we believe the above comment to be true, in theory we should be able to disable ASLR for most processes/apps by setting this special P_DISABLE_ASLR flag in launchd's proc structure.

New processes launched by launchd will then inherit this special flag, and when mapped into memory, the kernel will not bother generating an ASLR slide for them.

All proc structures are stored on the kernel heap, which of course must be writeable, so we’ll have no issues regarding memory protections like we would if we tried modifying kernel code like with the 32-bit method.

The P_DISABLE_ASLR constant is one of the multiple possible flag bits that can be set on the p_flag property of a proc structure.

Depending on your iOS kernel version, the offset to this flag will differ slightly. On my iOS 12 device, the p_flag was at offset 0x13c from the start of a given proc structure.

The definitions for all of the flag options (including P_DISABLE_ASLR) can be found in the file proc.h in the XNU source code.

As we can see, the constant P_DISABLE_ASLR is defined as value 0x1000 (only bit 12 is set).

There’s probably some interesting functionality to be discovered messing with the other available flags here also, so feel free to explore those.

Setting the P_DISABLE_ASLR flag on an arbitrary process is straightforward enough.

With kernel memory access granted to us by task-for-pid-zero, we can locate an arbitrary proc structure, and then directly write into the memory from user-space to add this special flag.

I have a video on my YouTube channel discussing proc structures in more detail, and how to locate them in the kernel memory here — https://www.youtube.com/watch?v=iXAlfTDSmwU&t=406s&ab_channel=BillyEllis.

Essentially, we can locate the proc structure for a given process by starting with the kernel’s own proc structure (which is at a hard-coded kernel address) and then following the proc->next pointer until we arrive at the desired proc structure, checking the proc->p_ppid value along the way against the process’s PID that we’re searching for.

Once the P_DISABLE_ASLR flag has been added to the p_flag variable in launchd's proc, all apps and processes launched via launchd from this point on will be free of ASLR.

It’s also a good idea to apply the flag to sh (so that binaries you run through terminal also have ASLR disabled), as well as any other process that might launch a child process.

Below is an example output after adding the flag to launchd and sh. As you can see by the size of the pointers, this is a 64-bit iOS device and ASLR is not in effect :)

So yeah, turns out it isn’t too difficult to disable ASLR on 64-bit iOS after all.

For those of you who have contacted me in the past regarding my exploit challenges on 64-bit, you’ll be pleased to know that using this method you will be able to follow along the same way as you would on 32-bit devices (no need to add an info-leak in the earlier challenges).

Check out the challenges here if you’re interested in learning exploit development on ARM — https://github.com/Billy-Ellis/Exploit-Challenges

For anyone wishing to disable ASLR for another reason, hopefully this aids your research.

Have a look at part 1 of this blog if you’re interested in how the kernel actually generates the ASLR slide and applies it to processes — https://bellis1000.medium.com/aslr-the-ios-kernel-how-virtual-address-spaces-are-randomised-d76d14dc7ebb

Feedback always welcome. Thanks :)

— Billy Ellis @bellis1000

--

--