最近,在SAS会议上,我谈到了“时髦恶意软件格式”- 仅由专有加载器加载的恶意软件使用的可执行格式。恶意软件作者使用它们才能使静态检测更加困难,因为自定义格式不会被AV扫描仪识别为可执行文件。

使用非典型格式也可能会减慢分析过程,因为无法通过典型工具将文件从框中解析出来。相反,我们需要写自定义加载器为了自由分析它们。

去年,我们描述了一种这样的格式关于隐藏蜜蜂的帖子。这次,我们想向您介绍在SAS会议上讨论的另一个案例。它是海洋莲花的样本,也称为APT 32,是与越南相关的威胁组。

样本

49A2505D54C83A65BB4D716A27438ED8F065C709.- 主要可执行文件

特别感谢Minh-Triet Pham Tran提供材料。

概述

该示例包含两个元素—blob和cab—它们都是以相同的未知格式执行的。自定义格式是通过从PE格式转换来实现的(我们可以通过观察一些典型的PE文件工件来猜测它,即manifest)。然而,头部是完全自定义的,并且它的加载方式与PE不相似。典型PE中的一些信息(例如,节的布局)没有保留:节被打乱了。

起源

这个样品是2017年6月10日的,来自以下邮件:

网络钓鱼电子邮件的内容以及其附件

标题“SổTayVấnđềPháplýchocácnhàcnạtđộngnhânquyền”转换为:“人权活动家的法律问题手册。”它是针对越南活动家的矛网络钓鱼活动的主题行。

恶意样本作为附件交付给电子邮件:压缩可执行文件。图标试图模仿PDF(Foxitpdf Reader)。

带有FoxitFDF图标的可执行文件

行为分析

运行后,将样本复制到%TEMP%,解压缩并启动诱饵PDF。

主要可执行文件和诱饵复制到TEMP文件夹

虽然用户正忙于阅读推出的文档,但下降器解压缩真正的有效载荷。它被删除了C:\ programdata \ Microsoft帮助

未包装恶意软件的所有元素

之后删除Dropper可执行文件。

恶意软件管理以默认级别绕过UAC。我们可以看到申请sporder.exe跑步升高的特权。
持久性由一个简单的运行密钥提供,导致丢弃的脚本:

添加了运行密钥(从sysinternals autoruns查看)

有趣的因素是,样本有一个“到期日”,之后安装人员不再运行。

内部

主要可执行的sporder.exe与UPX打包。它导入dll sporder.dll:

Import表的Sporder.exe(从PE-BEAR的视图)

sporder.dll导入另一个丢弃的dll,HP6000.dll.

Import表的Sporder.exe(从PE-BEAR的视图)

但是,密钥恶意软件功能由任何丢弃的PE文件提供。它们刚用作装载机。

事实证明,核心隐藏在两个未知的文件中:Blob和Cab。

自定义格式

扩展名为BLOB和CAB的文件用XOR混淆。解码后,我们注意到一些可读的代码串。然而,它们都不是有效的PE文件,我们也找不到任何典型的头文件。

斑点

blob文件由xor混淆。我们可以看到重复模式并将其用作XOR键:

SPORDER.BLOB(原始版本),选择了重复模式

结果,我们得到以下清晰版本:2E68AFAE82C1C299E886AB0B6B185658

Blob的标题:

Blob文件看起来像一个已处理的PE文件,但其部分似乎是交换顺序。第一部分似乎是.data,而不是.text。

我们可以看到来自的可见伪像Bzip库和C ++标准库。

出租车

CAB文件以类似的方式与XOR混淆,但具有不同的键:

当我们应用密钥时,我们得到一个类似的清晰版本:B3F9A8ADF0929B2A37DB7B396D231110

此示例还具有自定义标题,它不会类似于PE标题。但是,我们发现内部的部分是PE文件的典型,例如清单。

装载机

事实证明,两个文件都由HP6000.dll加载:67年b8d21e79018f1ab1b31e1aba16d201

加载函数以混淆方式执行:执行DLLMain时,它会修补加载DLL的主要可执行文件。

首先,检索当前模块的文件名。然后,读取文件并获取入口点的地址。然后,将在内存中加载的模块设置为可执行文件:

使用VirtualProtect使主模块可写

最后,修补字节,以便输入点将重定向到加载DLL中的相应功能:

修补主模块的入口点,byte byte

这就是应用程序修补程序后主模块的入口点如何关注:

主模块(sporder.exe)打补丁后的入口点

我们看到DLL内函数的虚拟地址(RVA 0x1210 + DLL加载基础)移动到EAx,然后将EAx用作跳转目标。

从RVA 0x1210开始的功能是Blob和Cab的装载机:

开始加载功能

这个重定向是有效的,因为当可执行文件被加载到内存中时,在主模块的Entry Point被命中之前,它的Import Table中的所有dll都会被加载,并且每个dll的DllMain都会被调用。加载dll之后,就开始执行主可执行文件。在我们的例子中,修补过的入口点重定向回DLL。

加载BLOB和CAB的函数内部:

加载BLOB和CAB的函数

如你所见,CAB文件首先被加载:

执行函数加载CAB文件(无条件)

此外,我们看到此功能检索某些环境变量。此变量用于存储应用程序的状态,并在连续的执行之间共享。根据此状态,可以采取多个执行路径中的一个。

通过连接创建变量的名称:

  1. 硬编码字符串:l“本地\\ {076b1db0-2c01-45a5-bd0a-0cf5d6410dcb}”
  2. 可执行文件的名称
  3. 本地用户名
设置变量名称

内容变量可以是' @ ',' * ',':'。如果为空,则设置第一个值“@”。这些变量被转换为控制流的特定状态。

  • '@' - >状态1
  • ' * ' ->状态2
  • ': ' ->状态3

主要过程在每个状态更改上重新启动。最后,状态3创建互斥锁并使用BLOB扩展加载文件。

最终状态:设置互斥锁并加载BLOB

互斥锁名称与变量名称相同,但使用后缀“_m”添加:

设置互斥锁

当应用程序运行时,我们可以看到BLOB在主模块的内存中以可执行的形式加载:

Sporder.exe的内存,从过程黑客查看

通过比较加载在内存中的格式和存储在磁盘上的格式,我们可以看到,在加载过程中跳过了BLOB的开始和结束。因此,我们可以猜测,这些部分是一些头文件,包含加载所需的信息,但不包含执行所需的信息。位于文件开头的头文件将被引用为Header1,位于文件末尾(footer)的头文件将被引用为Header2。

Memory中的Header2文件与其等效于磁盘:

将内存转储与原始文件进行比较

我们还发现重新安置了一些地址(添加了新的图像基础)。

反转反转PE

具有扩展速度驾驶室和BLOB的文件由相同的功能加载:

查看IFL(交互式功能列表)

装载机的核心处于以下功能:

加载功能

这是我们需要分析的函数,以便从自定义格式中进行意识。

让我们来看看加载过程本身。

头部的第一个DWORD是校验和(稍后将用于验证解码模块)。然后,我们有两个用作XOR键的DWORD。一旦它们被获取,标题的其余部分被解码。

示例:解码CAB文件

应用键后,我们以其清晰的形式获得文件的内容。但在模块继续加载之前,必须将头文件中的校验和与实际校验和进行比较,并使用自定义公式进行计算:

校验和计算算法

来自标题的下一个值用于计算用于加载模块的可执行部分的大小。在目前分析的情况下(驾驶室文件),它是0x17000:

标题1在驾驶室文件开头,解码

因此,0x17000 + 0x2000是将为有效载荷分配的内存的大小。

alloc_module_size.

示例(来自CAB文件):

alloc_executable.

然后,复制负载的0x17000字节,但是跳过包含Header1的开始部分(前16字节)。

复制模块内容后,Header2用于继续加载。

看看Header2,我们可以看到一些与Header1的相似之处。

初始DWORD是模块(DllMain)的入口点。它将在完整模块加载后很快加载:

加载模块的LONY点调用的加载器中的地址

然后,我们有一个值在计算要分配的存储器大小的公式中使用的值。正在分配此时间的新存储区域用于将加载的导入(将进一步解释完整过程)。

从概念上讲,我们可以将Header 2分为两部分。

首先是包含两个DWORD值的序言。来自当前分析的CAB文件的示例:

标题2(在驾驶室文件的末尾) - Prolog是HILIGHTED
  • val [0] = 0x21a0 - > dllmain的RVA
  • val [1] = 0x013d - > val [1] * 8 + 0x400 - >下一个区域的大小分配

然后有自定义类型的记录列表。每个记录代表加载模块所需的不同信息。它们由由记录开头的DWORD表示的类型ID标识。

Header2(在CAB文件的末尾)——记录被高亮显示

搬迁

类型1代表搬迁。它有一个DWORD作为参数。这是一个需要重新安置的地址。

typedef struct {dword reloc_field;} Reloc_t;
解析1型

我们可以看到该字段如何用于重新定位地址。示例:在0x8590填写地址:

由重定位记录指向的地址被重新定位为装入模块的基座

出口

2类型代表导出功能。尖头地址存储在列表上,以便在加载完成后稍后调用。此记录有三个DWORD参数。

typedef struct {
DWORD计数;
DWORD entry_rva;
dword name_rva;
} entry_point_t;

类型2的记录示例:

解析2型

存储地址:params[1] = 0x00001030

原始文件中类型2的记录

通过观察执行流程,我们可以确认在加载过程完成后正在调用存储的函数:

正在调用CAB模块导出的加载器中的地址

导出的功能可以与他们的名称一起存储。

进口

类型3代表进口。它有四个dword参数。

typedef struct {
DWORD类型;
dword dll_rva;
dword func_rva;
DWORD IAT_RVA;
import_t;
解析3型

负责编码导入的块示例:

原始文件中的3型记录

类型:params [0] = 0x00000002 - 表示函数将按名称导入,含义本记录的所有可能类型。

DLL的地址:params [1] = 0x0107da

DLL名称

导入地址:params[2] = 0x010774

导入名称

与PE格式相比,导入函数的地址未加载到主模块中。相反,它被写入单独的可执行区域(在给定的示例中,它写入VA:0x00240001):

填充进口

然后,填充导入的地址填写在主模块中。需要填充的主模块中的地址由此记录的最后一个参数指定。在给定的示例中,块[3] = 0x0000E014填充0x00240001:

填充到导入的地址

非典型IAT.

嵌入式列表中的函数用于加载器,但是,如前所述,地址不会填充在正常IAT中,典型的PE格式。相反,所有都填写为存储在新分配的内存页面中的跳转列表。

生成的跳跃列表,导致导入的功能

import loading函数不仅填满地址,还发出必要的跳转代码:

检索和写入导入函数的地址并写入发出的跳转
类型字段的含义

导入记录具有字段类型,可以具有以下值之一:1,2,3,4。

1和2是最重要的:它们用于加载导入。1表示按序号装载,2表示按名称装载。剩下的3和4用于清除不再需要的字段。3擦除导入名称,4擦除DLL名称。

负责加载功能的代码的片段

当发生类型3或4的记录时,IAT区域中的指针仍递增,因此我们可以在函数记录之间看到一些间隙:

跳跃之间的空白

自定义文件的功能

CAB文件是另一个安装程序,通过创建服务来为整个包提供持久性:

“c:\ windows \ system32 \ wscript.exe”/ b / nologo“c:\ users \ tester \ desktop \ mod \ sporder.vbs”

创建服务

我还生成丢弃的vbs脚本:

删除脚本

首先加载CAB文件,只是为了安装恶意软件,然后删除。

所有的间谍相关功能由稍后加载的BLOB执行,并在加载器的内存中保持持久性。

除了以自定义格式而言,Blob也很大困扰。

我们可以观察其连接到其中一个CNC的尝试:

连接到CNC的尝试
png.eirahrlichmann.com.AndyChroEder.com:9091 png.eirahrlichmann.com:3389

其中一些域从以前的Ocean Lotus报告中已经知道,例如[天空白皮书]。

海洋莲花:创意APT

海洋莲花经常让研究人员惊喜,具有创造性的混淆技术。最近,发现使用隐写术来隐藏其可执行文件的不同样本(您可以阅读更多信息以下是网站ThreatVector的报道).我们所描述的这种形式只是其中一种不寻常的植入方式。

附录

解析器的描述格式:https://github.com/hasherezade/funky_malware_formats/tree/master/lotus_parser
SAS会议的介绍:

此视频无法显示,因为您的功能饼干目前禁用。

要启用它们,请访问我们的隐私政策并搜索cookie部分。选择“点击这里”打开隐私首选项中心并选择“功能饼干”在菜单。您可以将选项卡切换回“积极的”或通过移动标签来禁用“不活跃。”点击“保存设置。”