对抗持续增强的权限控制:借助第三方框架实现macOS注入

番茄系统家园 · 2022-04-30 03:19:02

对抗持续增强的权限控制:借助第三方框架实现macOS注入

概述

在加入了TrustedSecAETR团队以来,我花了一些时间研究macOS环境下的Tradecraft。遗憾的是,对于攻击方来说,与Windows相比,针对macOS环境的攻击难度越来越大。随着隐私保护、沙箱和大量的权限控制,攻击者难以在macOS设备上放置植入工具。

在杀伤链(Killchain)的后漏洞利用(post-exploitation)中,进程注入是一种重要的方式,Apple也花费了巨大的努力去防范这种方式。从历史上来看,我们以前可以在目标进程上调用task_for_pid,检索其Mach端口,并执行mach_vm_以分配和读取/写入内存。而今天,这些API已经受到严格的限制,只有root用户才能调用这些函数。也就是说,只要二进制文件没有使用HardenedRuntime,并且目标不是Apple签名的二进制文件,那么即使是root用户,也不能查看其内存。

在这篇文章中,我们将介绍一些利用第三方技术实现代码注入的有趣方法。对我们来说,这种方式可以转换为在目标应用程序的上下文中运行代码,而无需再致力于禁用系统完整性保护(SIP)。

请注意:本文中分析的两种技术都不是特定于macOS的,它们也可以在Linux和Windows系统上实现,但是由于Apple对进程注入进行了限制,所以本文重点放在了对macOS的影响上。

首先,让我们看看所有人都应该熟悉的技术——.NET Core。

NET Core

Microsoft的.NETCore框架是一种流行的跨平台运行时和软件开发套件(SDK),可以使用我们熟知的.NET语言开发应用程序。由.NETCore运行时提供支持的最受欢迎的应用程序之一就是PowerShell的跨平台版本,我们将以它作为最开始的测试平台。

为了展示我们在尝试macOS注入过程中所面临的复杂性,我们首先演示通过task_for_pid API进行注入的传统方式。一种简单的方法是使用:

当针对目标PowerShell进程运行时,我们得到了预期的错误提示。

无法检索PowerShell的任务端口:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

接下来,我们尝试以root身份运行。尝试对没有Hardened Runtime标志的应用程序进行测试,得到了正常的结果。

以root用户身份成功获取PowerShell的任务端口:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

但是,一旦我们开始使用Hardened Runtime标志签名的应用程序,就会遇到相同的错误。

在加固后进程中不能以root用户身份获得任务端口:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

如果我们使用lldb之类的东西,而它拥有com.apple.security.cs.debugger的强大功能,会发生什么?当非root用户尝试访问未加固的进程时,我们取得了一些进展,但是这时也出现了一个对话框,警告我们存在的目标,这实际上就有些影响隐蔽性了。

请求调试权限时向用户显示的对话框:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

再次,我们以root用户身份运行lldb,也无法使用Hardened Runtime调试进程。

调试器无法以root用户身份附加到加固后的进程:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

总之,这意味着,只有在我们以root用户,且未使用Hardened Runtime标志对进程进行签名的情况下,才可以注入.NET Core进程。

既然如此,Apple的API现在对我们来说就毫无用处,我们在没有一个理想的漏洞的情况下,还能怎样去控制目标.NETCore进程呢?为了理解这一点,我们应该更深入分析一下运行时的源代码,可以在这里找到:https://github.com/dotnet/runtime。

NET Core调试

让我们从头开始,尝试了解诸如Visual Studio Code这样的调试器是如何与.NET Core进程进行交互的。

查看dbgtransportsession.cpp中的.NETCore源代码,可以发现这部分代码负责处理调试器与调试内容的通信,在函数DbgTransportSession::Init中创建了一系列命名管道。

对于macOS(和*nix),这些管道是使用以下代码创建的FIFO命名管道:

为进行分析,我们可以启动PowerShell,并看到在当前用户的$TMPDIR内创建了两个命名管道,其名称为PID,并带有in或out的后缀。

.NET Core创建用于调试的命名管道:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

在了解命名管道的位置和目的后,我们如何与目标进程进行通信?答案位于方法DbgTransportSession::TransportWorker之中,该方法负责处理来自调试器的传入连接。

阅读代码,我们发现调试器要做的第一件事是创建一个新的调试会话。这是通过以MessageHeader结构开头的out管道发送消息来完成的,这部分可以从.NET源代码中看到:

对于新的会话请求,该结构填充如下:

构造完成后,我们使用write syscall将其发送到目标:

在标头之后,我们需要发送一个sessionRequestData结构,该结构包含一个用于标识会话的GUID:

在发送完成会话请求后,我们从out管道中读取一个标头,该标头将指调试器会话是否成功:

在这一阶段,我们已经与目标建立了调试器会话。接下来,就可以与目标进程进行通信了,那么我们可以使用哪些功能呢?通过查看运行时公开的消息类型,可以找到两个值得关注的原语,分别是MT_ReadMemory和MT_WriteMemory。

这些消息完全符合我们的预期,可以让我们读取和写入目标进程的内存。这里需要考虑的是,我们可以在典型的macOSAPI调用之外读取和写入内存,从而为我们提供了.NETCore进程内存的后门。

让我们开始尝试从目标进程中读取一些内存。与会话创建一样,我们首先制作标头:

但是,这一次,我们还提供了一个希望从目标读取的地址:

我们可以借助下面的方法,分配一些非托管内存,来针对PowerShell进行尝试:

可以使用概念证明(POC)代码轻松读取此内存。

从PowerShell中转储内存:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

当然,通过使用命令覆盖内存注入PowerShell,我们也可以实现相反的操作。

将内存注入PowerShell:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

用于执行此操作的POC代码请参考:https://gist.github.com/xpn/7c3040a7398808747e158a25745380a5。

NET Core代码执行

之前我们聚焦于如何将代码注入到PowerShell中,接下来要解决的问题是,如何将读写原语转换为代码执行?这里还需要考虑到,我们没有更改内存保护的能力,所以如果要引入类似Shellcode的内容,只能写入标记为可写和可执行的内存页面。

在这种情况下,我们有几种选择,作为简单的概念证明来说,首先可以确定内存的RWX页面,并在其中托管我们的Shellcode。Apple限制了我们遍历远程进程地址空间的能力。但是,我们实际上还可以访问vmmap,其中包含了很多权限,也包括用于访问目标Mach端口的com.apple.system-task-ports。

在PowerShell中执行vmmap -p [PID],可以看到很多适合托管代码的内存区域,下面以rwx/rwx权限突出显示。

使用vmmap识别内存的RWX页面:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

既然我们知道了将Shellcode注入的地址,我们就需要找到一个可以写入的位置,来触发代码执行。函数指针是一个比较理想的位置,不用太长时间就可以发现许多理想的目标。我们用到的一个方法是覆盖动态函数表(DFT)中的指针,.NETCore运行时使用该指针为JIT编译提供帮助函数。可以在jithelpers.h中找到支持的函数指针的列表。

查找指向DFT的指针实际上很简单,我们可以使用类似于mimikatz的签名搜寻技术来搜索libcorclr.dll,以查找对符号_hlpDynamicFuncTable的引用,随后取消引用。

生成搜寻_hlpDynamicFuncTable符号签名的指令:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

接下来要做的,就是找到一个地址,从该地址开始进行签名搜索。为此,我们利用了另一个公开的调试器函数MT_GetDCB。这将会返回有关目标进程的许多有用信息,但是对我们来说,我们关注的是返回的字段,字段中包含帮助函数的地址m_helperRemoteStartAddr。通过这个地址,就可以知道libcorclr.dll在目标进程内存中的位置,并且可以开始搜索DFT。

现在,我们已经拥有了注入和执行代码所需的所有内容,可以尝试将一些Shellcode写入内存的RWX页面,并通过DFT传输代码执行。我们使用的Shellcode非常简单,只需要在PowerShell提示符中显示一条消息,然后再将执行返回给CLR(以防止崩溃)即可:

编写完成上述Shellcode之后,我们将所有这些组合在一起,看看执行过程究竟如何。

演示视频:https://youtu.be/KqTIrB_WUgA

用于注入Shellcode的完整POC代码请参考:https://gist.github.com/xpn/b427998c8b3924ab1d63c89d273734b6。

Hardened Runtime是否能防范攻击

现在,我们可以注入到.NETCore进程中,还剩下一个明显的问题,就是HardenedRuntime是否可以阻止这种情况?根据我们的分析,设置HardenedRuntime标志不会对暴露给我们的调试管道产生影响,这意味着HardenedRuntime标志签名的应用程序仍然会暴露上述IPC调试函数,该函数正是要实现注入所必须的。

举例来说,我们看一下另一个经过签名,并启用了Hardened Runtime标志的知名应用程序Fiddler。

与Fiddler应用程序相关联的Hardened Runtime标志:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

在这里,我们找到了Hardened Runtime标志集,但是,启动应用程序仍然会导致创建调试管道。

在使用Hardened Runtime的状态下Fiddler创建的命名管道:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

尝试向Fiddler中注入一些Shellcode,以确保一切正常。这次,我们使用的是CodyThomas的Mythic框架,将其中的Apfell植入工具注入到目标进程中。

有几种方法可以选择,但是为了简单起见,我们使用wNSCreateObjectFileImageFromMemory方法从磁盘加载Bundle:

我们利用加载的Bundle来实现JXA执行:

如果我们现在按照针对Fiddler的.NETCoreWebUI流程执行与之前完全相同的步骤,来实现代码注入,一切顺利的话,就可以将Apfell植入工具注入加固后的进程中,并派生出植入工具。

演示视频:https://youtu.be/-e4OrX2nmeY

用于注入Apfell植入工具的POC代码:https://gist.github.com/xpn/ce5e085b0c69d27e6538179e46bcab3c。

好了,现在我们看到了运行时这些隐藏函数的实用性,但这是.NETCore的个例吗?事实证明,不是。我们接下来看一下AppStore里面的另一个框架——Electron。

劫持Electron

众所周知,Electron是一个框架,可以用于将Web应用程序移植到桌面,同时能够安全地存储RAM,供后续需要时使用。

那么,我们如何才能在经过签名和加固后的Electron应用程序中执行代码?这里就要引入环境变量——ELECTRON_RUN_AS_NODE。

这个环境变量是将Electron应用程序转换为常规的旧NodeJSREPL所需要的全部。例如,我们从AppStore中获取一个流行的应用程序(例如Slack),并在设置了ELECTRON_RUN_AS_NODE环境变量的情况下启动该进程:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

这也适用于Visual Studio Code:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

Discord...

对抗持续增强的权限控制:借助第三方框架实现macOS注入

甚至是BloodHound:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

我本来以为这些是0-day,但实际上已经在文档中发布过(https://www.electronjs.org/docs/api/environment-variables#electron_run_as_node)。

那么,这对于我们来说意味什么?同样,在macOS环境中,这意味着,如果我们对某个应用程序感兴趣,或者允许针对Electron应用程序使用隐私控制,那么就可以在带有ELECTRON_RUN_AS_NODE环境变量的情况下执行签名和加固的进程,然后将NodeJS代码传递并执行。

我们以Slack为例,尝试利用该应用程序通常允许的桌面、文档等区域的访问,来解决TCC问题。在macOS中,子进程将继承父进程的TCC权限,因此这意味着我们可以使用NodeJS生成子进程(例如Apfell的植入程序),该子进程将继承用户授予的所有隐私设置允许的项目。

为此,我们将使用launchd通过以下plist生成Electron进程:

然后,我们可以启动任务,加载plist并使用ELECTRON_RUN_AS_NODE环境变量启动Slack,通过OSAScript执行Apfell:

如果一切顺利,我们将按照预期返回到Shell。

Apfell植入工具返回到Mythic框架:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

通常,在这里,当我们请求~/Downloads之类的内容时,大家可能担心会向用户显示隐私提示,但实际上,由于现在是Slack的子进程,因此可以使用其继承的隐私权限。

演示视频:https://youtu.be/1_3Q00-c_JA 。

当然,如果攻击者在未获得许可的情况下请求访问任何内容,我们可以让合法应用来背这个锅:

当加载Apfell植入工具请求访问时,显示的TCC对话框:

对抗持续增强的权限控制:借助第三方框架实现macOS注入

现在,我们就已经掌握了几种利用第三方架公开的功能来解决macOS进程注入限制的方法。这种注入技术适用于大量应用程序,考虑到Apple在macOS系统中不断加强的限制,这种效果也令人惊讶。我们希望,通过展示这种技术,可以帮助一些红队成员更好地实现macOS后漏洞利用的注入环节。

免责声明: 凡标注转载/编译字样内容并非本站原创,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如果你觉得本文好,欢迎推荐给朋友阅读;本文链接: https://m.nndssk.com/dngz/3325059LljHM.html
猜你喜欢
最新应用
热门应用