Skip to content

Commit 0f9a1d6

Browse files
authored
Merge pull request #2466 from HackTricks-wiki/research_update_src_binary-exploitation_rop-return-oriented-programing_srop-sigreturn-oriented-programming_srop-arm64_20260704_134603
Research Update Enhanced src/binary-exploitation/rop-return-...
2 parents 5834671 + f2e5722 commit 0f9a1d6

1 file changed

Lines changed: 46 additions & 20 deletions

File tree

  • src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming

src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ int main(int argc, char **argv) {
142142
143143
## Exploit
144144
145-
In the section **`vdso`** it's possible to find a call to **`sigreturn`** in the offset **`0x7b0`**:
145+
In the **example vDSO dump shown below**, it's possible to find a call to **`sigreturn`** at offset **`0x7b0`**:
146146
147147
<figure><img src="../../../images/image (17) (1).png" alt="" width="563"><figcaption></figcaption></figure>
148148
@@ -193,21 +193,33 @@ And to bypass the address of `/bin/sh` you could create several env variables po
193193

194194
---
195195

196-
## Finding `sigreturn` gadgets automatically (2023-2025)
196+
## Locating `rt_sigreturn` on ARM64 (2023-2025)
197197

198-
On modern distributions the `sigreturn` trampoline is still exported by the **vDSO** page but the exact offset may vary across kernel versions and build flags such as BTI (`+branch-protection`) or PAC. Automating its discovery prevents hard-coding offsets:
198+
On Linux/**AArch64** the most convenient SROP gadget is usually the **vDSO** trampoline exported as **`__kernel_rt_sigreturn`**. In current kernels the interesting sequence is intentionally tiny:
199+
200+
```armasm
201+
nop // unwinder marker
202+
__kernel_rt_sigreturn:
203+
mov x8, #0x8b // __NR_rt_sigreturn
204+
svc #0
205+
```
206+
207+
Do **not** hard-code an offset such as `0x7b0` unless you already dumped the exact target **vDSO**. The offset varies across kernel builds, so the reliable workflow is: recover the **vDSO** base, dump that mapping, and resolve `__kernel_rt_sigreturn` inside the dump.
199208

200209
```bash
201-
# With ROPgadget ≥ 7.4
202-
python3 -m ROPGadget --binary /proc/$(pgrep srop)/mem --only "svc #0" 2>/dev/null | grep -i sigreturn
210+
# 1) Find the vDSO mapping
211+
cat /proc/<pid>/maps | grep '\[vdso\]'
203212

204-
# With rp++ ≥ 1.0.9 (arm64 support)
205-
rp++ -f ./binary --unique -r | grep "mov\s\+x8, #0x8b" # 0x8b = __NR_rt_sigreturn
206-
```
213+
# 2) Dump it (replace base/size with the values from maps)
214+
dd if=/proc/<pid>/mem of=vdso bs=1 skip=$((0xBASE)) count=$((0xSIZE))
207215

208-
Both tools understand **AArch64** encodings and will list candidate `mov x8, 0x8b ; svc #0` sequences that can be used as the *SROP gadget*.
216+
# 3) Resolve the trampoline
217+
readelf -Ws vdso | grep rt_sigreturn
218+
objdump -d vdso | sed -n '/__kernel_rt_sigreturn/,+4p'
219+
ROPgadget --binary vdso --only 'svc'
220+
```
209221

210-
> Note: When binaries are compiled with **BTI** the first instruction of every valid indirect branch target is `bti c`. `sigreturn` trampolines placed by the linker already include the correct BTI landing pad so the gadget remains usable from unprivileged code.
222+
This is much more reliable than grepping the main binary because on ARM64 the **SROP entry point usually lives in the vDSO**, not in the target ELF itself.
211223

212224
## Chaining SROP with ROP (pivot via `mprotect`)
213225

@@ -225,23 +237,37 @@ frame.pc = svc_call # will re-enter kernel
225237

226238
After sending the frame you can send a second stage containing raw shell-code at `0x400000+0x100`. Because **AArch64** uses *PC-relative* addressing this is often more convenient than building large ROP chains.
227239

228-
## Kernel validation, PAC & Shadow-Stacks
240+
### Quick debugging note for `pwntools` users
241+
242+
If you are using the stock **AArch64** `SigreturnFrame()` from modern `pwntools`, the serialized frame is **600 bytes** long. Useful offsets when debugging corrupted payloads are:
243+
244+
* `x8` (syscall number): `0x178`
245+
* `sp`: `0x230`
246+
* `pc`: `0x238`
247+
248+
If your payload reaches the `svc #0` but the kernel rejects the frame, checking these offsets in a hexdump is often faster than single-stepping the whole chain.
249+
250+
## Modern kernel parsing and branch-protection caveats
229251

230-
Linux 5.16 introduced stricter validation of userspace signal frames (commit `36f5a6c73096`). The kernel now checks:
252+
Recent **arm64** kernels are stricter than many old SROP writeups assume. When you hand-craft a frame, keep in mind that:
231253

232-
* `uc_flags` must contain `UC_FP_XSTATE` when `extra_context` is present.
233-
* The reserved word in `struct rt_sigframe` must be zero.
234-
* Every pointer in the *extra_context* record is aligned and points inside the user address space.
254+
* `sigcontext.__reserved` parsing is **16-byte aligned**.
255+
* Only **one** `extra_context` record is accepted.
256+
* If `extra_context` is used, its `datap` pointer must point exactly to the extra data area and the total extra size must stay inside the accepted signal-frame limits.
257+
* `fpsimd_context` is still **mandatory** on systems that support FPSIMD, even if you only care about controlling `x0-x30`, `sp`, and `pc`.
258+
* If you include `sve_context`/`za_context`, the kernel validates their sizes and **current vector length**. In practice, `rt_sigreturn` is not a clean way to change VL mid-exploit.
259+
* On newer kernels there are even more optional records such as **GCS**, **POE**, and **FPMR** contexts, which is another reason to prefer the smallest valid frame possible.
235260

236-
`pwntools>=4.10` crafts compliant frames automatically, but if you build them manually make sure to zero‐initialize *reserved* and omit the SVE record unless you really need it—otherwise `rt_sigreturn` will deliver `SIGSEGV` instead of returning.
261+
Therefore, the most reliable offensive strategy is usually to keep the frame **minimal**: use the default `SigreturnFrame()` layout and avoid adding SVE/SME records unless you explicitly need them.
237262

238-
Starting with mainstream Android 14 and Fedora 38, userland is compiled with **PAC** (*Pointer Authentication*) and **BTI** enabled by default (`-mbranch-protection=standard`). *SROP* itself is unaffected because the kernel overwrites `PC` directly from the crafted frame, bypassing the authenticated LR saved on the stack; however, any **subsequent ROP chain** that performs indirect branches must jump to BTI-enabled instructions or PACed addresses. Keep that in mind when choosing gadgets.
263+
For hardened userlands, also keep in mind:
239264

240-
Shadow-Call-Stacks introduced in ARMv8.9 (and already enabled on ChromeOS 1.27+) are a compiler-level mitigation and *do not* interfere with SROP because no return instructions are executed—the flow of control is transferred by the kernel.
265+
* With **PAC** (`paciasp` / `autiasp`), a plain saved-`x30` overwrite may die before you ever reach the SROP trampoline if the function epilogue authenticates `LR`.
266+
* With **BTI**, indirect `br` / `blr` targets must land on a valid landing pad. The arm64 **vDSO** `__kernel_rt_sigreturn` trampoline intentionally omits `bti c` because the kernel expects it to be reached from a `ret`. So returning to it is fine, but jumping to it with a BTI-checked branch gadget can fault.
241267

242268
## References
243269

244-
* [Linux arm64 signal handling documentation](https://docs.kernel.org/arch/arm64/signal.html)
245-
* [LWN – "AArch64 branch protection comes to GCC and glibc" (2023)](https://lwn.net/Articles/915041/)
270+
* [Linux arm64 vDSO `__kernel_rt_sigreturn` source](https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S)
271+
* [Linux arm64 signal-frame parsing (`signal.c`)](https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c)
246272

247273
{{#include ../../../banners/hacktricks-training.md}}

0 commit comments

Comments
 (0)