0. 前言
Sodinokibi勒索病毒是去年最臭名昭著的勒索病毒之一,关于这个病毒样本的分析比较早就完成了,碍于懒于编辑一直没发出来。因为是以学习的目的进行分析,
所以分析过程尽可能比较详细地列出来。
分析结果可能存在一些遗漏或错误,也请大家指教。
图片从word拷贝过来比较模糊,所以把idb文件放在附件了
,病毒样本哈希已列出,请自行通过VT等获取。
1. 样本信息
这个样本来源于一封伪造的钓鱼邮件,该钓鱼邮件伪造邮件主题为“你需要偿还债务”,邮件内容为故意伪造的信息,并包含邮件附件名为“您的账号.zip”。
图1.1
下载附件并解压,是一个伪装成xlsx的可执行程序,文件名为“付款发票.xlsx.exe”, 样本详细信息如下:
文件名
|
付款发票.xlsx.exe
|
文件大小
|
365,568 bytes
|
文件格式
|
BinExecute/Microsoft.EXE(x86)
|
修改时间
|
2019年10月22日,12:28:22
|
MD5
|
57191a04cf06228c0643cc99b252ad1e
|
SHA256
|
3fcb6f1ae321e8052624132717fd1ef8 fb9e0f72c3a6aa650b16e1822bf728d5
|
2. 详细分析
2.1 加载PE文件
该模块的流程图如下图2.1。将加密的PE文件拷贝到内存,解密出真正的PE文件后,再拷贝到text节区,跳转到OEP开始执行代码。
图2.1
2.1.1 WinMain()
该勒索病毒程序未加壳,可以先直接拖到IDA中进行静态分析。定位找到WinMain()主函数,主函数首先循环检测内存的使用情况,确定是否有可分配的内存空间。
图2.1.1
2.1.2 LoadPayload()
如果内存空间足够,则跳出循环,执行图2.1.1中54行的LoadPayload()函数。该函数首先导入了kernel32.dll模块,再获得GlobalAlloc的函数地址。第一次分配大小0x372bc和地址为0x212960的内存空间,并将加密数据拷贝到这个内存空间中。
图2.1.2
接下,又两次调用了AllocAndDecrypt()函数,分配了两次内存空间(地址0x249c28和0x280EF0)。每次调用都将数据解密且存入当前的内存空间中。最终调用virtualProtect函数将第三个内存空间(地址0x280EF0)赋予可读可写可执行的权限。该内存起始地址即为payload函数起始地址。
图2.1.3
2.1.3 payload()
在内存(0x280EF0)成功加载解密后的payload模块后,执行图2.1.1中55行的函数,进入payload模块区域执行代码。使用OllyDbg动态调试进入payload模块入口点。
图2.1.4 payload函数起始地址
动态调试payload模块,首先获取了kernel32.dll模块的下列函数的地址。
图2.1.5
然后,payload模块又申请了一块内存空间,并将病毒真正的PE文件拷贝到这个内存。
图2.1.6
接着,跳转到新申请的内存空间中执行代码。
图2.1.7
内存空间3D0000的这部分代码作用是将已解密的病毒PE文件覆盖到进程映像中。
图2.1.8 从内存拷贝病毒PE文件到进程映像
最后,PE文件覆盖完成,该模块执行完毕退出,“leave”指令的下一条指令“jmp eax”, 返回到主线程的00403c7c处。
图2.1.9
主线程403c7c跳转到病毒真正的入口点403c33。
图2.1.10
但是,虽然已恢复了病毒真正的PE文件,但还不能dump下来静态分析(否则文件无法修复IAT),还需要等待程序动态加载DLL模块。
2.2 动态加载DLL
主线程第一条指令(call 407267)即为调用导入模块函数(图2.4.1中命名为Load Modules),动态解密修复IAT。
图2.2.1 导入第一个DLL
经过分析,IAT共修复了157+1=158个函数地址。
图2.2.2
2.3 dump和修复IAT
使用Scylla工具附加到正在调试的病毒进程,设置OEP点击“Get Import”,成功找到了IAT中158个导入函数地址。
图2.3.1
将进程的“.text”节区dump下来。
图2.3.2
最后,修复PE文件和修复导入表。
图2.3.3
修复完成,可以加载到IDA中分析了。
2.4 Start()
病毒start函数总体结构如图2.4.1,下面对其中关键函数一一分析。
图2.4.1
2.4.1 CreateMutex()
首先,病毒进程先创建了一个互斥体,防止双开。
图2.4.2
2.4.2 DecryptConfig()
然后调用解密文件配置信息的函数。
图2.4.3
开始解密前,通过CRC校验,才可以解密配置信息。
图2.4.4
动态调试得到解密信息。
图2.4.5
将配置信息导出,发现这是个json格式的文件,其各字段如下表:
pk(公钥) | pzprC6xbhNFhM/+qJI6gCrd2pnCgyRdai+B89OUhWAw= |
pid | 30 |
sub | 97 |
dbg | false |
fast | true |
wipe | true |
wht-fld
(过滤目录) | "programdata","program files (x86)","windows","program files", "$windows.~ws","intel","mozilla","application data","perflogs", "tor browser","$windows.~bt","google","$recycle.bin","appdata", "msocache","boot","windows.old","system volume information" |
wht-fls (过滤文件) | "ntldr","ntuser.dat","ntuser.dat.log","autorun.inf","thumbs.db","bootsect.bak", "bootfont.bin","ntuser.ini","desktop.ini","boot.ini","iconcache.db" |
wht-ext (过滤后缀名) | "icns","msstyles","cpl","hlp","lnk","deskthemepack","cmd","ics","adv", "dll","ico","exe","com","nls","bat","rtp","spl","msu","rom","key","ocx", "ps1","msp","386","drv","lock","mod","wpx","cab","idx","sys","icl", "nomedia","cur","scr","hta","themepack","bin","diagcfg","shs","ldf", "theme","prf","msc","mpa","msi","diagcab","ani","diagpkg" |
wfld | backup |
prc (结束进程) | "sqlbrowser","isqlplussvc","msaccess","onenote","wordpad","thebat64", "outlook","msftesql","winword","xfssvccon","excel","mysqld_opt","ocomm", "firefoxconfig","mysqld","ocssd","dbeng50","thunderbird","ocautoupds", "tbirdconfig","sqlwriter","synctime","mysqld_nt","mydesktopqos","dbsnmp", "oracle","mspub","mydesktopservice","sqbcoreservice","sqlagent","encsvc", "agntsvc","thebat","infopath","steam","sqlservr","powerpnt","visio" |
dmn (C2域名) | duthler.nl;citiscapes-art.com; …… |
net | false |
svc (结束服务) | "veeam","backup","memtas","svc$","mepocs","vss","sql","sophos" |
nobydy (加密勒索文本) | LQAtAC0APQA9AD0AIABXAGUAbABjAG8AbQB…… |
nname | {EXT}-readme.txt |
exp | false |
img (桌面壁纸) | QQBsAGwAIABvAGYAIAB5AG8AdQByACAAZgBpAGwAZQBz ACAAYQByAGUAIABlAG4AYwByAHkAcAB0AGUAZAAhAA0A CgANAAoARgBpAG4AZAAgAHsARQBYAFQAfQAtAHIAZQBh AGQAbQBlAC4AdAB4AHQAIABhAG4AZAAgAGYAbwBsAG wAbwB3ACAAaQBuAHMAdAB1AGMAdABpAG8AbgBzAAAA |
arn | false |
2.4.3 RunAsAdmin()
接着执行RunAsAdmin函数,确保程序以管理员权限运行。
图2.4.6
执行完这些准备工作后,下面进入main函数,执行病毒的功能模块。
2.5 Main()
病毒程序的整个功能模块分析后的逻辑如图2.5.1:
图2.5.1
2.5.1 BeforeCrypt()
图2.5.1的11行,这个函数体内做了许多操作,包括获取系统信息、注册表写入、构造勒索文本等。下面对其中关键函数进行分析。
图2.5.1.1
2.5.1.1 RegWriteKey()
首先是解密出注册表的写入路径和键名。
图2.5.1.2
然后,生成4个用于加密文件的密钥,并写入注册表。
图2.5.1.3
写入注册表效果如图2.5.1.4。
图2.5.1.4
2.5.1.2 GetUID()
这个函数,根据获得的用户的文件卷信息和处理器信息,进行CRC32计算得到特征值,然后拼接成用户的UID:
图2.5.1.5
2.5.1.3 RegWriteExt()
随机生成勒索文件后缀名,并写入注册表。
图2.5.1.6
在注册表中写入勒索文件后缀如下。
图2.5.1.7
2.5.1.4 GetSomeParameters()
紧接着获取计算机名、用户名,这里还获取了宿主机的一些其他参数。
图2.5.1.8
2.5.1.5 GetKeyboardLayoutList()
获取本机系统语言,并于硬编码的语言列表进行比较(查阅资料得知大部分为东欧国家):如果是列表中的语言,则退出程序;否则,继续执行。
图2.5.1.9
2.5.1.6 SetRegAndReadme()
这个函数在注册表写入键名为随机值,键值为前面获取得到的系统信息的加密字符串。
图2.5.1.10
图2.5.1.11 要加密的系统信息
加密后写入注册表中。
图2.5.1.12
在勒索文本内存中添加{UID}{KEY}{EXT}的值。
图2.5.1.13
2.5.1.7 Readme
调用了SetReadmeName函数设置勒索信文件名。
图2.5.1.14 勒索文件名
AppendReadme函数继续在Readme中补充数据。
图2.5.1.15
至此,这部分的生成加密密钥,获取系统信息计算作为标识ID,写入勒索文本等操作完成。
2.5.2 RegWriteFilePath()
图2.5.1的16行,这个函数在注册表中写入病毒文件路径。
图2.5.2.1
2.5.3 GetProcessSnapshot()
这个函数获取当前运行的进程,并与配置文件中的进程列表进行比较,如果相同,则结束该进程。
图2.5.3.1
图2.5.3.2
2.5.4 CloseService()
图2.5.1的21行CloseService函数,连接服务控制管理器,删除存在配置文件中的服务(“svc”字段)。
图2.5.4.1
下图显示的是枚举当前系统的服务。
图2.5.4.2
2.5.5 DeleteSystemShadowcopy()
图2.5.1的22行,这个函数的作用是删除系统卷影信息。首先,解密出base64加密了的powershell命令,然后调用“CreateProcessW”执行删除系统卷影的powershell命令。
图2.5.5.1
2.5.6 MainCrypt()
MainCrypt函数主要功能包括:
1. 创建了两个线程关联I/O完成端口分别执行入队和出队操作
2. 遍历所有文件夹,加密所有不在配置白名单(文件名和后缀)的文件;在所有文件夹下生成勒索文本和加密文件。
3. 多次获取宿主机的网络共享文件,并将其中的文件加密。
图2.5.6.1
下面分析主要的加密函数TraverseFilesAndCrypt()。
2.5.6.1 TraverseFilesAndCrypt()
这个函数遍历磁盘A-Z,然后递归调用遍历函数。
图2.5.6.2
进入递归函数,整体逻辑如下。
图2.5.6.3
如果是文件夹,则调用IsWhiteFolder函数判断是否属于不加密的文件夹。
图2.5.6.4
然后,调用CreateReadme函数,在当前文件目录下写入勒索信。
图2.5.6.5
如果是文件,则调用GetFileExt函数获取文件扩展名。
图2.5.6.6
接着,调用CryptFileAndWriteFileByIO函数,对文件进行加密,并将加密文件内容入队到I/O完成端口中。
图2.5.6.7
图2.5.6.7中的CryptFile加密函数,打开文件并获取了文件的读写属性,再调用真正的加密函数来加密文件。
图2.5.6.8
加密过程中,主线程进行加密计算,创建两个新线程,一个线程将I/O完成包入队到I/O完成端口,另一个线程出队将数据写入磁盘。这样可以加快文件加密效率。
图2.5.6.9
加密完成,效果如下,文件夹下包含勒索信和所有加密文件。
图2.5.6.10
2.5.6.2 GetNetResourceAndCrypt()
这个函数获取了宿主机网络共享资源,然后调用加密函数加密文件。
图2.5.6.11
2.5.7 SetWallpaper()
这个函数修改了桌面壁纸,并在桌面上绘制勒索信息。
图2.5.7.1
桌面显示效果如下。
图2.5.7.2
2.5.8 SendRequest()
这个函数,实现了如下功能:根据域名表构造url;向域名指向的服务器发送本机系统信息;处理返回的html文本信息。
图2.5.8.1
for循环遍历配置中域名表,GetDomian函数每次执行返回一个域名。
图2.5.8.2
然后调用JoinUrlAndSendHttpRequest函数,其函数逻辑如下。
图2.5.8.3
其中,首先调用了GenerateUrl函数生成一个URL。这个URL根据生成规则拼接而成的,拼接规则如下图。
例如:"https://duthler.nl/static/temp/lourjqziro.jpg"
调用SendHttpRequestAndGetRespone函数,向服务器发送HTTP消息,消息内容是存在注册表中的加密的宿主机系统信息:
图2.5.8.4
接着调用WinHttpReadData函数等待读取返回的html数据,但并未对返回的消息有实质的操作。
图2.5.8.5
分析时,其中一个C2服务器返回了一个HTML文件(但并未发现有实质内容,而且这些域名并非全是恶意域名)。
图2.5.8.6
病毒进程遍历完上千个域名发送http消息后,所有功能执行完毕,结束进程退出。
3. 病毒流程
4. 参考
1. 勒索软件Sodinokibi运营组织的关联分析
https://www.antiy.com/response/20190628.html
2. Sodinokibi 病毒分析报告
http://www.secwk.com/2019/09/23/7703/
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-2-16 18:06
被kumqu编辑
,原因: