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
The comment here is describing the meaning of a specific flag value that processes can have present on their
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
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.
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.
P_DISABLE_ASLR constant is one of the multiple possible flag bits that can be set on the
p_flag property of a
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
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.
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.
P_DISABLE_ASLR flag has been added to the
p_flag variable in
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
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