Skip to content

Commit babb4b1

Browse files
carlospolopgitbook-bot
authored andcommitted
GITBOOK-4332: No subject
1 parent 3f977cd commit babb4b1

File tree

1 file changed

+74
-10
lines changed
  • macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication

1 file changed

+74
-10
lines changed

macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/README.md

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -439,18 +439,75 @@ int main() {
439439
{% endtab %}
440440
{% endtabs %}
441441

442-
### Privileged Ports
442+
## Privileged Ports
443443

444-
* **Host port**: If a process has **Send** privilege over this port he can get **information** about the **system** (e.g. `host_processor_info`).
445-
* **Host priv port**: A process with **Send** right over this port can perform **privileged actions** like loading a kernel extension. The **process need to be root** to get this permission.
444+
There are some special ports that allows to **perform certain sensitive actions or access certain sensitive data** in case a tasks have the **SEND** permissions over them. This makes these ports very interesting from an attackers perspective not only because of the capabilities but because it's possible to **share SEND permissions across tasks**.
445+
446+
### Host Special Ports
447+
448+
These ports are represented by a number.
449+
450+
**SEND** rights can be obtained by calling **`host_get_special_port`** and **RECEIVE** rights calling **`host_set_special_port`**. However, both calls require the **`host_priv`** port which only root can access. Moreover, in the past root was able to call **`host_set_special_port`** and hijack arbitrary that allowed for example to bypass code signatures by hijacking `HOST_KEXTD_PORT` (SIP now prevents this).
451+
452+
These are divided in 2 groups: The **first 7 ports are owned by the kernel** being the 1 `HOST_PORT`, the 2 `HOST_PRIV_PORT` , the 3 `HOST_IO_MASTER_PORT` and the 7 is `HOST_MAX_SPECIAL_KERNEL_PORT`.\
453+
The ones starting **from** the number **8** are **owned by system daemons** and they can be found declared in [**`host_special_ports.h`**](https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/mach/host\_special\_ports.h.auto.html).
454+
455+
* **Host port**: If a process has **SEND** privilege over this port he can get **information** about the **system** calling its routines like:
456+
* `host_processor_info`: Get processor info
457+
* `host_info`: Get host info
458+
* `host_virtual_physical_table_info`: Virtual/Physical page table (requires MACH\_VMDEBUG)
459+
* `host_statistics`: Get host statistics
460+
* `mach_memory_info`: Get kernel memory layout
461+
* **Host Priv port**: A process with **SEND** right over this port can perform **privileged actions** like showing boot data or trying to load a kernel extension. The **process need to be root** to get this permission.
446462
* Moreover, in order to call **`kext_request`** API it's needed to have other entitlements **`com.apple.private.kext*`** which are only given to Apple binaries.
447-
* **Task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`.
448-
* **Task port** (aka kernel port)**:** With Send permission over this port it's possible to control the task (read/write memory, create threads...).
449-
* Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`in a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes\_to\_xnu\_mach\_ipc.html) while doing a `fork()`.
450-
* These are the restrictions to access the port (from `macos_task_policy` from the binary `AppleMobileFileIntegrity`):
451-
* If the app has **`com.apple.security.get-task-allow` entitlement** processes from the **same user can access the task port** (commonly added by Xcode for debugging). The **notarization** process won't allow it to production releases.
452-
* Apps with the **`com.apple.system-task-ports`** entitlement can get the **task port for any** process, except the kernel. In older versions it was called **`task_for_pid-allow`**. This is only granted to Apple applications.
453-
* **Root can access task ports** of applications **not** compiled with a **hardened** runtime (and not from Apple).
463+
* Other routines that can be called are:
464+
* `host_get_boot_info`: Get `machine_boot_info()`
465+
* `host_priv_statistics`: Get privileged statistics
466+
* `vm_allocate_cpm`: Allocate Contiguous Physical Memory
467+
* `host_processors`: Send right to host processors
468+
* `mach_vm_wire`: Make memory resident
469+
* As **root** can access this permission, it could call `host_set_[special/exception]_port[s]` to **hijack host special or exception ports**.
470+
471+
It's possible to **see all the host special ports** by running:
472+
473+
```bash
474+
procexp all ports | grep "HSP"
475+
```
476+
477+
### Task Ports
478+
479+
Originally Mach didn't have "processes" it had "tasks" which was considered more like a container of threads. When Mach was merged with BSD **each task was correlated with a BSD process**. Therefore every BSD process has the details it needs to be a process and every Mach task also have its inner workings (except for the inexistent pid 0 which is the `kernel_task`).
480+
481+
There are two very interesting functions related to this:
482+
483+
* `task_for_pid(target_task_port, pid, &task_port_of_pid)`: Get a SEND right for the task por of the task related to the specified by the `pid` and give it to the indicated `target_task_port` (which is usually the caller task which has used `mach_task_self()`, but could be a SEND port over a different task.)
484+
* `pid_for_task(task, &pid)`: Given a SEND right to a task, find to which PID this task is related to.
485+
486+
In order to perform actions within the task, the task needed a `SEND` right to itself calling `mach_task_self()` (which uses the `task_self_trap` (28)). With this permission a task can perform several actions like:
487+
488+
* `task_threads`: Get SEND right over all task ports of the threads of the task
489+
* `task_info`: Get info about a task
490+
* `task_suspend/resume`: Suspend or resume a task
491+
* `task_[get/set]_special_port`
492+
* `thread_create`: Create a thread
493+
* `task_[get/set]_state`: Control task state
494+
* and more can be found in [**mach/task.h**](https://github.com/phracker/MacOSX-SDKs/blob/master/MacOSX11.3.sdk/System/Library/Frameworks/Kernel.framework/Versions/A/Headers/mach/task.h)
495+
496+
{% hint style="danger" %}
497+
Notice that with a SEND right over a task port of a **different task**, it's possible to perform such actions over a different task.
498+
{% endhint %}
499+
500+
Moreover, the task\_port is also the **`vm_map`** port which allows to **read an manipulate memory** inside a task with functions such as `vm_read()` and `vm_write()`. This basically means that a task with SEND rights over the task\_port of a different task is going to be able to **inject code into that task**.
501+
502+
Remember that because the **kernel is also a task**, if someone manages to get a **SEND permissions** over the **`kernel_task`**, it'll be able to make the kernel execute anything (jailbreaks).
503+
504+
* Call `mach_task_self()` to **get the name** for this port for the caller task. This port is only **inherited** across **`exec()`**; a new task created with `fork()` gets a new task port (as a special case, a task also gets a new task port after `exec()`in a suid binary). The only way to spawn a task and get its port is to perform the ["port swap dance"](https://robert.sesek.com/2014/1/changes\_to\_xnu\_mach\_ipc.html) while doing a `fork()`.
505+
* These are the restrictions to access the port (from `macos_task_policy` from the binary `AppleMobileFileIntegrity`):
506+
* If the app has **`com.apple.security.get-task-allow` entitlement** processes from the **same user can access the task port** (commonly added by Xcode for debugging). The **notarization** process won't allow it to production releases.
507+
* Apps with the **`com.apple.system-task-ports`** entitlement can get the **task port for any** process, except the kernel. In older versions it was called **`task_for_pid-allow`**. This is only granted to Apple applications.
508+
* **Root can access task ports** of applications **not** compiled with a **hardened** runtime (and not from Apple).
509+
510+
**The task name port:** An unprivileged version of the _task port_. It references the task, but does not allow controlling it. The only thing that seems to be available through it is `task_info()`.
454511

455512
### Shellcode Injection in thread via Task port
456513

@@ -514,6 +571,9 @@ processIdentifier]);
514571

515572
```objectivec
516573
// gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector
574+
// Based on https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a?permalink_comment_id=2981669
575+
// and on https://newosxbook.com/src.jl?tree=listings&file=inject.c
576+
517577

518578
#import <Foundation/Foundation.h>
519579
#import <AppKit/AppKit.h>
@@ -716,6 +776,10 @@ gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
716776
./inject <pi or string>
717777
```
718778

779+
{% hint style="success" %}
780+
For this to work on iOS you need the entitlement `dynamic-codesigning` in order to be able to make a writable memory executable.
781+
{% endhint %}
782+
719783
### Dylib Injection in thread via Task port
720784

721785
In macOS **threads** might be manipulated via **Mach** or using **posix `pthread` api**. The thread we generated in the previos injection, was generated using Mach api, so **it's not posix compliant**.

0 commit comments

Comments
 (0)