参照 VMware Security 博客 学习 emotet 家族的 C2 配置提取,并对 DLL 进行完整分析。
0x00 前言
这次 DLL 的分析主要是参照 VMware Security 博客 学习 emotet 家族的 C2 配置提取,所以只有一个 DLL 样本,没有对应的宏文档和窃密程序。
由于 C2 配置在解密后的子 DLL 中,所以我首先分析的是内层的子 DLL,这一篇文章都是子 DLL 转储出来后的分析过程。分析完子 DLL 后我想看下母 DLL 是怎么与子 DLL 关联的,所以又把母 DLL 的行为分析了,但是由于篇幅的原因和手法的不太一样,所以我把母 DLL 的分析放在另一篇了。
建议先看这篇子 DLL 的分析过程,因为这是我先分析的,样本的发展阶段也在这里提及~
0x01 内层 DLL 分析
样本 IOC
HASH | 值 |
---|---|
MD5 | 4e22717b48f2f75fcfd47531c780b218 |
SHA1 | 60b637e95b1f2d14faaa71085b7e26321bfeeb6d |
SHA256 | 7f94107c9becbcc6ca42070fca7e1e63f29cdd85cbbd8953bbca32a1b4f91219 |
总体行为预览
动态获取函数手法
在 Emotet C2 Configuration Extraction and Analysis 文章中我得知该样本每个动态获取的 API 函数都通过包装器包装起来供外层核心代码调用,所以我们分析时需要进入每个包装器中识别出动态获取的函数。更高级的是在 get_dll_and_funbase 上下断点,因为所有 API 函数都通过此函数动态获取,这样我们就可以提取出所有 API 包装器并命名它们。
举例文章中分析的 ExitProcess 包装器:
在下面的分析中,所有标注了 API 函数别名的,都是基于手动进入包装器内提取动态获取的函数后再回到包装器外标注出来。
字符串解密手法
举例,在我定义的包装器 data_decrypt_to_string 中,申请空间,解密字符:
(这一部分待解密的字符串都在 .text 段中,在 IDA 的 string window 窗口中看不到)
混淆手法
通过控制流平坦化和大数混淆使相同代码编译得到的二进制特征各不相同,即干扰杀软特征匹配检查,也让逆向跟踪分析增大困难,特别是静态分析。
控制流平坦化:
大数混淆:
执行多次数学运算,运算结果有的传入函数中,但是从不使用。有的作为控制流跳转的一部分,不断混淆代码流。
所属阶段
该转储出来的内部 DLL 属于内层恶意代码,从火绒实验室的 层层对抗安全软件 火绒对Emotet僵尸网络深度分析 中我们可以对比出此次分析的内层恶意代码属于Emotet内层恶意代码中内层PE混淆的第三阶段。
3.控制流平坦化和大数运算混淆
在原有API动态获取和加密字符串的基础上,病毒使用控制流平坦化和大数运算进行混淆。这使得相同的代码编译得到的二进制特征各不相同,从而增大安全软件的检出难度,阻碍分析人员对病毒功能逻辑的分析。
控制流平坦化使逻辑难以分析
大数运算混淆改变二进制特征
C2 配置提取与解密
VMware Security 博客 中的重点就在这个 emotet 家族的 C2 配置提取,但是过程并没有很明晰,只是直接放上 截图说这就是 C2 的解密函数,现在我从头到尾分析过一遍后发现还是有迹可循的,比如 F8 单步执行时会有明显的 ECK1、ECS1 字样,在后面调用大量加密解密和网络通信 API 中也会发现有对密钥和 IP 的导入,凭借这些迹象足够我们定位到这个 C2 配置解密函数了。
迹象定位
解密流程分析
加密数据格式及手动解密
根据 VMware Security 博客 的研究可以知道公钥在加密中的数据格式,第一个 Dword 是解密的 key,第二个 Dword 是公钥的长度,剩下的就是加密的数据了,在上面解密流程分析的伪代码中也可以分析得出来这点。
手动解密中可以通过将加密数据区的第一个 DWORD 与第二个 DWORD 进行异或来获得加密数据的长度。从第 3 个 Dword 开始,在长度范围内,用第一个 DWORD 对每个块进行 XOR,即可得到解密后的公钥。
以上面 ECK1 为例解密:
提取公钥
通过以下方法得到如下公钥:
ECK1(base64 编码):RUNLMSAAAADzozW1Di4r9DVWzQpMKT588RDdy7BPILP6AiDOTLYMHkSWvrQO5slbmr1OvZ2Pz+AQWzRMggQmAtO6rPH7nyx2
ECS1(base64 编码):RUNTMSAAAABAX3S2xNjcDD0fBno33Ln5t71eii+mofIPoXkNFOX1MeiwCh48iz97kB0mJjGGZXwardnDXKxI8GCHGNl0PFj5
同理解密 IP 配置
行为分析
DLL 调试及入口设置
我们通过微软的 rundll32.exe 来调试子 DLL,按照母 DLL 样本给出的命令行参数设置同样的即可,这里我们跟进的是 DllRegisterServer 的导出函数。
"C:\Windows\SysWOW64\rundll32.exe" C:\Users***\AppData\Local\Kfsdwbgbdwjo\tlcdjloq.dfv,DllRegisterServer
先执行的入口点,检查命令行参数
生成随机数:(用途不详)
获取路径信息,尝试连接服务控制管理器:(猜测是想将自身作为服务来开机自启动)
获取命令行参数:(应该是冗余操作)
获取当前文件信息
获取计算机相关信息
使用事件对象来通知等待线程发生事件
线程函数
创建一个线程,线程的作用是检查当前目录下文件改动的情况。
解密公钥和 IP 等 C2 配置
导入ECK1公钥
动态生成 AES 密钥:(猜测是用 ECK1 公钥加密动态生成的 AES 加密密钥再来加密信息)
导入ECS1公钥
检索文件目录来确认当前目录中只有一个文件
收集当前系统相关信息
检索关联的远程桌面服务会话
对相关系统信息进行加密
BASE64 格式化输出加密信息
网络通信操作,利用COOKIE发送加密数据,读取远程文件
解密读取的远程文件数据
对文件流进行 HASH 加密后验证签名
注册表操作:(空操作)
创建临时文件并复制当前文件过去来躲避查杀
退出程序
行为总结
首先子 DLL 先获取当前程序命令行参数,然后截取出对应参数来看是否在运行导出函数 DllRegisterServer 。然后获取就尝试连接并打开本地服务控制管理器同时获取自身路径信息,猜测是想将自身作为服务来开机自启动。
紧接着检索程序自身所在问价的创建,访问,写入时间等信息,并对比系统时间来查看文件状态是否正常或已被他人操作。(检查是否改动的操作是在开启的线程中进行的)
然后就是获取本地计算机名、磁盘序列号、系统版本信息、关联的远程桌面服务会话来加密传输,其中加密的方式应该是使用解密出的 ECK1 公钥加密动态生成 AES 密钥后再对上面信息进行加密,并对加密后信息以 base64 格式附在 cookie 中发送给解密出的 C2 服务器列表。
再然后就是在发送完相关信息后从 C2 服务器处读取远程文件并进行 HASH 验证,猜测是进行另一种恶意操作,但是这里并没有跟踪到。
最后就是在系统开机自启动的 RUN 目录下进行操作,由于程序最后尝试在系统临时目录下根据系统时间创建唯一的临时文件名并复制自身过去,可以猜测程序想写入的是复制成功后的临时文件路径到 RUN 目录中来维持权限和延长存活时间。
0x02 函数链顺序划分
获取程序命令行参数切割导出函数并匹配验证:(对比不上就退出程序)
GetCommandLineA---->L"DllRegisterServer"---->lstrcmpiw_data(---->SHGetFolderPathA)
申请内存空间:processheap---->RtlAllocateHeap
生成随机数:"RNG"---->BCryptOpenAlgorithmProvider---->BCryptGenRandom---->BCryptCloseAlgorithmProvider
连接控制管理器:OpenSCManagerW
获取计算机信息:SHGetFolderPathA、GetModuleFileName
获取时间信息:GetTickCount
获取当前程序命令行参数:GetCommandLineW---->CommandLineToArgvW---->LocalFree
打开文件获取信息:GetModuleFileName---->CreateFileW---->GetFileInformationByHandleEx---->GetSystemTimeAsFileTime---->closehandle
获取计算机系统信息并格式化输出:GetComputerNameA---->GetWindowsDirectoryW---->GetVolumeInformationW---->sprintfW
使用事件对象创建线程(可参考:CreateEvent函数用法):CreateEventW---->CreateThread
线程函数:GetModuleFileName---->PathFindFileNameW---->CreateFileW---->ReadDirectoryChangesW---->
循环格式化输出解密 IP 配置:循环---->snwprintf
使用 Windows 的 API 导入 ECK1 密钥附加生成的私钥创建协议值:
“ECDH_P256”+L"Microsoft Primitive Provider"---->BCryptOpenAlgorithmProvider(256位素数椭圆曲线 Diffie-Hellman 密钥交换算法)---->BCryptGenerateKeyPair---->BCryptFinalizeKeyPair(啥变化也没,可能就是标志用的)---->L"ECCPUBLICBLOB"---->BCryptExportKey---->memcpy---->BCryptImportKeyPair---->BCryptDeriveKey---->BCryptDestroySecret---->BCryptCloseAlgorithmProvider
使用 Windows 的 API 进行 AES 加密:
“AES”+L"Microsoft Primitive Provider"---->BCryptOpenAlgorithmProvider(基于高级加密标准 (AES) 密码的消息认证码 (CMAC) 对称加密算法。)L"ObjectLength"---->BCryptGetProperty---->BCryptImportKey---->BCryptCloseAlgorithmProvider
使用 Windows 的 API 导入解密的 ECS1 密钥:
L"ECDSA_P256"+L"Microsoft Primitive Provider"---->BCryptOpenAlgorithmProvider---->L"ECCPUBLICBLOB"---->BCryptImportKeyPair---->BCryptCloseAlgorithmProvider
单个线程等待函数:WaitForSingleObject
检索文件目录并用通配符比较文件名,确保当前目录文件夹中只有一个文件:"%s%s"---->sprintfw---->PathFindFileNameW---->L"%s\"---->L"C:\Users\xxx\AppData\Local\Kfsdwbgbdwjo\\"---->FindFirstFileW---->FindNextFileW(2次)---->PathFindFileNameW---->lstrcmpiw---->FindClose
收集当前系统相关信息:RtlGetVersion---->GetNativeSystemInfo
检索与指定进程关联的远程桌面服务会话:ProcessIdToSessionId---->GetCurrentProcessId
加密信息:L“SHA256”+L"Microsoft Primitive Provider"---->BCryptOpenAlgorithmProvider---->L"ObjectLength"---->BCryptGetProperty---->BCryptGetProperty---->BCryptCreateHash---->BCryptHashData---->BCryptDestroyHash---->BCryptCloseAlgorithmProvider---->BCryptEncrypt(这个句柄不知道是谁的,加密了两次)
BASE64 格式化输出:CryptBinaryToStringW
BASE64加密数据合并到cookie中传输:L"Cookie: %s=%s\r\n"---->sprintfW
网络通信操作:InternetOpenW---->InternetConnectW---->HttpOpenRequestW---->InternetSetOptionW(选项要16进制转10进制)---->InternetQueryOptionW---->InternetQueryOptionW---->HttpSendRequestW---->HttpQueryInfoW---->InternetReadFile---->InternetCloseHandle(3次)
又加密读取的文件数据流:L“SHA256”+L"Microsoft Primitive Provider"---->BCryptOpenAlgorithmProvider---->L"ObjectLength"---->BCryptGetProperty---->BCryptCreateHash---->BCryptHashData---->BCryptFinishHash---->BCryptDestroyHash---->BCryptCloseAlgorithmProvider---->BCryptVerifySignature
注册表操作:0x800001(HKEY_CURRENT_USER)+ L"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"---->RegCreateKeyExW---->RegDeleteValueW---->RegCloseKey
复制文件到临时文件:GetTempPathW---->GetTempFileNameW---->L"C:\Users\xxx\AppData\Local\Kfsdwbgbdwjo\tlcdjloq.dfv" + L"C:\Users\xxx\AppData\Local\Temp\9000.tmp"---->SHFILEOPSTRUCTA(移动文件)---->PathFindFileNameW---->RemoveDirectoryA
- 本文作者: 沐一·林
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/1804
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!