11 KiB
macOS通过任务端口进行线程注入
☁️ HackTricks云 ☁️ -🐦 推特 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 你在一家网络安全公司工作吗?你想在HackTricks中看到你的公司广告吗?或者你想获得PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFT收藏品The PEASS Family
- 获取官方PEASS和HackTricks周边产品
- 加入💬 Discord群组 或 Telegram群组 或 关注我在Twitter上的🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交PR来分享你的黑客技巧。
本文摘自https://bazad.github.io/2018/10/bypassing-platform-binary-task-threads/(其中包含更多信息)
代码
- https://github.com/bazad/threadexec
- 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. 基本内存读写
现在我们将使用执行原语来创建基本的内存读写原语。这些原语并不会用于太多的事情(我们很快将升级到更强大的原语),但它们是帮助我们扩展对远程进程控制的关键步骤。
为了使用我们的执行原语读写内存,我们将寻找这样的函数:
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运行时库中的property_getName()
函数:
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位写入操作,我们可以执行远程调用:
_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库中查看完整的实现和公开的API。
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
- 您在一家网络安全公司工作吗?您想在HackTricks中为您的公司做广告吗?或者您想获得PEASS的最新版本或下载PDF格式的HackTricks吗?请查看订阅计划!
- 发现我们的独家NFT收藏品The PEASS Family
- 获得官方PEASS和HackTricks周边产品
- 加入💬 Discord群组或电报群组,或在Twitter上关注我🐦@carlospolopm。
- 通过向hacktricks repo 和hacktricks-cloud repo 提交PR来分享您的黑客技巧。