Skip to content

Commit e6913b4

Browse files
author
HackTricks News Bot
committed
Add content from: Research Update Enhanced src/binary-exploitation/stack-overf...
1 parent e77fbe1 commit e6913b4

1 file changed

Lines changed: 27 additions & 6 deletions

File tree

src/binary-exploitation/stack-overflow/ret2win/ret2win-arm64.md

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ clang -o ret2win ret2win.c -fno-stack-protector -Wno-format-security -no-pie -mb
4646

4747
- 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`.
4848
- This means the saved return address lives at `sp+8` relative to the frame base. With a `char buffer[64]` placed below, the usual overwrite distance to the saved `x30` is 64 (buffer) + 8 (saved x29) = 72 bytes — exactly what we’ll find below.
49-
- 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.
49+
- 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.
50+
51+
### Why partial overwrites work so well on AArch64
52+
53+
- AArch64 Linux is usually **little-endian**, so the first byte you overwrite in memory is the **least significant byte** of the saved `x30`. That is why a short overwrite with `p8()`/`p16()` can retarget the return address without touching the higher bytes.
54+
- On PIE binaries, **the page offset stays constant** after relocation. In practice the lowest **12 bits** of a function address are preserved by ASLR, so a **1-byte overwrite** can only move within the same `0x100` window and a **2-byte overwrite** can only move within the same `0x10000` window.
55+
- Therefore, before attempting a partial ret2win, compare the original saved return address with the target `win()` address. If they differ outside those low bytes, a 1- or 2-byte overwrite is not enough and you need either a leak or a larger overwrite primitive.
5056

5157
## Finding the offset
5258

@@ -177,7 +183,17 @@ You can find another off-by-one example in ARM64 in [https://8ksec.io/arm64-reve
177183
178184
### Off-by-2
179185

180-
Without a leak we don't know the exact address of the winning function but we can know the offset of the function from the binary and knowing that the return address we are overwriting is already pointing to a close address, it's possible to leak the offset to the win function (**0x7d4**) in this case and just use that offset:
186+
Without a leak we don't know the exact address of the winning function but we can know the offset of the function from the binary and, because the return address we are overwriting already points inside the same PIE image, we can often redirect it by changing only the low bytes. In this example the relevant offset to `win()` is **0x7d4** and a 2-byte overwrite is enough because the saved return address and `win()` still share the same higher bytes.
187+
188+
A quick way to sanity-check this before writing the exploit is to compare both addresses in the debugger and keep only the low bytes you really need to change:
189+
190+
```text
191+
saved x30 : 0x0000aaaaaa00079c
192+
win() : 0x0000aaaaaa0007d4
193+
^^^^
194+
```
195+
196+
Only the last two bytes differ here, so `p16(0x07d4)` is enough. If your target looked like `0x0000aaaaab1207d4`, the higher bytes changed as well and the same trick would fail.
181197

182198
<figure><img src="../../../images/image (1213).png" alt="" width="563"><figcaption></figcaption></figure>
183199

@@ -482,12 +498,15 @@ p.interactive()
482498

483499
## Notes on modern AArch64 hardening (PAC/BTI) and ret2win
484500

501+
- 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.
485502
- 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:
486503
- Returning to an address that is not a valid BTI landing pad may raise a `SIGILL`. Prefer targeting the exact function entry that contains `bti c`.
487-
- 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.
504+
- `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`.
505+
- 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.
488506
- To check features quickly:
489507
- `readelf --notes -W ./ret2win` and look for `AARCH64_FEATURE_1_BTI` / `AARCH64_FEATURE_1_PAC` notes.
490508
- `objdump -d ./ret2win | head -n 40` and look for `bti c`, `paciasp`, `autiasp`.
509+
- `readelf -n ./ret2win | grep -A1 'AArch64 feature'` is useful to confirm whether the linker actually kept the GNU property note.
491510

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

@@ -503,7 +522,9 @@ qemu-aarch64 -L /usr/aarch64-linux-gnu ./ret2win
503522
# Debug with GDB (qemu-user gdbstub)
504523
qemu-aarch64 -g 1234 -L /usr/aarch64-linux-gnu ./ret2win &
505524
# In another terminal
506-
gdb-multiarch ./ret2win -ex 'target remote :1234'
525+
gdb-multiarch ./ret2win -ex 'set architecture arm64' -ex 'target remote :1234'
526+
# If symbols for shared libraries are missing inside GDB
527+
(gdb) set solib-search-path /usr/aarch64-linux-gnu/lib/
507528
```
508529

509530
### Related HackTricks pages
@@ -522,6 +543,6 @@ gdb-multiarch ./ret2win -ex 'target remote :1234'
522543

523544
## References
524545

525-
- Enabling PAC and BTI on AArch64 for 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
526-
- Procedure Call Standard for the Arm 64-bit Architecture (AAPCS64). https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst
546+
- GCC AArch64 options (`-mbranch-protection=standard`, `pac-ret`, `bti`). https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
547+
- Enabling PAC and BTI on AArch64 for 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
527548
{{#include ../../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)