0|前言
此次样本来源于Cyble威胁情报,虽然Cyble提及的该样本是Higaisa组织行为,但是并没有做准确的同源说明,国内开源情报渠道对木马中的外联地址标记了APT,但未作背景说明。
从网站、文件签名信息、运营商等我都觉得像国内靓仔所为,所以此点存疑。
但从技术角度讲,样本白加黑、反调试、加解密操作多,还是值得学习的,遂记录。分析共计三篇,前两篇为按样本执行流程分析,第三篇画画流程图、IOC列个表。
1|背景
Higaisa APT 据信起源于韩国,最初由腾讯安全威胁情报中心于 2019 年初披露。该组织的恶意活动至少可以追溯到 2016 年,涉及使用 Gh0st 和 PlugX 等木马,以及移动恶意软件。他们的目标包括政府官员、人权组织和其他与中国、朝鲜等有关的实体。
经威胁情报分析发现域名地址为“open-vpn.top”的钓鱼网站,模仿、伪造合法的 OpenVPN 网站,其下托管了一个恶意 OpenVPN 安装程序文件,该文件与正版 OpenVPN 可执行文件和基于RUST的恶意可执行文件捆绑在一起。
值得注意的是,基于 Rust 的可执行文件具有有效的数字签名。该数字签名归属于"Chengdu Nuoxin Times Technology Co., Ltd."。在Cyble的报告中还有一个数字签名信息“Zhiya Yunke (Chengdu) Finance and Tax Service Co., Ltd”。
2|样本分析
2.1|安装释放恶意文件
网站界面如下图,还原度很高,应该是直接克隆的。
可以从网站获取到“openvpn.exe”的文件。该文件是基于 32 位图形用户界面的可执行文件。
值得注意的是,签名信息归属于"Chengdu Nuoxin Times Technology Co., Ltd."。
执行该文件后,它会提示用户选择所需的语言,如下图所示。它会显示一个安装向导以继续安装过程。
在安装过程中,安装程序会在“C:Program Files (x86)OpenVPN”目录中放置几个文件。这些文件包含正版 OpenVPN MSI 安装程序 (openvpn.msi)、两个合法的运行时 DLL(vcruntime140.dll 和 vcruntime140Org.dll)。
以及一个使用C++编程语言编写的附加恶意 64 位控制台可执行文件,名为“rar.exe”,这是一个 shellcode 运行程序。然后,安装程序文件执行恶意“rar.exe”文件。
“rar.exe”和安装程序具有相同的签名信息,样本基本信息如下。
2.2|初次shellcode解密
找到功能函数位置后,开始调试。
这里会遇到小问题,不知道各位的IDA会不会遇到,这里JUMPOUT报错,在报错对应的汇编界面找到“db 0xXX”指令,按快捷键c就可以,应该是IDA数据类型识别的问题,具体成因不太清楚。
程序执行完成一些初始化操作后,连续调用FindResource()、LoadResource()和SizeofResource()等API函数在程序数据段中定位和获取shellcode密文信息。
动态调试FindResource()函数,通过字符0xA确定shellcode密文位置。
随后加载、计算shellcode密文长度为0xE2A1,做解密操作。
2.3|shellcode段执行
定位到shellcode函数入口。
对应汇编指令是call rsi。
因为是解密的,静态的初始文件中没有这一段,在内存布局中dump出这一段,在IDA中单独打开。
2.3.1|反调试
代码继续执行会遇到一段反调试代码。
这段代码计算当前shellocde代码段(长度E035)的32位hash值,与硬编码在代码中的正确计算结果(AEE91C72)对比,具体hash的计算方法如上图。
动态调试信息如图。
具体实现原理,当使用调试器并添加断点时,调试器通常会在代码的某个位置插入 0xCC 操作码(代表中断指令 INT 3),以便在执行到该位置时触发断点。但是,由于插入了新的操作码,改变了代码段的内容,导致计算得到的哈希值与预期的哈希值不匹配,从而触发反调试的检测。
看明白了也很容易绕过,在汇编指令cmp ebp,r8d,修改寄存器r8的低32位就好了。若哈希不匹配,Shellcode 将终止执行。
2.3.2|解密字符
绕过反调试后,shellcode会解密一些字段信息,解密结果位于地址v14处。
解密后可以看到一些网络等相关的字段,后续应该会有网络通讯。
因为解密后改变了内存结构,需要继续静态分析就要重新dump内存。同时在新的shellcode调用前,做了一次相同的反调试操作。
正确的shellocde段32位HASH为86412D7,在指令cmp修改寄存器edx的值即可绕过。
经过一段时间的分析,可以发现,整个代码的函数调用,尤其是windows API的调用,几乎以函数指针的形式调用,定位函数都是用一个固定地址做偏移定位函数,该地址有固定字节0x01F4。
这样做在开发层面对静态免杀有一定的帮助。
继续分析,重新打开二次解密的shellcode。
进入新的shellcode段出了点问题,堆栈不平衡。不知道IDA数据识别的问题,还是强制转code的问题,动态调用的是地址“000001F254B9D450”,而不是下图中的“1F254B9D458”,有8个字节的偏移,具体成因尚不清楚,以动态调试结果为准。
进入shellcode后,初始化了一些dll,例如KERNEL32.DLL等,利用相同的反调试思路校验了32位的HSAH。
2.3.3|解密字符、初始化函数
初始化结束,将这些函数放入a1的地址段,后续都以函数指针的形式调用。
后续一段异或解密操作(异或参数0x60),下两图为解密前、解密后。
随后做整数和字符串转换,解密出windows API。
结束之后又将整数类型的明文异或还原为密文。
这一段操作,完整代码如下。
2.3.4|线程创建-生成128位密钥
完成解密操作后,创建线程,并且利用WaitForSingleObject函数保证线程函数句柄v34执行结束。
线程函数首先利用函数UUIDCreate()创建32位的整数,并使用sprintf()函数拼接。
53C52E66F3E1654CAFBC000FDEFCDC0B
662EC553-E1F3-4C65-889C6FFCB8
之后又会再次创建一个UUID,新创建的 UUID 的最后一个字节被用于通过三目运算确定一个字节,该字节将用于使用基于ROR和ADD的指令为续保的UUID计算 32 位哈希。这个过程与反调试操作时shell代码计算hash的过程类似。
该哈希值传递给CryptBinaryToStringA() API 函数,以便使用第二个 UUID 生成 Base64 编码数据,最终获取base64形式的hash。
Shellcode 使用 Windows加解密组件API CryptCreateHash()对第一个 UUID 生成 MD5 哈希值,利用CryptGetHashParam()获取hash值。
利用这个HASH值继续HASH一次,随后利用连续hash的结果作为基础数据在CryptDeriveKey()函数创建 128 位 AES 加密密钥。
之后又生成了一次CryptDeriveKey,基础数据有变化。
上面这一段看得有点头皮发麻,后面具体调用了这些key或者hash应该就清晰一点。
2.3.5|socks初始化
shellcode利用函数初始化 Winsock 库。
可以看到是winsock 2.0版本。
2.3.6|解密获取IP
随后在shellcode最后,异或解密到IP(43.246.209.83)以及部分二进制流。参与异或解密参数动态的,遍历shellcode靠近末尾的一段。
2.3.8|搜集系统基本信息
随后搜集了一些系统基本信息,获取进程ID、判断操作系统类型等,模块话输出为定义好的格式,即“%s-%d-%d-%d”。
最终动态调试结果如下,win10,64位操作系统等信息。
2.3.9|创建线程-连通性检测
完成这些操作后,就会创建线程做连通性检测。
在该线程中建立套接字连接并验证互联网连接。Shellcode 尝试与下图中的URL连接。如果可以访问这些网站中的任何一个,Shellcode 就会继续执行其他操作。下图显示了包含硬编码 URL 的例程。
文章已经很长了,后续再一篇。
原文始发于微信公众号(帅仔回忆录):近期 Higaisa(黑格莎) APT 针对中国用户的钓鱼网站、样本分析(一)