挖矿木马分析报告

基本信息

样本名称 d48ab2e921f5c725672fce16135d1f09.vir
样本类型 挖矿木马
样本大小 457 KB (468,480 字节)
MD5 d48ab2e921f5c725672fce16135d1f09

简介

该挖矿木马感染受害主机后,会将自身复制到%AppData%目录下,并以svchostx64.exe来命名,从而伪装自身。然后通过创建计划任务来维持该样本运行。样本文件的.plato节表中包含了一个开源的矿机XMRig.exe,该恶意样本再执行时通过创建自身傀儡进程,将开源矿机XMRig.exe注入其中,从而实现利用受害主机资源来进行挖矿。该样本不仅会判断受害主机当前是否处于空闲,还会监控进程。当不空闲时(也就是用户正在执行操作时)或是监控到任务管理器进程时,便结束傀儡进程,终止挖矿,来隐藏自身不被发现。

流程图

07_挖矿木马

简单行为分析

复制自身

运行样本后,复制自身文件到指定目录C:\Users\<UserName>\AppData\Roaming\下,并重命名为svchostx64.exe。如图1:

1570759489414

图1

创建计划任务

该样本通过调用schtasks.exe,执行如下命令:

1
"C:\Windows\System32\schtasks.exe" /SC MINUTE /MO 1 /F /Create /TN Adasdsadas3id /tr "\"C:\Users\john\AppData\Roaming\svchostx64.exe\""

创建名为==Adasdsadas3id==的计划任务,每一分钟运行一次释放出来的svchostx64.exe,如图2:

1570759700585

图2

每一分钟运行一次计划任务,并迅速结束了自身程序,如图3:

1570760693484

图3

进行网络连接

可以从上图中看出,再创建完计划任务后,再次==运行自身程序==,并执行了如下参数:

1
-o monerohash.com:3333 -u 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQni58KYZqH43YSDeqY -p x -k --donate-level=1 -t 3

其中含有可疑URL: monerohash.com:3333,如图4:

1570760329353

图4

并与IP:107.191.99.95:3333107.191.99.221:3333,进行TCP链接,如图5:

1570760539014

图5

样本详细分析

获取dump.exe

首先通过工具查壳,显示该恶意样本没有壳,如图:

1570865891662

异常区段

然后查看样本的节表,发现其中.data区段很可疑。其中.data区段的virtualsize远远大于Raw Size,基本可以推测该样本是一个经过特殊处理的,如图:

1570873032573

混淆代码

然后将样本拖入IDA中,发现其中有大量窗体程序的API,而样本的执行行为中并没有发现有关窗体的行为。

经过分析,这些窗体程序的API全部都==存在问题==,都无法正常执行,(推断这些API是用来==迷惑分析==的)。但在这些==迷惑分析==的API中,夹杂着真正执行的程序代码。如下图,红色框标识的便是真正执行的程序代码。除红色框标识外的指令,大多都是垃圾指令,都无法正常执行。

==真正执行的程序代码:==

  • 调用函数LocalAlloc,开辟大小为0X4FCF3的堆空间
  • 将地址0x40D1C+0x7958C中的==经过加密==的汇编指令,写入开辟的堆空间中
  • 执行函数Decode_409320,==解密==堆空间中的汇编指令
  • 执行开辟的堆空间中的汇编指令

1570871646828

获取dump.exe

由于在执行堆空间中的指令之前都没有执行任何功能代码,便==推测==在堆空间中,很可能会开辟虚拟空间来存储==PE文件==,然后将其展开再执行。

这里用OD跟到开辟的堆空间中,给函数VirtualAlloc下断点,然后F9运行,发现果不其然命中断点,然后再在开辟的虚拟空间中下==硬件写入断点==,果真写入了一个PE文件,==应征了推测==。这里便可将其dump下来,并起名为dump.exe,该文件便是真正要执行的程序。如图:

1570873528537

然后继续用OD跟,便会发现在虚拟空间中,将这个==PE文件进行展开==,然后最终跳回该PE文件的OEP处执行。

分析dump.exe

上一步将样本进行了脱壳,获取到dump.exe,该文件便是该恶意样本的主要行为程序。

其主要执行了:

  • 创建互斥体
  • 复制自身到%AppData%\svchostx64.exe
  • 创建计划任务,每分钟执行一次svhostx64.exe
  • 创建线程,防止系统进入休眠状态
  • 获取区段.plator中的开源矿机程序XMRig.exe
  • 创建==傀儡进程==,将矿机程序XMRig.exe注入创建的傀儡进程,并监视进程,当检测到进程==任务管理器时==,便终止==傀儡进程==

主要行为函数代码段如图:

1570875067157

隐式加载调用函数

该恶意样本使用大量的隐式加载调用系统API,如下图获取函数OpenMutexW,就是通过隐式加载调用的。

  • 首先通过硬编码的方式,将要获取的函数,以及相应的模块字符串,获取到。

  • 然后将要动态加载的DLL模块名,以及要调用的函数名作为参数,传给函数GetProcAddress_402536,来获取要调用的API

1570878056421

函数:GetProcAddress_402536获取函数地址

  • 该函数通过遍历PEB->PEB_LDR_DATA->inMemoryOrderModuleList-> LDR_MODULE()-> LDR_MODULE(ntdll)->LDR_MODULE(kernel32).BaseAddress ,先获取到模块kernel32的基址。
  • 再执行函数traverse_Export_402483遍历模块kernel32的导出表,来获取函数GetModuleHandleA
  • 再执行函数traverse_Export_402483遍历模块kernel32的导出表,来获取函数LoadLibraryA
  • 然后再用获取到的GetModuleHandleA去获取要获取DLL模块句柄,再用获取到的LoadLibraryA去加载该DLL模块句柄,就能根据传进来参数,来加载不同的DLL模块
  • 然后再从加载的DLL模块中,获取想要获取函数的地址

1570879158336

  • 函数:traverse_Export_402483遍历DLL模块的导出表获取函数地址
    • 先遍历模块的PE头,获取到导出表结构,然后遍历导出表。
    • 遍历导出表中的名称表AddressOfNames中的函数名去进行比较,找到想要获取的函数名。记下索引
    • 在通过索引,去名称序号表AddressOfNameOrdinals中获取索引相对应的NameOrdinal名称序号
    • 再根据NameOrdinal名称序号,去函数地址表AddressOfFunction中获取要获取的函数地址

1570879877252

创建互斥体

创建互斥体NIHILMsINERaassdaa

  • 首先通过隐式加载调用函数OpenMutexW,来判断互斥体NIHILMsINERaassdaa是否存在,若存在便直接退出
  • 当互斥体NIHILMsINERaassdaa不存在时,便调用函数CreateMutexW来创建互斥体

1570880165230

复制自身并创建计划任务

复制自身文件到%AppData%\svchostx64.exe,并添加计划任务每一分钟执行一次复制后的程序svchostx64,应征了简单行为分析==图1,图2==抓到的行为。

主要行为:

  • 获取路径%AppData%,并将其拼接为:%AppData%\svchostx64.exe
  • 获取当前进程的绝对路径,并与拼接出的路径进行判断,判断当前正在执行进程是否为svchostx64.exe
  • 若两路径不一样时,再通过函数CreateFileW检测svhostx64文件是否已经存在
  • svhostx64文件不存在,便执行函数CopyFileW,将当前执行的自身文件拷贝至%AppData%\svchostx64.exe
  • 当复制成功后,执行函数Create_Schtask_4026CC,调用schtasks创建计划任务每分钟执行一次复制后的程序svchostx64

代码如图:

1570880802333

函数:Create_Schtask_4026CC创建计划任务

通过执行函数ShellExecuteW,调用schtasks来执行下面指令,来创建名为==Adasdsadas3id==的计划任务,每分钟执行一次svchostx64.exe

1
/SC MINUTE /MO 1 /F /Create /TN Adasdsadas3id /tr "\"C:\Users\john\AppData\Roaming\svchostx64.exe\

1570880981354

创建线程,防止进入休眠

创建线程,创建死循环,每一秒执行一次函数SetThreadExecutionState,来通知系统当前进程正在使用,从而防止应用程序运行时进入休眠状态或关闭显示器

主要行为:

  • 通知系统所设置的状态应保持有效
  • 启用离开模式
  • 通过重置系统空闲计时器来强制系统进入工作状态。

代码如图:

1570883758174

获取区段中的PE文件

该文件的.plato区段中存储着一个开源的矿机程序XMRig.exe,要想运行改程序首先要先获取到该矿机的PE文件

主要行为:

  • 读取当前进程,遍历节表寻找区段.plato,并获取相应区段的.plato.Virtual Adress.plato.Virtual Size
  • 开辟大小为.plato.Virtual Size的虚拟空间,并将.plato.Virtual Adress中的PE文件数据保存至开辟的虚拟空间中

代码如图:

1570885295817

既然是从内存中读取了区段.plato中的PE文件数据,并且并没有将其展开,可以直接dump下来,或是直接去文件中找到该区段,直接将数据拷贝下来,然后发现拷贝下来的文件是一个名为XMRig.exe的开源的矿机,如图:

1570885740843

该矿机可在==GitHub==直接找到其源码,链接为:https://github.com/xmrig/xmrig

1570885848889

创建傀儡进程并监控进程

创建自身进程,并将上一步获取到的开源的矿机程序XMRig.exe注入该进程中并执行,其中还会监控进程,当检测到进程==任务管理器==时,便会终止傀儡进程

主要行为:

  • 避免用户察觉,执行函数JudgeSystemFree_4019E1,检测操作系统当前是否处于空闲状态。当系统处于忙碌状态,将==终止傀儡进程==,也就是只在系统空闲时实施挖矿行为,来防止用户在使用时发现。
  • 解密硬编码指令CommandLine,并转换为unicode,放入开辟好的虚拟空间中。
  • 创建和当前进程一样的傀儡==子进程==,并将解密出来CommandLine作为参数传给创建的子进程来执行。
  • 将上一步获取到的开源矿机程序XMRig.exe展开,==注入到创建的子进程中==。
  • 避免用户察觉,监视当前所有进程,当检测到进程==任务管理器==时,便==终止傀儡进程==,来隐藏自身防止被发现。

代码如图:

1570888935890

  • 避免用户察觉,函数JudgeSystemFree_4019E1,检测操作系统当前是否处于空闲状态

通过调用函数GetLastInputInfo和函数CallNtPowerInformation来检索系统空闲状态信息,从而来判断操作系统当前是否处于空闲状态,当操作系统属于空闲状态返回1,否则返回0,如图:

1570945418503

然后死循环,根据函数JudgeSystemFree_4019E1的==两次返回值==来进行判断,当系统处于==忙碌==时,便==终止傀儡进程,来避免被用户察觉==,其两次执行结果如下:

第一次执行函数JudgeSystemFree_4019E1

1570946355225

第二次执行函数JudgeSystemFree_4019E1

1570946510902

  • 解密硬编码指令CommandLine,并转换为unicode,放入开辟好的虚拟空间中

然后根据第一次执行函数JudgeSystemFree_4019E1的返回的结果,执行不同操作,但所执行的代码实则却是一样的,都是解密一样的CommandLine,然后最终再将解密出来的CommandLine转换为Unicode存起来,如图:

1570947124194

解密出来的指令,如图:

1570948514232

  • 创建和当前进程一样的傀儡==子进程==,并将解密出来CommandLine作为参数传给创建的子进程来执行

以没有控制台窗口并挂起的方式,来创建当前进程的子进程,并执行解密出来的CommandLine作为参数来执行,应征了简单行为分析==图4==抓到的行为,代码如下图:

1570949665978

  • 将上一步获取到的开源矿机程序XMRig.exe展开,==注入到创建的子进程中==
    • 先通过函数GetThreadContext,读取线程上下文
    • 再通过函数ReadProcessMemory,读取context->Ebx+0x8AddressOfImageBase字段,获取创建的傀儡子进程的==加载基址(随机基址)==
    • 再将获取到的==加载基址(随机基址)==与前面获取到的矿机程序XMRig.exeImageBase进行比较。当一样时,便通过函数NtUnmapViewOfSection强制卸载==加载基址(随机基址)==处的模块,反之不做操作。
    • 然后通过函数VirtualAllocEx,远程开辟大小为XMRig.exesizeofImage,基址为其ImageBase的==虚拟空间==
    • 然后往远程开辟的==虚拟空间==中,写入文件XMRig.exeDOS头,并将各个区段==展开==
    • 再修复傀儡进程的==加载基址(随机基址)==,并通过函数SetThreadContext,设置线程上下文,修改傀儡进程的入口点地址为XMRig.exeAddressOfEntryPoint
    • 最后通过函数NtAlertResumeThread,恢复进程,结束挂起

1570951469149

  • 避免用户察觉,监视当前所有进程,当检测到进程==任务管理器==时,便终止傀儡进程

创建了两个死循环,来==避免用户察觉==。

  • 其中第一个死循环,前面分析JudgeSystemFree_4019E1中提到过,来判断系统是否处于忙碌,当处于==忙碌时==,便终止傀儡进程,来避免用户察觉。不过这里还有一个检测函数traverse_Proc_4014EF,遍历进程,当检测到进程名为Taskmgr便返回1,退出循环,终止傀儡进程。

  • 第二个死循环,不断执行检测函数traverse_Proc_4014EF监控进程。当检测到进程Taskmgr,便一直睡眠,直到检测不到进程Taskmgr时,便从新执行当前函数inject_4010C5,创建傀儡进程,注入矿机。

1570951668325

    • 函数traverse_Proc_4014EF

快照系统中的所有进程,然后遍历所有进程,比较进程名,当遇到Taskmgr,便返回1,如图:

1570953268202

分析矿机程序

前面分析也有提到,dump.exe的最后一个区段.plato中包含一个矿机程序XMRig.exe,并且该矿机程序XMRig.exe是开源的,可以直接在网上找到,并下载,如图:

1570954719719

其中我们比较关注的就是傀儡进程,执行的指令对应的是什么意思,这里我们可以在GitHub上查看该矿机的帮助手册README,链接: https://github.com/xmrig/xmrig ,通过帮助手册中的Options,查看该框架的指令,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
-a, --algo=ALGO               specify the algorithm to use
cn/r, cn/2, cn/1, cn/0, cn/double, cn/half, cn/fast,
cn/rwz, cn/zls, cn/xao, cn/rto, cn/gpu,
cn-lite/1,
cn-heavy/xhv, cn-heavy/tube, cn-heavy/0,
cn-pico,
rx/wow, rx/loki
-o, --url=URL URL of mining server
-O, --userpass=U:P username:password pair for mining server
-u, --user=USERNAME username for mining server
-p, --pass=PASSWORD password for mining server
--rig-id=ID rig identifier for pool-side statistics (needs pool support)
-t, --threads=N number of miner threads
-v, --av=N algorithm variation, 0 auto select
-k, --keepalive send keepalived packet for prevent timeout (needs pool support)
--nicehash enable nicehash.com support
--tls enable SSL/TLS support (needs pool support)
--tls-fingerprint=F pool TLS certificate fingerprint, if set enable strict certificate pinning
--daemon use daemon RPC instead of pool for solo mining
--daemon-poll-interval=N daemon poll interval in milliseconds (default: 1000)
-r, --retries=N number of times to retry before switch to backup server (default: 5)
-R, --retry-pause=N time to pause between retries (default: 5)
--cpu-affinity set process affinity to CPU core(s), mask 0x3 for cores 0 and 1
--cpu-priority set process priority (0 idle, 2 normal to 5 highest)
--no-huge-pages disable huge pages support
--no-color disable colored output
--donate-level=N donate level, default 5% (5 minutes in 100 minutes)
--user-agent set custom user-agent string for pool
-B, --background run the miner in the background
-c, --config=FILE load a JSON-format configuration file
-l, --log-file=FILE log all output to a file
--asm=ASM ASM optimizations, possible values: auto, none, intel, ryzen, bulldozer.
--print-time=N print hashrate report every N seconds
--api-worker-id=ID custom worker-id for API
--api-id=ID custom instance ID for API
--http-enabled enable HTTP API
--http-host=HOST bind host for HTTP API (default: 127.0.0.1)
--http-port=N bind port for HTTP API
--http-access-token=T access token for HTTP API
--http-no-restricted enable full remote access to HTTP API (only if access token set)
--randomx-init=N threads count to initialize RandomX dataset
--randomx-no-numa disable NUMA support for RandomX
--export-topology export hwloc topology to a XML file and exit
--dry-run test configuration and exit
-h, --help display this help and exit
-V, --version output version information and exit

然而该样本执行的参数为:

1
-o monerohash.com:3333 -u 4BrL51JCc9NGQ71kWhnYoDRffsDZy7m1HUU7MRU4nUMXAHNFBEJhkTZV9HdaL4gfuNBxLPc3BeMkLGaPbF5vWtANQni58KYZqH43YSDeqY -p x -k --donate-level=1 -t 3

其各项参数解析如下表:

Options Meaning Value
- o URL of mining server(矿池) monerohash.com:3333
- u username for mining server(用户名) 4BrL51JCc9NGQ71kW….
- p password for mining server(密码) x
- k send keepalived packet for prevent timeout (防止超时) NULL
-donate-level Default donation 5% (5 minutes in 100 minutes) can be reduced to 1%(捐赠比例) 1
- t number of miner threads(线程数量) 3

其中矿池:monerohash.com:3333,为门罗币的矿池。

也就说明该样本通过创建傀儡进程,注入这个开源的矿机程序XMRig.exe,连接到(门罗币)的矿池,用病毒作者自己的用户名密码登陆上,来进行挖矿来获利的。

样本溯源

文件信息

FileName FileSize MD5
d48ab2e921f5c725672fce16135d1f09.vir 468480 bytes D48AB2E921F5C725672FCE16135D1F09
svchostx64.exe 468480 bytes D48AB2E921F5C725672FCE16135D1F09
XMRig.exe 283136 bytes C131DE679C5B230CDD3415A47F53ED10

网络信息

矿池URL为:monerohash.com:3333

矿池的 IP 服务器所在地
107.191.99.221 美国 纽约
107.191.99.95 美国 纽约

IOCs

互斥体 NIHILMsINERaassdaa
释放文件名 svchostx64.exe
计划任务名 Adasdsadas3id
特定区段名 .plato
矿池 monerohash.com:3333

查杀方案

  • 1.删除计划任务

由于该样本只在复制自身之后才会创建计划任务,因此我们可以直接删除该计划任务。

打开CMD,执行指令schtasks /delete /TN Adasdsadas3id /F,删除计划任务。

  • 2.将文件MD5加入杀软黑名单

由于该样本是复制自身到%AppData%目录下的svhostx64.exe,两文件的MD5值是一样的,并且该恶意样本是通过创建傀儡进程将自身文件中的矿机程序注入其中,因此只需要删除该文文件,便能实现查杀。

重启电脑,将恶意样本的MD5值**D48AB2E921F5C725672FCE16135D1F09**,加入杀软的应用黑名单中,进行一次全盘查杀

总结

该挖矿木马通过创建傀儡进程,将自身最后一个区段存储的开源矿机程序注入到傀儡进程中,从而进行挖矿,使攻击者利用受害者的主机赚取加密数字货币来获利。并且该挖矿木马还会检测当前系统是否空闲,是否启用进程管理器等操作来隐蔽自身,避免用户察觉。长期被感染的主机会大大降低系统硬件的寿命。因此大家应不要从不正规的网址下载文件,或是从不确定来源的地方接收文件,安装杀软,定期杀毒,做好防范工作。