PlayCTI
Play 勒索软件(又名PlayCrypt)活动至少从2022年7月中旬开始活跃。最多五张勒索信 play勒索软件已上传到病毒总数到目前为止。2022年8月中旬,首例公开的 play勒索软件是在一名记者揭露阿根廷科尔多瓦司法机构受害后宣布的。
众所周知,运营商使用常见的大型游戏狩猎(BGH)战术,如SystemBC RAT的持久性和cs后渗透的战术。他们还使用自定义PowerShell脚本和AdFind进行枚举,使用WinPEAS进行权限提升,使用RDP或SMB在目标网络内部进行横向移动。
该组织在加密文件后附加“.play”,其勒索信只包括“PLAY”一词和一个与威胁行为者通信的电子邮件地址。众所周知,威胁行为者使用WinSCP泄露文件,但不像许多其他BGH勒索软件活动那样拥有Tor数据泄露网站。
非常感谢我的人威尔托马斯为这个信息!
概述
这是我对 PLAY勒索软件。我将只关注它的反分析和加密特性。还有一些其他特性,如DLL注入和网络,将不在本分析中讨论。
尽管它很简单,但PLAY被大量独特的技巧严重混淆,这些技巧之前没有被任何勒索软件使用过。
该恶意软件使用通用的RSA-AES混合加密系统来加密文件。PLAY的执行速度相当平均,因为它使用深度优先遍历算法来遍历文件系统。尽管启动了单独的线程来加密每个文件,但这种递归遍历会显著地降低其性能。
IOCS
分析的示例是32位Windows可执行文件。
MD5 :223有效期1610年432年1月1日aa06年c60年7月9日a6
SHA256 :006ae41910887f0811a3ba2868ef9576bbbd265216554850112319af878f06e55型飞机
示例 :恶意软件市场
图2:病毒总数结果。
勒索信
默认勒索信的内容以编码字符串的形式存储在PLAY的可执行文件中,其中包含字符串“PLAY”以及受害者联系威胁参与者的电子邮件地址。
PLAY的 勒索信文件名是“ReadMe.txt”。
图3:PLAY的赎金笔记。
反分析
反分析:面向回报的程序设计
在IDA中打开可执行文件时,我们可以看到大多数汇编代码没有意义,也不是很有意义。一个例子可以从 WinMain软件,其中没有clear return语句会在有效代码中弹出垃圾字节。
图3:WinMain中的反编译功能。
如上面的反汇编代码所示,中的控制流 WinMain软件 唿叫 子模块_4142F5 并且在返回时, 埃迪弹出,然后在0x4142F2处遇到垃圾字节。因此,IDA无法正确地反编译此代码。
图4:未修补的WinMain反编译代码。
检查 子模块_4142F5 ,我们可以看到存储在堆栈指针处的值立即在 雷滕指令被执行。
我们知道 唿叫 指令基本上包含两个原子指令,一个推送下一个指令的地址(在 唿叫指令)到堆栈上且一个跳到正被调用的子例程。当密码输入 子模块_4142F5时,返回地址(在本例中为0x4142F1)存储在堆栈顶部的堆栈指针处。子例程将0x35加到这个地址,将返回地址更改为0x414326,然后 雷滕跳到它上面。
知道这一点后,我们可以向下滚动并尝试反汇编0x414326处的字节,以获取 WinMain软件密码。
图5:反汇编的隐藏代码。
使用这种面向返回的编程方法来转移程序的常规控制流,PLAY能够通过IDA的反汇编和反编译绕过大多数静态分析。
我们还可以很快看到,在0x41433A处,还有另一个调用指令,后面跟着一些垃圾字节。这意味着模糊处理在代码中发生多次。
我的方法是通过编程修补所有这些 唿叫指示完毕。在我的分析中使用的一个简单修补程序是计算跳转(添加到返回地址的值)并替换 唿叫 指令与 跳跃指令到目标地址。
为了扫描所有这些模糊代码,我使用了3个不同的(但非常相似的)正则表达式(这是一个词吗?)在IDAPython中查找并修补它们。您可以找到我的修补脚本 这里。
打完补丁后,WinMain代码如下所示。
图6:修补WinMain。
有点平淡无奇,但现在我们已经成功地对代码进行了反模糊处理,得到了一个有意义的 唿叫 指示 子模块_415110以及反编译代码中的正确返回语句!
反分析:垃圾代码
除了控制流混淆,PLAY还在代码中添加了随机移动指令,这些指令对程序的主要功能没有任何贡献。
图7、8:垃圾代码。
这使得反编译的代码看起来很混乱,而且修补所有这些错误代码并不简单,因为有效代码通常被填充在这些垃圾代码之间。跳过它们进行修补有时会破坏程序本身。
对此,我唯一的解决办法就是在分析时在心理上忽略它们。
反分析:API散列
与大多数现代勒索软件类似,PLAY通过API名称哈希来混淆其API调用。API解析函数接受目标哈希和DLL地址。
它遍历DLL的导出表以获取导出的名称。对于每个API名称,恶意软件调用 小于40F580并将0x4E986790添加到结果以形成最终散列。将此哈希与目标哈希进行比较,如果匹配,则返回API的地址。
图9:API散列
如下所示,散列函数包含许多唯一的常量,这使我们能够快速查找到它是xxHash32。这样,我们就知道完整的散列算法是xxHash32,种子为1,结果加到0x4E986790。
图10:xxHash32代码。
从这里开始,我开发了一个IDAPython脚本来自动解析恶意软件使用的所有API,您可以在这里找到它。
图11:解析API。
反分析:字符串加密
中最重要的字符串 播放都被编码在内存中。解码算法似乎不是太清楚,所以我只是动态通过这些我的方式。学校现在正忙得不可开交,所以我尽量避免分析东西。
图12:PLAY的字符串解密。
静态代码分析
命令行参数
PLAY 可以使用或不使用命令行参数运行。
下面是操作员可以提供的参数列表。
论证 |
-mc |
-d<驱动器路径> |
-ip<共享资源路径> |
-d |
图13:检查命令行参数。
加密初始化
在加密之前,PLAY初始化并检索加密算法提供程序。
首先,它调用BCryptOpenAlgorithmProvider加载并初始化CNG提供程序以生成随机数,并调用BCryptImportKeyPair导入其硬编码的RSA公钥。
图14:初始化&导入加密密钥。
接下来,恶意软件调用VirtualAlloc来分配缓冲区以存储128个用于加密文件的文件结构。该结构的大小为0x48字节,其内容如下所示。
struct play_file_struct
{
int struct_index;
char *filename;
int initialized_flag;
int padding1;
char *file_path;
int file_marker[2];
int chunk_count;
int chaining_mode_flag;
DWORD large_file_flag;
HANDLE AES_provider_handle;
HANDLE bcrypt_RNG_provider;
HANDLE RSA_pub_key_handle;
HANDLE file_handle;
LARGE_INTEGER file_size;
DWORD file_data_buffer;
DWORD padding2;
};
字段 |
结构索引 |
档名 |
初始化标志 |
文件路径 |
文件标记 |
块计数 |
链接模式标志 |
大文件标志 |
AES提供程序句柄 |
b加密RNG提供程序 |
RSA发布密钥句柄 |
文件句柄 |
文件大小 |
文件数据缓冲区 |
PLAY 迭代这个全局结构列表并填充每个结构的字段。首先,它将结构体中的加密文件标记设置为以下硬编码值,这些值稍后将写入每个加密文件的末尾。
图15:加密文件标记。
然后,恶意软件将RNG和AES提供程序句柄以及RSA公钥句柄设置为该结构。这些将在以后用于生成随机AES密钥和IV来加密文件。
图16:加密文件标记。
检查现有驱动器
在遍历所有驱动器以进行加密之前, 播放 通过调用枚举受攻击系统上的所有卷 查找第一卷W 以及 查找下一卷W。如果卷不是CD-ROM驱动器或RAM磁盘,则恶意软件将调用 获取卷名称W的卷路径名称检索指定卷的驱动器号和已装入文件夹路径的列表。
如果此列表为空,这意味着卷未装入任何文件夹,PLAY将调用GetDiskFreeSpaceExW来检查卷的可用空间是否大于0x40000000字节。如果是,恶意软件将调用SetVolumeMountPointW尝试将卷装载到驱动器路径。
图17:枚举卷。
对于要装入的每个卷,PLAY将遍历所有字符以查找驱动器名称,它可以调用SetVolumeMountPointW将卷装入到该驱动器名称。
图18:设置卷的挂接点。
使用相同的技巧迭代所有可能的驱动器名称, 播放 唿叫 获取驱动器类型W检查每个驱动器的类型。
它避免了加密光驱或RAM磁盘。如果是远程驱动器,恶意软件将调用WNetGetUniversalNameW来检索网络驱动器的通用名称。
图19:处理网络驱动器。
要加密的最终驱动器路径设置为网络驱动器的通用名称或连接名称,具体取决于存在哪一个。
图20:检索网络驱动器名称。
如果驱动器是常规驱动器,则其名称保持不变。每个有效驱动器的名称都添加到要遍历和加密的驱动器名称列表中。
递归遍历
为了开始横向驱动, 播放 遍历上面的驱动器名称列表并使用以下内容生成线程 创建线程遍历系统上的每个驱动器。
图21:生成线程以遍历驱动器。
在处理驱动器之前,恶意软件会提取以下勒索信内容,然后将其放入驱动器文件夹。这是唯一的地方,赎金说明是下降,而不是在每个文件夹像其他勒索软件。
PLAY
teilightomemaucd@gmx.com
图22、23:在驱动器中放置勒索信。
要开始枚举,恶意软件调用FindFirstFileW和FindNextFileW来枚举子文件夹和文件。它专门检查以避免处理当前和父目录路径“”。和 “...”。
图24:枚举文件。
如果遇到的文件是目录,恶意软件会进行检查以避免加密“Windows”目录。然后,它将子目录的名称连接到当前文件查找路径,并通过调用遍历函数递归地遍历子目录。
图25:递归遍历子目录。
如果遇到的文件是一个普通文件,恶意软件会检查其名称和大小,以查看它是否可以加密。
图26:检查文件。
如果其名称/扩展名在下面的列表中,或者其大小小于6,PLAY将避免对其进行加密。
.exe, .dll, .lnk, .sys, readme.txt, bootmgr, .msi, .PLAY, ReadMe.txt
图27:检查文件&扩展名。
PLAY 还执行附加检查,查看文件扩展名是否是典型的大文件扩展名,以便稍后确定其加密类型。如果文件的扩展名在下面的列表中,则该文件被归类为大文件。
mdf, ndf, ldf, frm
填充文件结构
对于每个要加密的文件,PLAY首先用关于该文件的适当数据填充文件结构。
首先,它开始遍历全局文件结构列表,以检查是否有可用的结构来处理文件。
图28:检查可用的文件结构。
如果全局列表中没有可用的结构,PLAY调用Sleep使线程休眠并重新检查,直到找到一个为止。
一旦找到该结构,恶意软件就将其initialized_flag字段设置为1,并将filename字段设置为目标文件名。它还填充其他字段,如文件大小、大文件标志和文件句柄。
图29、30:填充文件结构以加密文件。
子线程加密
在填充了特定文件的文件结构之后,PLAY会生成一个线程来开始加密文件。
如果文件未被分类为大文件,则恶意软件将根据文件大小计算需要加密的区块数。如果文件大小小于或等于0x 3fffffff字节,则加密组块的数量为2;如果文件大小小于或等于0x 27 fffffff字节且大于0x 3fffffff字节,则加密组块的数量为3;如果文件大小等于0x 280000000,则加密组块的数量为0。如果文件大小大于0x 280000000字节,则加密组块的数目是5。
图32:计算加密区块。
默认链接模式设置为AES-GCM。但是,如果文件大小大于加密大小的4025倍(即块大小0x 100000乘以块计数),则链接模式将设置为AES-CBC。
这是因为与AES-CBC相比,AES-GCM的性能最差。根据这篇文章,AES-GCM是一个比AES-CBC更安全的密码,因为AES-CBC通过XOR'ing(异或)将每个块与前一个块进行运算,并且不能并行写入。由于涉及需要串行加密的复杂数学运算,这会影响性能。
对于文件加密,PLAY现在引入了一个新的结构,表示写入每个加密文件的文件页脚内容。
我花了很长时间才完全理解和解析这个结构的字段,这提醒了我,我可能只是在恶意软件分析现在撕裂。
struct file_footer_struct
{
byte footer_marker_head[16];
WORD last_chunk_size;
WORD skip_chunks;
WORD large_file_flag;
WORD small_file_flag;
DWORD default_chunk_size;
DWORD footer_marker_tail;
QWORD encrypted_chunk_count;
byte encrypted_symmetric_key[1024];
};
字段 |
页脚标记页眉 |
最后块大小 |
跳过_块 |
大文件标志 |
小文件标志 |
块计数 |
默认块大小 |
页脚标记尾部 |
加密块计数 |
加密对称密钥 |
首先,PLAY读取文件末尾的0x428字节以检查文件页脚。如果文件大小小于0x428字节,则可以保证文件不会被加密,因此恶意软件会立即对其进行加密。
如果成功读取了最后一个0x428字节,则恶意软件将检查 xxHash32页脚标记头的散列等于页脚标记尾。如果是,则确认文件页脚有效,并且文件已经加密。
如果不是这种情况,PLAY会检查页脚标记头中的每个DWORD,并将其与文件结构中的硬编码值进行比较。这将检查文件页脚是否未加密,文件页脚是否已写入但尚未加密,或者文件是否已加密。
图33、34:检查文件页脚的加密状态。
文件加密
要从头开始加密文件,PLAY首先生成一个AES密钥来加密文件。
它调用BCryptGenRandom生成一个随机的0x20字节缓冲区。根据文件结构中指定的链接模式,恶意软件调用BCryptSetProperty为其AES提供程序句柄正确设置链接。
接下来, B加密生成对称密钥在随机生成的0x20字节缓冲区中调用,以生成AES密钥句柄。
图35、36、37:正在生成AES密钥句柄。
接下来,为了将AES密钥存储在文件页脚结构体中,PLAY调用BCryptExportKey将AES密钥导出到0x230字节的密钥blob中。它还调用BCryptGenRandom来随机生成0x10字节的IV,并将其附加到密钥blob之后。
图38、39:正在导出AES密钥块&IV。
然后,它调用BCryptEncrypt以使用RSA公钥句柄加密导出的密钥blob和IV,并将加密的输出写入0x400字节的缓冲区。然后将此缓冲区复制到文件页脚结构的encrypted_symmetric_key字段。
图40:使用RSA公钥加密AES密钥块。
PLAY 然后用文件结构中的现有信息填充文件页脚的其他字段,如footer_marker_head、footer_marker_tail、small_file_flag和large_file_flag。默认块大小也设置为0x100000字节。
图41:填充文件页脚结构。
文件页脚完全填充后,恶意软件调用SetFilePointerEx将文件指针移动到文件末尾,并调用WriteFile将结构写入其中。
图42:将文件页脚结构写到文件末尾.
如果文件大小大于0x500000字节,PLAY只加密文件中的第一个和最后一个块。
图43、44:加密大文件的第一个&最后一个块。
加密函数包括 读取文件 调用以读取文件结构中缓冲区中的块数据, B加密加密调用以使用AES密钥句柄和生成的IV加密文件。加密完成后,恶意软件调用 写入文件将加密输出写入文件以及在文件页脚中加密的块的索引。这可能用于在发生损坏或中断的情况下跟踪有多少块已被加密。
图45、46、47:数据加密功能。
如果文件大小小于默认块大小0x100000字节,恶意软件将加密整个文件。
图48:加密整个小文件。
如果文件大小介于0x100000和0x500000之间,则恶意软件会将其加密为0x100000字节的块,直到文件末尾。
图49:加密中型文件。
最后,文件加密后,恶意软件将其扩展名更改为 播放 通过调用 移动文件W。
图50:附加加密扩展名。
代码中有一个小错误,即无论加密是否成功,由于文件加密函数的返回值,它总是更改文件的扩展名。
图51:加密中型文件。
翻译自网络
原文始发于微信公众号(闲聊知识铺):PLAY勒索软件完整分析