Translated ['src/macos-hardening/macos-security-and-privilege-escalation

This commit is contained in:
Translator 2025-07-23 10:36:01 +00:00
parent 39711e21dd
commit 45ecaf0f0b

View File

@ -9,47 +9,47 @@
## 1. Thread Hijacking
처음에, **`task_threads()`** 함수가 원격 작업에서 스레드 목록을 얻기 위해 작업 포트에서 호출됩니다. 스레드가 하이재킹을 위해 선택됩니다. 이 접근 방식은 새로운 완화 조치가 `thread_create_running()`을 차단하기 때문에 기존의 코드 주입 방법과 다릅니다.
처음에, `task_threads()` 함수가 원격 작업에서 스레드 목록을 얻기 위해 작업 포트에서 호출됩니다. 스레드가 하이재킹을 위해 선택됩니다. 이 접근 방식은 `thread_create_running()`을 차단하는 완화 조치로 인해 새로운 원격 스레드를 생성하는 것이 금지되므로 기존의 코드 주입 방법과 다릅니다.
스레드를 제어하기 위해 **`thread_suspend()`**가 호출되어 실행이 중단됩니다.
스레드를 제어하기 위해 `thread_suspend()`가 호출되어 실행이 중단됩니다.
원격 스레드에서 허용되는 유일한 작업은 **중지****시작**과 **레지스터** 값을 **가져오기****수정하기**입니다. 원격 함수 호출은 레지스터 `x0`에서 `x7`을 **인수**로 설정하고, **`pc`**를 원하는 함수로 설정한 후 스레드를 활성화하여 시작됩니다. 반환 후 스레드가 충돌하지 않도록 하려면 반환을 감지해야 합니다.
원격 스레드에서 허용되는 유일한 작업은 **중지****시작**과 **레지스터 값**을 **가져오고**/**수정하는** 것입니다. 원격 함수 호출은 레지스터 `x0`에서 `x7`을 **인수**로 설정하고, `pc`를 원하는 함수로 설정한 후 스레드를 재개하여 시작됩니다. 반환 후 스레드가 충돌하지 않도록 하려면 반환을 감지해야 합니다.
한 가지 전략은 `thread_set_exception_ports()`를 사용하여 원격 스레드에 대한 예외 처리기를 **등록**하고, 함수 호출 전에 `lr` 레지스터를 잘못된 주소로 설정하는 것입니다. 이는 함수 실행 후 예외를 발생시켜 예외 포트에 메시지를 보내고, 스레드의 상태를 검사하여 반환 값을 복구할 수 있게 합니다. 또는 Ian Beer의 triple_fetch exploit에서 채택한 대로 `lr`을 무한 루프에 설정할 수 있습니다. 그런 다음 스레드의 레지스터를 지속적으로 모니터링하여 **`pc`가 해당 명령어를 가리킬 때까지** 대기합니다.
한 가지 전략은 `thread_set_exception_ports()`를 사용하여 원격 스레드에 대한 **예외 처리기**를 등록하고, 함수 호출 전에 `lr` 레지스터를 잘못된 주소로 설정하는 것입니다. 이는 함수 실행 후 예외를 발생시켜 예외 포트에 메시지를 전송하고, 스레드의 상태를 검사하여 반환 값을 복구할 수 있게 합니다. 또는 Ian Beer의 *triple_fetch* 익스플로잇에서 채택한 대로, `lr`을 무한 루프에 설정하여 스레드의 레지스터를 지속적으로 모니터링하다가 `pc`가 해당 명령어를 가리킬 때까지 기다립니다.
## 2. Mach ports for communication
다음 단계는 원격 스레드와의 통신을 용이하게 하기 위해 Mach 포트를 설정하는 것입니다. 이러한 포트는 작업 간에 임의의 전송 및 수신 권한을 전송하는 데 필수적입니다.
다음 단계는 원격 스레드와의 통신을 용이하게 하기 위해 Mach 포트를 설정하는 것입니다. 이러한 포트는 작업 간에 임의의 송신/수신 권한을 전송하는 데 필수적입니다.
양방향 통신을 위해 두 개의 Mach 수신 권한이 생성됩니다: 하나는 로컬 작업에, 다른 하나는 원격 작업에 있습니다. 이후 각 포트에 대한 송 권한이 상대 작업으로 전송되어 메시지 교환이 가능해집니다.
양방향 통신을 위해 두 개의 Mach 수신 권한이 생성됩니다: 하나는 로컬 작업에, 다른 하나는 원격 작업에 있습니다. 이후 각 포트에 대한 송 권한이 상대 작업으로 전송되어 메시지 교환이 가능해집니다.
로컬 포트에 집중하면, 수신 권한은 로컬 작업에 의해 보유됩니다. 포트는 `mach_port_allocate()`로 생성됩니다. 이 포트에 대한 전송 권한을 원격 작업으로 전송하는 것이 도전 과제가 됩니다.
로컬 포트에 집중하면, 수신 권한은 로컬 작업에 의해 보유됩니다. 포트는 `mach_port_allocate()`로 생성됩니다. 이 포트에 송 권한을 원격 작업으로 전송하는 것이 도전 과제가 됩니다.
전략은 `thread_set_special_port()`를 활용하여 원격 스레드의 `THREAD_KERNEL_PORT`에 로컬 포트에 대한 송 권한을 배치하는 것입니다. 그런 다음 원격 스레드에 `mach_thread_self()`를 호출하여 송 권한을 가져오도록 지시합니다.
전략은 `thread_set_special_port()`를 활용하여 원격 스레드의 `THREAD_KERNEL_PORT`에 로컬 포트에 대한 송 권한을 배치하는 것입니다. 그런 다음 원격 스레드에 `mach_thread_self()`를 호출하여 송 권한을 가져오도록 지시합니다.
원격 포트의 경우, 과정 본질적으로 반대로 진행됩니다. 원격 스레드는 `mach_reply_port()`를 통해 Mach 포트를 생성하도록 지시받습니다(반환 메커니즘 때문에 `mach_port_allocate()`는 적합하지 않습니다). 포트 생성 후, `mach_port_insert_right()`가 원격 스레드에서 호출되어 전송 권한을 설정합니다. 이 권한은 `thread_set_special_port()`를 사용하여 커널에 저장됩니다. 로컬 작업으로 돌아가서, `thread_get_special_port()`를 사용하여 원격 작업의 새로 할당된 Mach 포트에 대한 송 권한을 획득합니다.
원격 포트의 경우, 과정 본질적으로 반대로 진행됩니다. 원격 스레드는 `mach_reply_port()`를 통해 Mach 포트를 생성하도록 지시받습니다(반환 메커니즘 때문에 `mach_port_allocate()`는 적합하지 않음). 포트가 생성되면, 원격 스레드에서 `mach_port_insert_right()`가 호출되어 송신 권한이 설정됩니다. 이 권한은 `thread_set_special_port()`를 사용하여 커널에 저장됩니다. 로컬 작업으로 돌아가서, `thread_get_special_port()`를 사용하여 원격 작업의 새로 할당된 Mach 포트에 대한 송 권한을 획득합니다.
이 단계가 완료되면 Mach 포트가 설정되어 양방향 통신을 위한 기초가 마련됩니다.
## 3. Basic Memory Read/Write Primitives
이 섹션에서는 기본 메모리 읽기쓰기 원시 작업을 설정하기 위해 실행 원시 작업을 활용하는 데 중점을 둡니다. 이러한 초기 단계는 원격 프로세스에 대한 더 많은 제어를 얻는 데 중요하지만, 이 단계의 원시 작업은 많은 용도로 사용되지 않을 것입니다. 곧 더 고급 버전으로 업그레이드될 것입니다.
이 섹션에서는 기본 메모리 읽기/쓰기 원시 작업을 설정하기 위해 실행 원시 작업을 활용하는 데 중점을 둡니다. 이러한 초기 단계는 원격 프로세스에 대한 더 많은 제어를 얻는 데 중요하지만, 이 단계의 원시 작업은 많은 용도로 사용되지 않을 것입니다. 곧 더 고급 버전으로 업그레이드될 것입니다.
### Memory Reading and Writing Using Execute Primitive
### Memory reading and writing using the execute primitive
목표는 특정 함수를 사용하여 메모리 읽기 및 쓰기를 수행하는 것입니다. 메모리를 읽기 위해 다음과 유사한 구조의 함수가 사용됩니다:
목표는 특정 함수를 사용하여 메모리 읽기 및 쓰기를 수행하는 것입니다. **메모리 읽기**:
```c
uint64_t read_func(uint64_t *address) {
return *address;
}
```
메모리에 쓰기 위해, 이 구조와 유사한 함수들이 사용됩니다:
**메모리 쓰기**:
```c
void write_func(uint64_t *address, uint64_t value) {
*address = value;
}
```
이 함수는 주어진 어셈블리 명령어에 해당합니다:
이 함수들은 다음 어셈블리에 해당합니다:
```
_read_func:
ldr x0, [x0]
@ -62,100 +62,112 @@ ret
일반 라이브러리를 스캔한 결과 이러한 작업에 적합한 후보가 발견되었습니다:
1. **메모리 읽기:**
`property_getName()` 함수는 [Objective-C 런타임 라이브러리](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html)에서 메모리를 읽기 위한 적합한 함수로 확인되었습니다. 함수는 아래에 설명되어 있습니다:
1. **메모리 읽기 — `property_getName()`** (libobjc):
```c
const char *property_getName(objc_property_t prop) {
return prop->name;
}
```
이 함수는 `objc_property_t`의 첫 번째 필드를 반환함으로써 효과적으로 `read_func`처럼 작동합니다.
2. **메모리 쓰기:**
메모리를 쓰기 위한 미리 구축된 함수를 찾는 것은 더 어려운 일입니다. 그러나 libxpc의 `_xpc_int64_set_value()` 함수는 다음과 같은 디스어셈블리와 함께 적합한 후보입니다:
2. **메모리 쓰기 — `_xpc_int64_set_value()`** (libxpc):
```c
__xpc_int64_set_value:
str x1, [x0, #0x18]
ret
```
특정 주소에 64비트 쓰기를 수행하기 위해 원격 호출은 다음과 같이 구성됩니다:
임의의 주소에 64비트 쓰기를 수행하려면:
```c
_xpc_int64_set_value(address - 0x18, value)
_xpc_int64_set_value(address - 0x18, value);
```
이러한 원시 기능이 설정되면, 원격 프로세스를 제어하는 데 있어 중요한 전을 이루는 공유 메모리를 생성할 준비가 됩니다.
이러한 원시 기능이 설정되면, 원격 프로세스를 제어하는 데 있어 중요한 전을 이루는 공유 메모리를 생성할 준비가 됩니다.
## 4. 공유 메모리 설정
목표는 로컬 및 원격 작업 간에 공유 메모리를 설정하여 데이터 전송을 간소화하고 여러 인수를 가진 함수 호출을 용이하게 하는 것입니다. 이 접근 방식은 Mach 메모리 항목을 기반으로 하는 `libxpc`와 그 `OS_xpc_shmem` 객체 유형을 활용하는 것입니다.
목표는 로컬 및 원격 작업 간에 공유 메모리를 설정하여 데이터 전송을 간소화하고 여러 인수를 가진 함수 호출을 용이하게 하는 것입니다. 이 접근 방식은 `libxpc`와 Mach 메모리 항목을 기반으로 구축된 `OS_xpc_shmem` 객체 유형을 활용합니다.
### 프로세스 개요:
### 프로세스 개요
1. **메모리 할당**:
- `mach_vm_allocate()`를 사용하여 공유할 메모리를 할당합니다.
- 할당된 메모리 영역에 대해 `OS_xpc_shmem` 객체를 생성하기 위해 `xpc_shmem_create()`를 사용합니다. 이 함수는 Mach 메모리 항목의 생성을 관리하고 `OS_xpc_shmem` 객체의 오프셋 `0x18`에 Mach 전송 권한을 저장합니다.
2. **원격 프로세스에서 공유 메모리 생성**:
- 원격 호출을 통해 원격 프로세스에서 `OS_xpc_shmem` 객체를 위한 메모리를 할당합니다.
- 로컬 `OS_xpc_shmem` 객체의 내용을 원격 프로세스로 복사합니다. 그러나 이 초기 복사는 오프셋 `0x18`에서 잘못된 Mach 메모리 항목 이름을 가질 것입니다.
3. **Mach 메모리 항목 수정**:
- `thread_set_special_port()` 메서드를 사용하여 원격 작업에 Mach 메모리 항목에 대한 전송 권한을 삽입합니다.
- 원격 메모리 항목의 이름으로 오프셋 `0x18`의 Mach 메모리 항목 필드를 덮어씁니다.
4. **공유 메모리 설정 완료**:
- 원격 `OS_xpc_shmem` 객체를 검증합니다.
- `xpc_shmem_remote()`에 대한 원격 호출로 공유 메모리 매핑을 설정합니다.
이 단계를 따르면 로컬 및 원격 작업 간에 공유 메모리가 효율적으로 설정되어 데이터 전송과 여러 인수를 요구하는 함수 실행이 간단해집니다.
## 추가 코드 스니펫
메모리 할당 및 공유 메모리 객체 생성을 위한:
```c
mach_vm_allocate();
xpc_shmem_create();
```
원격 프로세스에서 공유 메모리 객체를 생성하고 수정하기 위해:
```c
malloc(); // for allocating memory remotely
thread_set_special_port(); // for inserting send right
```
Mach 포트와 메모리 항목 이름의 세부 사항을 올바르게 처리하여 공유 메모리 설정이 제대로 작동하도록 해야 합니다.
1. **메모리 할당**
* `mach_vm_allocate()`를 사용하여 공유할 메모리를 할당합니다.
* 할당된 영역에 대해 `xpc_shmem_create()`를 사용하여 `OS_xpc_shmem` 객체를 생성합니다.
2. **원격 프로세스에서 공유 메모리 생성**
* 원격 프로세스에서 `OS_xpc_shmem` 객체를 위한 메모리를 할당합니다 (`remote_malloc`).
* 로컬 템플릿 객체를 복사합니다; `0x18` 오프셋에서 내장된 Mach 전송 권한의 수정이 여전히 필요합니다.
3. **Mach 메모리 항목 수정**
* `thread_set_special_port()`로 전송 권한을 삽입하고 `0x18` 필드를 원격 항목의 이름으로 덮어씁니다.
4. **최종화**
* 원격 객체를 검증하고 `xpc_shmem_remote()`에 대한 원격 호출로 매핑합니다.
## 5. 완전한 제어 달성
공유 메모리를 성공적으로 설정하고 임의 실행 기능을 얻으면 본질적으로 대상 프로세스에 대한 완전한 제어를 얻게 됩니다. 이 제어를 가능하게 하는 주요 기능은 다음과 같습니다:
임의 실행 및 공유 메모리 백 채널이 가능해지면, 효과적으로 대상 프로세스를 소유하게 됩니다:
1. **임의 메모리 작업**:
* **임의 메모리 R/W** — 로컬 및 공유 영역 간에 `memcpy()`를 사용합니다.
* **8개 이상의 인수를 가진 함수 호출** — arm64 호출 규약에 따라 스택에 추가 인수를 배치합니다.
* **Mach 포트 전송** — 설정된 포트를 통해 Mach 메시지에서 권한을 전달합니다.
* **파일 설명자 전송** — 파일 포트를 활용합니다 (참조: *triple_fetch*).
- `memcpy()`를 호출하여 공유 영역에서 데이터를 복사하여 임의 메모리 읽기를 수행합니다.
- `memcpy()`를 사용하여 공유 영역으로 데이터를 전송하여 임의 메모리 쓰기를 실행합니다.
이 모든 것은 쉽게 재사용할 수 있도록 [`threadexec`](https://github.com/bazad/threadexec) 라이브러리에 포장되어 있습니다.
2. **다중 인수를 가진 함수 호출 처리**:
---
- 8개 이상의 인수를 요구하는 함수의 경우, 호출 규약에 따라 추가 인수를 스택에 배치합니다.
## 6. Apple Silicon (arm64e) 뉘앙스
3. **Mach 포트 전송**:
Apple Silicon 장치(arm64e)에서는 **포인터 인증 코드(PAC)**가 모든 반환 주소와 많은 함수 포인터를 보호합니다. 기존 코드를 재사용하는 스레드 하이재킹 기술은 `lr`/`pc`의 원래 값이 이미 유효한 PAC 서명을 가지고 있기 때문에 계속 작동합니다. 공격자가 제어하는 메모리로 점프하려고 할 때 문제가 발생합니다:
- 이전에 설정된 포트를 통해 Mach 메시지를 통해 작업 간에 Mach 포트를 전송합니다.
1. 대상 내부에 실행 가능한 메모리를 할당합니다 (원격 `mach_vm_allocate` + `mprotect(PROT_EXEC)`).
2. 페이로드를 복사합니다.
3. *원격* 프로세스 내에서 포인터에 서명합니다:
```c
uint64_t ptr = (uint64_t)payload;
ptr = ptrauth_sign_unauthenticated((void*)ptr, ptrauth_key_asia, 0);
```
4. 하이재킹된 스레드 상태에서 `pc = ptr` 설정.
4. **파일 설명자 전송**:
- Ian Beer가 `triple_fetch`에서 강조한 기술인 fileports를 사용하여 프로세스 간에 파일 설명자를 전송합니다.
또는 기존의 가젯/함수를 연결하여 PAC 준수를 유지합니다 (전통적인 ROP).
이 포괄적인 제어는 [threadexec](https://github.com/bazad/threadexec) 라이브러리에 캡슐화되어 있으며, 피해자 프로세스와의 상호 작용을 위한 상세한 구현과 사용자 친화적인 API를 제공합니다.
## 7. 탐지 및 EndpointSecurity를 통한 강화
## 중요한 고려 사항:
**EndpointSecurity (ES)** 프레임워크는 방어자가 스레드 주입 시도를 관찰하거나 차단할 수 있도록 하는 커널 이벤트를 노출합니다:
- 시스템 안정성과 데이터 무결성을 유지하기 위해 메모리 읽기/쓰기 작업에 `memcpy()`를 적절히 사용해야 합니다.
- Mach 포트나 파일 설명자를 전송할 때는 적절한 프로토콜을 따르고 자원을 책임감 있게 처리하여 누수나 의도하지 않은 접근을 방지해야 합니다.
* `ES_EVENT_TYPE_AUTH_GET_TASK` 프로세스가 다른 작업의 포트를 요청할 때 발생합니다 (예: `task_for_pid()`).
* `ES_EVENT_TYPE_NOTIFY_REMOTE_THREAD_CREATE` *다른* 작업에서 스레드가 생성될 때마다 발생합니다.
* `ES_EVENT_TYPE_NOTIFY_THREAD_SET_STATE` (macOS 14 Sonoma에 추가됨) 기존 스레드의 레지스터 조작을 나타냅니다.
이 가이드라인을 준수하고 `threadexec` 라이브러리를 활용함으로써, 프로세스를 세밀하게 관리하고 상호 작용하여 대상 프로세스에 대한 완전한 제어를 달성할 수 있습니다.
원격 스레드 이벤트를 출력하는 최소한의 Swift 클라이언트:
```swift
import EndpointSecurity
## 참고 문헌
let client = try! ESClient(subscriptions: [.notifyRemoteThreadCreate]) {
(_, msg) in
if let evt = msg.remoteThreadCreate {
print("[ALERT] remote thread in pid \(evt.target.pid) by pid \(evt.thread.pid)")
}
}
RunLoop.main.run()
```
**osquery** ≥ 5.8로 쿼리하기:
```sql
SELECT target_pid, source_pid, target_path
FROM es_process_events
WHERE event_type = 'REMOTE_THREAD_CREATE';
```
### Hardened-runtime considerations
애플리케이션을 `com.apple.security.get-task-allow` 권한 없이 배포하면 비루트 공격자가 해당 작업 포트를 얻는 것을 방지할 수 있습니다. 시스템 무결성 보호(SIP)는 여전히 많은 Apple 바이너스에 대한 접근을 차단하지만, 서드파티 소프트웨어는 명시적으로 옵트아웃해야 합니다.
## 8. Recent Public Tooling (2023-2025)
| Tool | Year | Remarks |
|------|------|---------|
| [`task_vaccine`](https://github.com/rodionovd/task_vaccine) | 2023 | Ventura/Sonoma에서 PAC 인식 스레드 하이재킹을 보여주는 간결한 PoC |
| `remote_thread_es` | 2024 | 여러 EDR 공급자가 `REMOTE_THREAD_CREATE` 이벤트를 표출하는 데 사용하는 EndpointSecurity 헬퍼 |
> 이러한 프로젝트의 소스 코드를 읽는 것은 macOS 13/14에서 도입된 API 변경 사항을 이해하고 Intel ↔ Apple Silicon 간의 호환성을 유지하는 데 유용합니다.
## References
- [https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/](https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/)
- [https://github.com/rodionovd/task_vaccine](https://github.com/rodionovd/task_vaccine)
- [https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create](https://developer.apple.com/documentation/endpointsecurity/es_event_type_notify_remote_thread_create)
{{#include ../../../../banners/hacktricks-training.md}}