写在前面的话
前段时间,MalwareHunterTeam的研究人员发现了一款包含恶意软件的盗版macOS应用程序,该程序文件名为“ultraedit.dmg”(9eb7bda5ffbb1a7549b1e481b1a6ed6efe2e28d0463370c87630fed74eee6228),其中包含了一个名为“libConfigurer64.dylib”(ce40829673687b48d68defa3176c8ab59a2a50ee9c658fe46a5de7692fbc112d)的恶意库。据分析,这个样本最早出现在2023年,那么在本文中,我们将对其进行深入剖析,我们首先会从磁盘镜像文件着手,然后深入分析其恶意动态库。
定性归类
macOS用户应该都知道UltraEdit这款软件,它是一款强大且高度可配置的文本及十六进制编辑器,并且支持大型文件。该软件的售价为79.95美元一年,但有些用户可能会直接选择使用盗版(免费),那代价是什么呢?
打开VirusTotal后,我们可以看到这个样本已经被几十家反病毒供应商标记了:
尽管已经有了这么多标记,但标记的名称非常普通,比如说“Trojan.MAC.Generic”或“Trojan-Downloader.OSX.Agent”,因此这样看的话我们无法看出它到底包含了什么类型的恶意软件。
接下来,我们加载获取到的恶意软件样本磁盘镜像(ultraedit.dmg-SHA1:40AD975E6656C9050325C4D5C989795C14665BA7):
% hdiutil attach -noverify /Users/patrick/Downloads/ultraedit.dmg /dev/disk5 GUID_partition_scheme /dev/disk5s1 Apple_HFS /Volumes/UltraEdit 22.0.0.16
它会挂载到/Volumes/UltraEdit 22.0.0.16:
由背景图的文字来看,这明显是一个盗版的UltraEdit应用程序。使用WhatsYourSign可以看到这个应用程序并没有签名:
而官方的UltraEdit版本都包含合法的签名信息:
一般来说,想要在盗版软件样本中寻找恶意组件,还是比较困难的,尤其是这种大型应用程序。当然了,人家可能压根没有恶意组件,也并不是所有的盗版软件都包含恶意软件。不过,我们还是发现并识别了这个名为libConfigurer64.dylib(SHA-1:7F5A34B0CFEF974122D6717C60D68F0AC4CA46E0)的恶意组件。
首先,它是一个无符号64位(Intel)dylib库:
% codesign -dvv /Volumes/UltraEdit\ 22.0.0.16/UltraEdit.app/Contents/Resources/libConfigurer64.dylib libConfigurer64.dylib: code object is not signed at all % file /Volumes/UltraEdit\ 22.0.0.16/UltraEdit.app/Contents/Resources/libConfigurer64.dylib libConfigurer64.dylib: Mach-O 64-bit dynamically linked shared library x86_64
它同样也被VirusTotal标记了:
虽然这个样本被20多个厂商标记了,但名字还是很普通,比如说MacOS:Downloader之类的,我们还是没办法识别它的恶意软件类型。
使用otool工具(-L参数)查看这款盗版UltraEdit app的依赖库信息,我们会看到libConfigurer64.dylib已经被添加为了依赖库:
% otool -L /Volumes/UltraEdit\ 22.0.0.16/UltraEdit.app/Contents/MacOS/UltraEdit /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore /System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration ... /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText /System/Library/Frameworks/ImageIO.framework/Versions/A/ImageIO @loader_path/../Resources/libConfigurer64.dylib
它是唯一一个非系统库,而且官方正版的UltraEdit并没有这个库:
% otool -L /Volumes/UltraEdit\ Setup/UltraEdit.app/Contents/MacOS/UltraEdit | grep libConfigurer64.dylib | wc 0 0 0
深入分析libConfigurer64.dylib动态库文件
libConfigurer64.dylib这个动态库是盗版UltraEdit应用程序的依赖库,这也就意味着,当用户启动这个盗版软件之后,它会被自动加载。但库中的代码是如何执行的呢?(因为加载一个库和执行里面的代码是分开的步骤)
通过otool -l命令,我们可以看到它加载的命令,其中包含一个起始于偏移量0xd030的__mod_init_func字段:
% otool -l libConfigurer64.dylib ... Load command 1 cmd LC_SEGMENT_64 cmdsize 312 segname __DATA_CONST vmaddr 0x000000000000d000 vmsize 0x0000000000001000 ... Section sectname __mod_init_func segname __DATA_CONST addr 0x000000000000d030 size 0x0000000000000008
__mod_init_func字段中包含了这个库在被加载时自动执行的构造器,通过提取和查看libConfigurer64.dylib中嵌入的字符串,我们可以看到下列非常有价值的信息:
% strings - libConfigurer64.dylib /Users/Shared/.fseventsd /tmp/.test %*[^//]//%[^/]%s GET %s HTTP/1.1 HOST: %s http://download.ultraedit.info/bd.log http://download.ultraedit.info/ud01.log ...
还记得VirusTotal将其标记为了一个通用下载器吗?根据这些字符串,看来标记是正确的。通过nm工具,我们可以转储该库导入(可能会调用)的API:
% nm - libConfigurer64.dylib ... external _chmod (from libSystem) external _connect (from libSystem) external _execve (from libSystem) external _gethostbyname (from libSystem) external _recv (from libSystem) external _system (from libSystem) external _write (from libSystem)
由此可以看出,API会期望某个程序来实现下载和执行逻辑。
现在,我们在反汇编工具中加载代码库,然后定位到偏移量0xd030,即__mod_init_func字段的起始处:
0x000000000000d030 dq __Z10initializev 0x000000000000d038 dq 0x0000000000000000
其中包含了一个名为initialize的构造器,对initialize函数进行反编译后,我们可以得到两个未命名函数:
int initialize() { var_20 = *qword_value_52426; var_40 = *qword_value_52448; sub_3c20(0x2, &var_20, &var_40); rax = sub_2980(); return rax; }
这两个函数,即sub_3c20和sub_2980非常的复杂,代码非常多,而且不值得进行逆向分析。快速分析后,我们发现这两个函数会下载并执行download.ultraedit.info中的代码。
现在,我们运行盗版UltraEdit应用程序并开始对其执行动态分析,同时我们还要监控其网络活动、文件使用和进程活动情况,至于服务端,download.ultraedit.info仍然处于活动状态,并负责托管文件。
分析发现,这个库实际上会从download.ultraedit.info/下载两个文件,第一个文件名为ud01.log,第二个为bd.log。根据网络捕捉数据,我们可以看到下载的文件似乎是部分经过混淆的Mach-O源码:
通过查看文件监控器,我们发现ud01.log文件会被转储为/tmp/.test:
# ./FileMonitor.app/Contents/MacOS/FileMonitor -pretty -filter UltraEdit { "event": "ES_EVENT_TYPE_NOTIFY_CREATE", "file": { "destination": "/private/tmp/.test", "process": { "pid": 1026, "name": "UltraEdit", "path": "/Volumes/UltraEdit 22.0.0.16/UltraEdit.app/Contents/MacOS/UltraEdit", ... } } }
而bd.log文件则会被转储为/Users/Shared/.fseventsd:
# ./FileMonitor.app/Contents/MacOS/FileMonitor -pretty -filter UltraEdit { "event": "ES_EVENT_TYPE_NOTIFY_CREATE", "file": { "destination": "/Users/Shared/.fseventsd", "process": { "pid": 1026, "name": "UltraEdit", "path": "/Volumes/UltraEdit 22.0.0.16/UltraEdit.app/Contents/MacOS/UltraEdit", ... } } }
现在我们不仅可以让代码库解码下载的文件,而且还可以在反汇编代码中找到参与解码的函数(名为ConstInt_decoder):
int ConstInt_decoder(int arg0) { rax = (arg0 ^ 0x78abda5f) - 0x57419f8e; return rax; }
这个解码器函数可以通过多种方式调用,并使用了硬编码的“密钥”:
文件下载并解码后,该库就拿到了这两个需要执行的代码了,这里它将生成.test文件:
# ./ProcessMonitor.app/Contents/MacOS/ProcessMonitor -pretty { "event" : "ES_EVENT_TYPE_NOTIFY_EXEC", "process" : { "path" : "/private/tmp/.test", "name" : ".test", "pid" : 1334, "arguments" : [ "/usr/local/bin/ssh", "-n" ], } ... }
有趣的是,它会使用/usr/local/bin/ssh和-n参数来执行.test。
.test文件分析
.test源码(SHA-1:5365597ECC3FC59F09D500C91C06937EB3952A1D)似乎是一个已知的恶意软件:
它貌似是Khepri的macOS构建版本,而Khepri则是一个基于Golang和C++开发的开源跨平台代理+后渗透工具。那么它既然是开源的,那我们就不用花时间去分析它了,但这样看来的话,它似乎能够允许威胁行为者完全接管受感染的目标系统。
.fseventsd文件分析
libConfigurer64.dylib从download.ultraedit.info/bd.log下载的另一个文件会被保存为/Users/Shared/.fseventsd。如果在一台虚拟机中运行的话,恶意软件似乎会报错,并且无法向.fseventsd中写入任何内容。
但是,在调试工具的帮助下,我们就可以直接强制让它下载并解码这个源码文件。
值得一提的是,当前VirusTotal上还没有反病毒引擎能够检测到.fseventsd源码文件(SHA-1:C265765A15A59191240B253DB33554622393EA59):
通过提取嵌入的字符串数据,我们能够看到它似乎又是一个下载器:
% strings .fseventsd /tmp/.fseventsds GET %s HTTP/1.1 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.apple.fsevents</string> <key>ProgramArguments</key> <array> <string>/Users/Shared/.fseventsd</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist> http://bd.ultraedit.vip/fs.log /Library/LaunchAgents /com.apple.fsevents.plist
在对其进行反汇编和动态分析之后,似乎证实了它的功能与嵌入字符串的描述相符。首先,通过一个文件监控器,我们可以看到/Users/Shared/.fseventsd文件将以启动代理的形式实现持久化:
# ./FileMonitor.app/Contents/MacOS/FileMonitor -pretty -filter .fseventsd { "event" : "ES_EVENT_TYPE_NOTIFY_OPEN", "file" : { "destination" : "/Users/user/Library/LaunchAgents/com.apple.fsevents.plist", "process" : { "pid" : 1716, "name" : ".fseventsd", "path" : "/Users/Shared/.fseventsd" } } }
实现持久化之后,我们转储了com.apple.fsevents.plist文件的内容:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.apple.fsevents</string> <key>ProgramArguments</key> <array> <string>/Users/Shared/.fseventsd</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
由于RunAtLoad键被设置为了true,因此每次用户使用指定的文件登录时,/Users/Shared/.fseventsd都会自动启动或重启。
接下来,.fseventsd文件会尝试从http://bd.ultraedit.vip/fs.log下载另一个二进制文件,并将其存储为/tmp/.fseventsds。不幸的是,这个fs.log文件我们无法获取到,因为bd.ultraedit.vip服务器现在已经无法访问了:
% curl http://bd.ultraedit.vip/fs.log curl: (6) Could not resolve host: bd.ultraedit.vip
所以这个文件的作用我们目前就无从知晓了。
总结
在这篇文章中,我们对一个盗版UltraEdit应用进行了深入分析。首先,我们确认了其恶意性,并分析了其第二阶段Payload的功能,然后顺藤摸瓜弄清楚了其第三阶段所做的事情。
下面给出的是BlockBlock、LuLu和KnockKnock针对相关恶意组件的检测结果:
恶意软件样本
友情提示:别把自己给感染了...
ultraedit.zip:【点我获取】
参考资料
https://www.virustotal.com/gui/file/9eb7bda5ffbb1a7549b1e481b1a6ed6efe2e28d0463370c87630fed74eee6228
https://objective-see.org/tools.html
https://twitter.com/malwrhunterteam/
https://github.com/geemion/Khepri
https://www.virustotal.com/gui/file/1b2d50cdacfd39205c3caff2925eb35b59312dbe099bd3a98ae3b2f2f909ab17