背景介绍
近期,我一直在研究macOS上的一些持久化技术,尤其是如何利用低等级用户权限来修改文件以影响用户交互。对于macOS终端用户来说,交互最频繁的当属Dock了。
在我的研究过程中,我发现了一个plist文件,它可以控制Apple D 对于macOS终端用户来说,交互最频繁的当属Dock了。ock应用程序是否可见。在检查plist文件中的值时,我就想判断是否可以通过修改这些值来将合法程序替换成我们的恶意程序,并执行恶意代码。
最终,我开发出了一个包含PersistentJXA项目的DockPersist.js脚本文件,并且能够将Dock中最常见的Safari和Chrome替换成我们的恶意程序,不过这种方法适用于其他任何应用程序。一旦用户点击了Safari或Chrome图标,我们的恶意软件就会运行,因为这里的Dock图标充当了实际应用程序的快捷方式。
接下来,我们将在这篇文章中介绍这项技术的实现方式。
环境配置
这种持久化方法需要将恶意程序上传到目标设备中,不过我们有很多种方法绕过Gatekeeper保护机制,并实现恶意程序的上传:
压缩应用程序Bundle,在Mythic代理中使用上传命令,然后解压至目标路径;
压缩应用程序Bundle,使用curl命令在目标设备中拉取程序,然后解压至目标路径;
压缩应用程序Bundle,进行base64编码,然后在目标设备中解码,并解压至目标路径;
在这里,首先使用Automator创建一个应用程序,这个应用程序会在后台打开Safari,然后运行我们的Apfell Payload:
为了不引起终端用户的察觉,我用Safari罗盘图标替换掉了Automator图标。当然了,我们也可以使用Xcode来生成更加复杂的应用程序。
接下来,压缩应用程序Bundle并上传至目标系统,然后解压至/Users/Shared/路径下,此时我们就可以调用持久化方法了。
这里需要注意的是,考虑到plist文件的格式,这里要求伪造的应用程序命名为“Google Chrome”或“Safari”,并且存储路径为/Users/Shared/,然后修改Safari64和Chrome64变量来指向该地址。
调用持久化方法
将脚本导入至Apfell代理中:
调用DockPersist函数,该函数可以接收三个参数:
应用程序名称;
应用程序Bundle ID;
重载Dock选项;
Bundle ID存在于Info.plist文件中,可以使用下列命令直接获取:
/usr/libexec/PlistBuddy -c 'Print CFBundleIdentifier' ~/FakeApp/Safari.app/Contents/Info.plist
检测技术
Crescendo是一款在单主机中快速捕捉事件的优秀工具,Crescendo也是macOS上的一款实时事件查看器,它可以利用苹果的终端安全框架ESF来实现其功能。ESF能够监控系统事件,并识别潜在的恶意活动,它相当于Windows中的ETW。
在Crescendo的帮助下,我们可以轻松查看持久化执行所生成的文件和进程事件。
比如说,下列ESF事件将会被映射至Crescendo中:
ES_EVENT_TYPE_AUTH_EXEC = process::exec
ES_EVENT_TYPE_NOTIFY_EXIT = process::exit
ES_EVENT_TYPE_NOTIFY_CREATE = file::create
ES_EVENT_TYPE_NOTIFY_KEXTLOAD = process:kext::load
ES_EVENT_TYPE_NOTIFY_MOUNT = file::mount
ES_EVENT_TYPE_NOTIFY_UNLINK = file::unlink
ES_EVENT_TYPE_NOTIFY_RENAME = file::rename
ES_EVENT_TYPE_NOTIFY_UIPC_CONNECT = network::ipcconnect
ES_EVENT_TYPE_NOTIFY_FORK = process::fork
ES_EVENT_TYPE_NOTIFY_EXEC可以捕捉这种持久化方法的各种事件,如果还需要覆盖其他事件,我强烈推荐使用Xorrior的Appmon。
以下几个因素针对的是持久化方法的运行,因为实际的恶意应用程序组件将根据攻击者开发的内容而有所不同。
首先,plutil可以将Dock plist转换为XML,XML格式可以轻松修改:
除此之外,temp9876文件的创建也会被记录到日志中。
DockPersist.js会在/private/tmp/目录下创建一个随机名称的文件,该脚本将修改plist文件的XML版本,并将其存储至这个随机文件名中。在这里,temp0wsn4p包含了XML格式的恶意plist,所以我们可以使用正确的格式来修改代码格式版本,并在Dock中正确加载:
接下来,DockPersist.js会删除~/Library/Preferences/com.apple.dock.plist路径下的plist文件:
ESF会捕捉到我们存储新的恶意plist文件的行为:
~/Library/Preferences/com.apple.dock.plist
最后,因为我们在函数调用中重载了Dock,因此killall命令将会被调用:
这些事件都可以作为构建检测机制的入口点,这里最重要的就是plutil和killall了。除此之外,还可以利用文件创建、删除和修改事件进行检测。
正常执行
我们已经知道了如何使用ESF来捕捉恶意行为了,那么我们如何让ESF来显示正常执行行为呢?
在正常操作中,cfprefsd会触发com.apple.dock.plist上的file::rename(文件重写)事件,当用户通过GUI手动修改Dock时同样会触发这些事件:
防护绕过
攻击者可以在目标设备外执行plist修改操作,然后再上传到目标Dock plist路径下,这样可以降低被检测到的风险。但是,这同样会触发file::rename事件,不过它不会使用到cfprefsd进程。
可视化指标
我们的PoC应用程序执行之后,会在Dock中出现两个Safari图标:
第一个Safari是恶意程序,它存在于plist文件中的persistent-apps字段,第二个就是真实的Safari了,它存在于plist文件中的recent-apps字段。
总结
在这篇文章中,我们介绍了一种在macOS上的持久化机制,它有点类似于在Windows中利用.LNK文件实现持久化的方法。更重要的是,我们希望通过这篇文章来帮助广大研究人员开发出相应的检测技术。
精彩推荐