# macOS通过任务端口进行线程注入
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * 你在一家**网络安全公司**工作吗?你想在HackTricks中看到你的**公司广告**吗?或者你想获得**PEASS的最新版本或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 发现我们的独家[NFT](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family) * 获取[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com) * **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**Telegram群组**](https://t.me/peass) 或 **关注**我在**Twitter**上的[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。** * **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享你的黑客技巧。**
本文摘自[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/bazad/threadexec](https://github.com/bazad/threadexec) * [https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36](https://gist.github.com/knightsc/bd6dfeccb02b77eb6409db5601dcef36) ### 1. 线程劫持 首先,我们调用任务端口上的**`task_threads()`**来获取远程任务中的线程列表,然后选择其中一个线程进行劫持。与传统的代码注入框架不同,我们**无法创建一个新的远程线程**,因为`thread_create_running()`将被新的防护机制阻塞。 然后,我们可以调用**`thread_suspend()`**来停止线程的运行。 此时,我们对远程线程的唯一有用的控制是**停止**它,**启动**它,**获取**它的**寄存器**值,并**设置**它的寄存器**值**。因此,我们可以通过将远程线程中的寄存器`x0`到`x7`设置为**参数**,将**`pc`**设置为要执行的函数,并启动线程来**发起远程函数**调用。此时,我们需要检测返回值并确保线程不会崩溃。 有几种方法可以实现这一点。一种方法是使用`thread_set_exception_ports()`为远程线程注册异常处理程序,并在调用函数之前将返回地址寄存器`lr`设置为无效地址;这样,在函数运行后,将生成一个异常并向我们的异常端口发送一条消息,此时我们可以检查线程的状态以获取返回值。然而,为了简单起见,我复制了Ian Beer的triple\_fetch漏洞利用中使用的策略,即将`lr`设置为一个会无限循环的指令的地址,然后反复轮询线程的寄存器,直到**`pc`指向该指令**。 ### 2. 用于通信的Mach端口 下一步是**创建Mach端口,以便我们可以与远程线程进行通信**。这些Mach端口在稍后帮助在任务之间传输任意的发送和接收权限时非常有用。 为了建立双向通信,我们需要创建两个Mach接收权限:一个在**本地任务中**,一个在**远程任务中**。然后,我们需要将一个发送权限**传输到另一个任务的每个端口**。这样,每个任务都有一种可以发送消息并被另一个任务接收的方法。 首先,让我们专注于设置本地端口,即本地任务持有接收权限的端口。我们可以像创建其他Mach端口一样,调用`mach_port_allocate()`来创建Mach端口。关键是将发送权限传递到远程任务中的该端口。 我们可以使用一种方便的技巧,只使用基本的执行原语将发送权限从当前任务复制到远程任务中,即使用`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端口。 ### 3. 基本内存读写 现在我们将使用执行原语来创建基本的内存读写原语。这些原语并不会用于太多的事情(我们很快将升级到更强大的原语),但它们是帮助我们扩展对远程进程控制的关键步骤。 为了使用我们的执行原语读写内存,我们将寻找这样的函数: ```c uint64_t read_func(uint64_t *address) { return *address; } void write_func(uint64_t *address, uint64_t value) { *address = value; } ``` 它们可能对应以下汇编代码: ``` _read_func: ldr x0, [x0] ret _write_func: str x1, [x0] ret ``` 快速扫描一些常见的库,发现了一些不错的候选项。要读取内存,我们可以使用[Objective-C运行时库](https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-runtime-new.mm.auto.html)中的`property_getName()`函数: ```c const char *property_getName(objc_property_t prop) { return prop->name; } ``` 事实证明,`prop`是`objc_property_t`的第一个字段,因此与上面的假设的`read_func`直接对应。我们只需要进行远程函数调用,第一个参数是我们想要读取的地址,返回值将是该地址处的数据。 找到一个现成的用于写入内存的函数稍微困难一些,但仍然有很好的选择,而且没有不需要的副作用。在libxpc中,`_xpc_int64_set_value()`函数的反汇编代码如下: ``` __xpc_int64_set_value: str x1, [x0, #0x18] ret ``` 因此,要在地址`address`执行64位写入操作,我们可以执行远程调用: ```c _xpc_int64_set_value(address - 0x18, value) ``` 有了这些基本原理,我们就可以创建共享内存了。 ### 4. 共享内存 我们的下一步是在远程任务和本地任务之间创建共享内存。这将使我们能够更轻松地在进程之间传输数据:有了共享内存区域,任意内存读写只需通过远程调用`memcpy()`即可完成。此外,拥有共享内存区域还可以轻松设置堆栈,以便我们可以调用具有超过8个参数的函数。 为了简化操作,我们可以重用libxpc的共享内存功能。Libxpc提供了一种XPC对象类型`OS_xpc_shmem`,允许在XPC上建立共享内存区域。通过反向工程libxpc,我们确定`OS_xpc_shmem`基于Mach内存条目,这些Mach内存条目是表示虚拟内存区域的Mach端口。由于我们已经展示了如何将Mach端口发送到远程任务,因此我们可以使用这个方法轻松地设置自己的共享内存。 首先,我们需要使用`mach_vm_allocate()`来分配我们将共享的内存。我们需要使用`mach_vm_allocate()`来使用`xpc_shmem_create()`创建一个表示该区域的`OS_xpc_shmem`对象。`xpc_shmem_create()`将负责为我们创建Mach内存条目,并将Mach发送权限存储在偏移量为`0x18`的不透明`OS_xpc_shmem`对象中。 一旦我们有了内存条目端口,我们将在远程进程中创建一个表示相同内存区域的`OS_xpc_shmem`对象,从而允许我们调用`xpc_shmem_map()`来建立共享内存映射。首先,我们通过远程调用`malloc()`来为`OS_xpc_shmem`分配内存,并使用我们的基本写入原语将本地`OS_xpc_shmem`对象的内容复制到其中。不幸的是,结果对象并不完全正确:其偏移量为`0x18`的Mach内存条目字段包含了本地任务对内存条目的名称,而不是远程任务的名称。为了解决这个问题,我们使用`thread_set_special_port()`技巧将Mach内存条目的发送权限插入到远程任务中,然后用远程内存条目的名称覆盖字段`0x18`。此时,远程的`OS_xpc_shmem`对象是有效的,并且可以通过远程调用`xpc_shmem_remote()`建立内存映射。 ### 5. 完全控制 有了已知地址的共享内存和任意执行原语,我们基本上已经完成了。通过调用`memcpy()`来实现任意内存读写,通过按照调用约定在堆栈上布置超过8个的附加参数来执行具有超过8个参数的函数调用。通过在之前建立的端口上发送Mach消息,可以在任务之间传输任意Mach端口。甚至可以使用文件端口在进程之间传输文件描述符(特别感谢Ian Beer在triple_fetch中演示了这种技术!)。 简而言之,我们现在对受害进程拥有完全且轻松的控制。您可以在[threadexec](https://github.com/bazad/threadexec)库中查看完整的实现和公开的API。
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 * 您在一家**网络安全公司**工作吗?您想在HackTricks中**为您的公司做广告**吗?或者您想获得**PEASS的最新版本或下载PDF格式的HackTricks**吗?请查看[**订阅计划**](https://github.com/sponsors/carlospolop)! * 发现我们的独家[NFT](https://opensea.io/collection/the-peass-family)收藏品[**The PEASS Family**](https://opensea.io/collection/the-peass-family) * 获取[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com) * **加入**[**💬**](https://emojipedia.org/speech-balloon/) [**Discord群组**](https://discord.gg/hRep4RUj7f)或[**电报群组**](https://t.me/peass),或在**Twitter**上**关注**我[**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**。** * **通过向**[**hacktricks repo**](https://github.com/carlospolop/hacktricks) **和**[**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud) **提交PR来分享您的黑客技巧。**