导语:本文将讲述如何在macOS上创建一个带有安装程序插件的产品安装包。
恶意安装程序插件
2017年,Patrick Wardle在Twitter上发布了一些令我感兴趣的东西:
本质上来说,他所说的这种姿势允许用户利用已签名的本地macOS应用程序来执行未签名的代码。这里滥用的可能性很大。以这种方式运行恶意代码会使攻击者融入环境并继承该进程的访问权限。如果你关注过几年前由Matt Graeber(@mattifestation)主导的“ 离地生活 ”的安全研究,那么可能你对滥用操作系统的代码执行功能会很熟悉。Patrick描述的这个特殊功能称为安装程序插件。
安装程序插件可以通过向安装会话添加自定义安装窗格(或窗口)来扩展安装程序应用程序的功能。这些插件可以完全访问Objective C运行时和Cocoa框架。安装程序插件不能用于组件包(即使用pkgbuild命令行工具创建的包)。安装程序插件可以用于通常通过mac应用程序商店分发的产品档案或元数据包。我们来回顾产品归档中包含的组件:
分发 :这是一个xml文件,用于定义存档的属性,要安装的软件包以及自定义的JavaScript安装检查。
资源目录:目录包含安装程序所需的任何资源。这可以包括背景图像,自述文件或许可证文件。这些也在分布式xml文件中定义。
组件包:一个存档可以包含一个或多个组件包。安装程序应用程序将根据分发文件中列出的内容安装软件包。
插件目录:这将包括将在安装会话中使用的所有插件。InstallerSections.plist也会在这里。这定义了每个安装程序窗格在安装会话中使用的顺序,包括任何自定义插件。
在macOS上创建一个带有安装程序插件的产品安装包
所以,让我们一起来看看如何使用安装程序插件创建产品安装包。你需要在创建产品档案之前创建组件包。在这个例子中,我们将创建一个没有插入有效载荷的组件包(一个只包含脚本的包)。这可以通过macOS或Linux完成。如果你对使用Linux感兴趣,请参阅Empire ,其中包含生成组件包所需的依赖关系和代码。为简洁起见,我们将坚持使用macOS。
1.检查你是否使用xcode-select -p命令安装了Xcode命令行工具。如果已安装,则会显示Xcode应用程序中开发者目录的完整路径。
2.如果未安装命令行工具,则可以通过Xcode或通过简单的bash命令安装它们:xcode-select -install。
3.一旦安装了命令行工具,创建一个新的目录。在该目录下,在第一行创建一个只有bash的文件,然后 exit 0。该脚本应该命名为postinstall或preinstall,这个例子并不重要。
4.现在使用以下命令来构建组件包: pkgbuild –identifier com.debug.company –version x.x –scripts /path/to/scripts/directory </path/to/output/file>
5.现在我们已经有了组件包,我们可以构建安装程序插件。一个插件示例可以在这里找到。一旦用户进入自定义安装窗格,会简单地弹出消息框。使用Xcode安装程序插件模板,替换现有的代码,然后构建项目。
创建一个新文件夹,然后将编译的软件包和InstallerSections.plist文件复制到新文件夹中。
将InstallerSections.plist文件修改为如下图所示。 使用这个顺序没有特别的优势。 你可以根据需要更改它:
使用以下命令构建产品存档: productbuild –identifier com.nopayload.package –version x.x –package /path/to/component/pkg –plugins /path/to/plugins/directory 。现在继续打开存档。你最初会看到一个提示,警告你该程序包将运行程序,并且你应该只安装来自可信来源的应用程序。选择继续并单击安装窗格,直到到达消息框窗格。正如我们所预料的那样,你会看到弹框出现。
为了验证我们的bundle已经加载,我们可以使用TaskExplorer 。正如Patrick在他的推文中所述,我们的软件包在被加载到安装程序应用程序之前被复制到临时位置,尽管它是随机位置。
太好了,让我们继续讨论一个更实用的应用程序。我们的执行方法有一个问题。 一旦安装会话结束并且用户单击'关闭'按钮,安装程序应用程序将退出,从而导致我们想要执行的任何代码执行失败。我们需要越过这个技术障碍才能将其变成完全武器化的有效载荷。我们需要找到一种方法来阻止应用程序退出,然后在用户单击关闭按钮后隐藏UI。
为了获得对安装程序应用程序的控制权,我们可以在Objective C中使用一种众所周知的技术,称为swizzling 。Swizzling涉及在运行时更改方法的实现。在这种情况下,我们需要调整InstallerSectionController 协议的closeWindow方法。这是用户在摘要安装程序窗格上单击关闭时调用的方法。幸运的是,一个在stackoverflow上的用户提供了一个设置协议方法的具体实现。一旦我们替换了目标函数的方法实现,我们可以包含逻辑来隐藏用户的应用程序,然后在后台线程中执行Python Empire有效载荷。这个特定插件的一个例子可以在这里找到,你可以在下面查看它的执行演示。
使用python Empire 有效载荷 执行一个安装程序插件
可能的检测方法
程序包文件通常由第三方开发人员用于在Mac应用程序商店之外分发应用程序或安装软件更新。我不确定使用安装程序插件的频率是怎么样的,因此在开发基于产品安装包中安装程序插件的检测时,你的检测方式可能会有所不同。你也可以考虑使用fsevents API来监视被丢弃到文件系统上的安装包。但是,每次执行安装程序时,插件都会被复制到随机位置。 因此,基于文件系统变化的任何检测方法实现起来都非常困难。install.log文件保存在路径/var/log/中 。 当安装程序加载插件时,安装程序会创建一个条目。
该条目仅提供关于安装程序插件是否已加载的信息,而没有关于其位置或名称的任何细节。可以使用pkgutil命令行实用程序来检查产品安装包的内容。使用-expand标志展开产品归档文件以解压缩安装包并查看其内容。 请注意,plugins目录未被压缩。此外可以使用pkgutil通过软件包ID列出主机的所有已安装软件包。 -pkg-info标志将提供有关特定软件包的其他详细信息,包括版本、卷、位置和安装时间。
另外,我探索了自动化并且更适合企业环境的解决方案。Osquery包含两个表格,可以提供我们构建检测所需的信息。process_open_files表包含了特定进程持有的每个文件描述符的信息。process_memory_map表包含了与每个进程中的内存映射文件有关的信息。令人惊讶的是,这两个表中的安装程序插件都没有任何具体信息。
不幸的是,我还没有找到解决方案来监控特定进程中的dylib加载或bundle加载事件。希望上面介绍的方法将为开发缓解措施提供一个起点。