Heap Overflows on iOS ARM64: Heap Grooming, Use-After-Free (Part 3)
Welcome back to part 3 of my iOS arm64 exploitation series!
If you’ve missed the blogs in the series, check them out below ^_^
Part 1: How to Reverse Engineer and Patch an iOS Application for Beginners
Part 2: Guide to Reversing and Exploiting iOS binaries: ARM64 ROP Chains
If you’re more of a visual learner – I have filmed a YouTube video on this that you can check out! The YouTube video does not go into the same level of depth as this blog post will, so just keep that in mind.
We’ve gone through iOS hooking, buffer overflows and simple ROP chains on ARM64. Now it’s time to talk about heap overflows and exploiting use-after-free (UAF) bugs. The goal of this blog is to show you how a UAF bug can be exploited and turned into something “malicious”. I will be walking you through step-by-step the following things:
- How to identify a UAF bug
- How to statically analyse the binary to figure out how to perform the exploitation
- Heap overflow logic
- Heap grooming
- Full exploitation
As always, we will be using open-source tools to perform this and I have compiled, signed, and uploaded the exercise binary that we will use for this exercise. I have also uploaded the source code for those of you that want to read it. This is all available for you to download on my Github:
This blog has been broken down into five parts:
- High level walkthrough of the steps
- Introduction to the exercise binary
- Static analysis of the binary using Radare2
- ARM64 heap overflow
These are the high-level steps we will take during this exercise, and I will explain each one in detail. If detail isn’t your kinda thing, then just peep my YouTube video:
- Upload the binary to your jailbroken iOS device via SFTP to the /var/mobile directory. I am running a jailbroken iOS 14 device called “honeypot”
- Find the UAF vulnerability by playing around with the program
- Statically analyse the binary using R2 to locate any meaningful functions that we can manipulate
- Set up a remote debug session on the iOS device using LLDB
- Debug the binary and set breakpoints after memory allocation to fetch the heap address where the objects will be allocated to
- Perform heap grooming by populating areas of the heap until all the free lists are emptied and the only “free gap” to write to is the initial freed object on the heap
- Perform the heap overflow
- Invoke a function call to trigger the payload
INTRODUCTION TO THE EXERCISE BINARY
This binary is called “moneymachine” and it’s a MACH-O 64-bit binary. It was inspired by the internet and all my weird friends who grew up playing Runscape! Lol. The name of the binary is a reference to the 100 gecs iconic meme song “money machine”.
Anyway, when you download the binary and run the binary, there are 6 option to choose from (as pictured below).
The goal of the exercise is to exploit a heap overflow and perform heap grooming until you can buy the legendary item! There is an embedded use-after-free vulnerability coded into this binary. Please note, no security controls need to be disabled for this exploit to work. ;)
When you select , the program will call ‘malloc’ and allocate memory on the heap for your quest (in this instance, Goblin diplomacy). The “quest” is a struct – an object which stores the name of the quest along with a function pointer to a function which lists all the available quests .
STATIC ANALYSIS USING R2 AND LLDB
- r2 <binaryname>
- aaa – to trigger r2 to analyse the flags, function calls, bytes etc
- afl – lists all the functions
- s sym._buyItems
- First, we notice there is a variable called “sym._money”, we can guess variable may be storing how much gold we have
- The value of “sym._money” is stored in the w8 register
- The function moves the value of 999999 into w9 register (as highlighted in purple). This is represented as 0XF423F in hex.
- There is a comparison being done between w9 and w8 (comparing our current money with the value of 999999)
- The program branches to two different locations depending on if the amount of money we have is greater than or equal to 999999 (b.ge), otherwise it jumps somewhere else
Now with this knowledge, it’s immediately clear we need to find a way to get more money to exploit this program.
- Stores the value of sym._money inside the w8 register
- Stores the value of 0xF4240 in w10 register which corresponds to the value “1000,000”
- Adds the value from w10 register to the w8 register
- Prints something
- malloc – something is allocating memory on the heap
- free – something is being freed on the heap
- fread/fopen – something is being read from a file
HEAP OVERFLOW WITH HEAP GROOMING
- Allocate a new quest on the heap with option 
- Free the new object on the heap 
- Read data from a file by calling  to import items from a file
- Use the data from the file to overwrite the original quest heap object
- Overwrite the function call to listQuests() on the heap with the address of makeItRain()
- Trigger the use-after-free by calling  available quests
- See if the exploit was successful by calling  to buy legendary items
- Get the heap address where the quest is allocated to
- Get the heap address where option  is writing the file contents to
- Ensure that the two heap addresses are the same, otherwise perform heap grooming
On your workstation, open LLDB and connect to the running process.
- disass -n main
- br s -a 0x1028637bc
- process interrupt
- x/64 0x0000000102a04510