1.样本信息
⽂件名 | Fast.exe |
---|---|
SHA256 | 3c95bd8e14f6aa92e94ec3318d23a8cc34192259 |
MD5 | 28c6c0b4f54912ec73c9bfeb3f2a8f07 |
运行平台 | Windows |
2.感染迹象
2.1 文件结构分析
整体文件大小为200+k,把冗余数据去掉,发现仍然可以运行,大小变为56k。与phobos家族的标准一致。
2.1.1 勒索信
2.1.2 勒索壁纸
2.1.3 密钥
加密aes256 key(此key用于加密文件)的rsa公钥为
[0xB7,0x0AA,0x5B,0x0CB,0x0EC,0x0B8,0x1C,0x0E8,0x0B0,0x0EE,0x5,0x0D9,0x8E,0x92,0x67,0x8F,0x0B0,0x11,0x0D9,0x0E0,0x42,0x50,0x0C,0x2A,0x0B1,0x65,0x30,0x23,0x8C,0x0D7,0x0E7,0x6F,0x60,0x0,0x0A4,0x0FA,0x77,0x0DE,0x0C6,0x11,0x6B,0x0DF,0x0E5,0x2D,0x94,0x0E4,0x67,0x0D8,0x1E,0x74,0x0A3,0x0F6,0x53,0x0AB,0x0FE,0x0AA,0x0E9,0x0CC,0x2A,0x0BF,0x5,0x4D,0x7E,0x2A,0x18,0x9F,0x34,0x87,0x9,0x99,0x0AB,0x46,0x7D,0x0D8,0x66,0x4D,0x9,0x55,0x2D,0x0C9,0x8E,0x6C,0x90,0x75,0x0B5,0x0ED,0x6C,0x46,0x77,0x55,0x48,0x38,0x70,0x0CC,0x0DC,0x0F3,0x12,0x1B,0x88,0x44,0x24,0x0C,0x0E9,0x6,0x0A0,0x0C0,0x68,0x33,0x0C,0x95,0x0DB,0x0D,0x5D,0x0EC,0x0D8,0x15,0x92,0x0D1,0x3D,0x0D4,0x44,0x0FA,0x2E,0x73,0x1A,0x49,0x0C1,0x0BD]
写入文件的配置中的iv(变化的)后面固定的16字
[0xD,0x0DB,0x95,0x0C,0x33,0x68,0x0C0,0x0A0,0x6,0x0E9,0x0C,0x24,0x44,0x88,0x1B,0x12]
加密字符串的aes128 key
[0xC3,0x70,0x0D7,0x0CC,0x94,0x27,0x77,0x70,0x0DF,0x2,0x0DF,0x0CE,0x0E2,0x0D5,0x32,0x14]
2.1.4 加密文件结构
文件被加密并重命名如下格式1.png.id[8E1EA49A-3524].[sqlback@memeware.net].2700
被加密的文件的后8 Byte为固定值。
2.1.5 加密文件样式
模拟客户中招后环境
加密后
解密后
2.2 软件运行分析
2.2.1 函数sub_4054BF(内存初始化)
文件加密部分可以从sub_4054BF函数开始分析,此函数的关键部分如下
if ( ntfs_get_names(newfilename, 0x7FFFu, 2) )
{
GenerateRandomBytes_SHA224_256Pad(16, key);
v6 = *lpThreadParameter;
if ( !sub_40868F() )
break;
sub_408EBE(lpMem, v3, key, newfilename, *(*v6 + 9));
}
BOOL __usercall sub_40868F@<eax>(int a1@<esi>)
{
int v1; // eax
v1 = CRC32_ProcessInteger(0, a1, 32);
return *(a1 + 160) == CRC32_ProcessInteger(v1, (a1 + 32), 128);
}
-
首先调用函数ntfs_get_names生成了新的文件名,存放在newfilename中;
-
调用函数GenerateRandomBytes_SHA224_256Pad随机生成加密此文件的密钥,命名此数据为filekey;
-
调用函数sub_40868F,通过计算CRC32值并比较,判断给定块中的数据是否与文件的第一个块相匹配,以此来确定该块是否是文件的开头;
-
最后调用函数sub_408EBE进行加密的其他步骤,下文将函数sub_408EBE重命名为encode;
-
函数ntfs_get_names仅实现了字符串拼接功能,ID的生成逻辑id[8E1EA49A-3354]不在此函数中,id是通过sub_4054BF的参数传递到此函数中。
2.2.2 ntfs_get_names函数(新文件名字)
ida反编译时参数缺失
mov esi, [ebp+lpThreadParameter]
...
mov eax, [esi]
mov eax, [eax]
push dword ptr [eax+8]
push [esp+34h+lpMem]
push 2
push 7FFFh
push [esp+40h+newfilename]
call ntfs_get_names
其中push dword ptr [eax+8]即为.id[8E1EA49A-3524].[sqlback@memeware.net].2700
此函数仅实现字符串拼接功能。
2.2.3 sub_408EBE函数(encode)
int __usercall sub_408EBE@<eax>(const WCHAR *a1@<edi>, _DWORD *a2, int a3, const WCHAR *lpFileName, char a5)
{
int FileW; // ebx
DWORD FileAttributesW; // eax
int v7; // eax
LARGE_INTEGER FileSize; // [esp+8h] [ebp-20h] BYREF
LARGE_INTEGER v10; // [esp+10h] [ebp-18h]
DWORD dwFileAttributes; // [esp+1Ch] [ebp-Ch]
DWORD v12; // [esp+20h] [ebp-8h]
int v13; // [esp+24h] [ebp-4h]
v13 = 0;
FileW = (int)CreateFileW(a1, 0x80000000, 7u, 0, 3u, 0, 0);
if ( FileW == -1 )
return v13;
if ( !GetFileSizeEx((HANDLE)FileW, &FileSize) )
goto LABEL_15;
CloseHandle((HANDLE)FileW);
FileW = -1;
v10 = FileSize;
if ( FileSize.QuadPart )
{
FileAttributesW = GetFileAttributesW(a1);
dwFileAttributes = FileAttributesW;
if ( FileAttributesW != -1 )
{
v12 = FileAttributesW & 1;
if ( (FileAttributesW & 1) != 0 )
SetFileAttributesW(a1, FileAttributesW & 0xFFFFFFFE);
v7 = (a5 & 1) != 0 || v10.QuadPart < 0x180000ui64
? sub_408782(a2, a3, a1, lpFileName, a5)
: sub_408C42(a3, a1, lpFileName, a5);
v13 = v7;
if ( v12 )
{
if ( v7 )
{
SetFileAttributesW(lpFileName, dwFileAttributes);
return v13;
}
SetFileAttributesW(a1, dwFileAttributes);
LABEL_15:
if ( FileW != -1 )
CloseHandle((HANDLE)FileW);
}
}
}
return v13;
}
此函数大致逻辑为:
-
函数尝试使用CreateFileW打开文件,并检查操作是否成功。如果不成功,则返回;
-
然后使用GetFileSizeEx获取文件大小,若文件为空,则关闭句柄并返回;
-
使用GetFileAttributesW检索文件属性,检查文件属性中的只读标志,如果文件是只读的,接下来的代码会使用SetFileAttributesW函数将只读标志清除;
-
根据文件大小是否小于 0x180000,调用sub_408782或sub_408C42函数;
-
此代码通过加密现有文件来创建一个新文件。该函数接受现有文件的文件名、密码和要创建的新文件的文件名称。该函数分块读取现有文件,对每个块进行加密,并将加密的块写入新文件。加密是通过使用密码和文件属性计算哈希值来完成的。哈希值用于对文件数据进行块加密。该函数还将新文件的文件属性设置为与现有文件匹配;
-
sub_408782与sub_408C42函数的区别为,一个为全加密,一个为部分加密。
2.2.4 sub_408782函数(全加密)
if ( hObject != (HANDLE)-1 && !sub_40669B(v16, a1->mainkey, a2) )
{
while ( ReadFile(hFile, (LPVOID)a1->buffer, nNumberOfBytesToRead, &nNumberOfBytesToWrite, 0) )
{
if ( nNumberOfBytesToWrite < nNumberOfBytesToRead )
{
dwFlagsAndAttributes = 16 - (nNumberOfBytesToWrite & 0xF);
sub_408FA9((char *)(nNumberOfBytesToWrite + a1->buffer), 0, dwFlagsAndAttributes);
nNumberOfBytesToWrite += dwFlagsAndAttributes;
}
if ( !sub_406432((int)v16, a1->buffer, a1->buffer)
|| !WriteFile(hObject, (LPCVOID)a1->buffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0)
|| NumberOfBytesWritten != nNumberOfBytesToWrite )
{
break;
}
if ( nNumberOfBytesToWrite < nNumberOfBytesToRead )
{
v15 = v18;
*buffer = 0;
v14 = v19;
buffer[1] = 2;
v13 = (int)v20;
buffer[2] = -257466862;
buffer[6] = 32;
sub_408FD7(v13, v14, v15);
sub_408FD7(v9, (int)a1->zero, 20);
sub_408FD7(v9 + 20, a2, 16);
sub_408FD7(v9 + 40, a1->mainkey + 32, 128);
sub_408FD7(v9 + 172, (int)a1->ident, 6);
v11 = v22;
*(_DWORD *)(v9 + 36) = dwFlagsAndAttributes;
*(_DWORD *)(v9 + 168) = v11 + 178;
if ( !sub_40669B(v17, a1->mainkey, a2) )
{
if ( sub_406432((int)v17, a1->buffer, a1->buffer) )
{
sub_408FA9(v17, 0, 0x128u);
if ( WriteFile(hObject, (LPCVOID)a1->buffer, *(_DWORD *)(v9 + 168), &NumberOfBytesWritten, 0) )
{
if ( NumberOfBytesWritten == *(_DWORD *)(v9 + 168) )
{
if ( (a5 & 4) != 0 )
sub_4086B7(hFile);
if ( (a5 & 2) != 0 )
{
FlushFileBuffers(hFile);
FlushFileBuffers(hObject);
}
v23 = 1;
}
}
}
}
break;
}
}
sub_408FA9(v16, 0, 0x128u);
}
其中函数sub_406432为加密函数,v16与v17为加密时需要使用的数据,包括密钥与初始IV等,通过函数sub_40669B生成加密需要使用的数据,加密后的数据写入新文件,最后会删除原文件。
在函数sub_C6669B中的a1为重要结构体,包括加密缓冲区与加密需要的信息,分析结构如下:
struct a1{
Byte* mainkey;//指向164 Byte的地址
Byte zero[20];//暂未发现作用
Byte ident[8];//存放机器的唯一标识
Byte* buffer;//指向一块1114370h大小的内存,用作加密时的缓冲区
int buffer_size=1114370;//缓冲区大小
}
加密完文件内容后会加密文件的基础信息,包括文件名等信息,其中,a2也会被加密后放入文件末尾(a2为之前生成的filekey)。
在此处下断点:
被加密的数据如下
0348F020 00 00 00 00 02 00 00 00 12 5E A7 F0 79 6F A5 46 .........^.....F
0348F030 37 80 23 35 7A 83 51 3B 20 00 00 00 9E 3F 4F 64 7.#5z.Q; ....?Od
0348F040 43 00 55 00 41 00 73 00 73 00 69 00 73 00 74 00 C.U.A.s.s.i.s.t.
0348F050 61 00 6E 00 74 00 54 00 61 00 73 00 6B 00 2E 00 a.n.t.T.a.s.k...
0348F060 78 00 6D 00 6C 00 00 00 F8 FC 14 D4 F7 93 92 4A x.m.l..........J
其中包括当前文件的文件名CUAssistantTask.xml
查看此函数sub_C6669B的其余变量可得到被加密的文件路径为C:\Program Files\CUAssistant\CUAssistantTask.xml。
函数sub_408C42的加密流程与函数sub_408782一致,区别是sub_408C42是部分加密,2个函数都会调用同一个加密核心函数sub_406432。
2.2.5 sub_406432函数(加密核心函数)
int aes_crypt_cbc( aes_context *ctx,
int mode,
const unsigned char iv[16],
const unsigned char *input,
unsigned char *output,
unsigned int length )
{
int i;
unsigned char temp[16];
if( length % 16 )
return( POLARSSL_ERR_AES_INVALID_INPUT_LENGTH );
if( mode == AES_DECRYPT )
{
while( length > 0 )
{
memcpy( temp, input, 16 );
aes_crypt_ecb( ctx, AES_DECRYPT, input, output );
for( i = 0; i < 16; i++ )
output[i] = (unsigned char)( output[i] ^ iv[i] );
memcpy( iv, temp, 16 );
input += 16;
output += 16;
length -= 16;
}
}
else
{
while( length > 0 )
{
for( i = 0; i < 16; i++ )
output[i] = (unsigned char)( input[i] ^ iv[i] );
aes_crypt_ecb( ctx, AES_ENCRYPT, output, output );
memcpy( iv, output, 16 );
input += 16;
output += 16;
length -= 16;
}
}
return( 0 );
}
2.2.6 killsystemprocesses函数(杀死不利进程)
2.2.7 eternal_blue函数(永恒之蓝)
使用永恒之蓝漏洞,通过445端口横向感染内网其他主机,达到利用的目的:
尝试连接其他主机的445端口:
复制令牌:
2.2.8 self_copy函数(自我复制)
自我复制并隐藏.目录如下
C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\StartMenu\Programs\Startup
C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
C:\Users\Administrator\AppData\Local
写入注册表启动项
Software\Microsoft\Windows\CurrentVersion\Run
2.2.9 write_black_page函数(线程执行函数)
写入黑页.通过执行cmd,将所有用户桌面改为全黑色,并生成勒索页面和勒索信息,地址等等。
3.病毒分析概览
根据对样本的深入逆向工程分析,得出该勒索病毒分析结果概览:
主要功能
自我复制、提升权限、开机自启、利用永恒之蓝漏洞进行传播、文件加密、生成勒索提示页(黑页)、创建任务完成监视线程
安全特征
CRC32校验:用于自校验
字符串混淆
采用6347模式,输入49作为ID号解密字符串
AES ECB加密
使用明确指定的Key
子进程操作
创建子进程关闭防火墙
系统操作
修改注册表、添加自启动项目、获取硬盘信息,并进行SHA-256尝试发送HTTP POST请求(未指定参数)
网络与传播
内网扫描,寻找开放445端口的主机利用永恒之蓝漏洞进行攻击,创建攻击线程
进程操作
枚举并终止特定进程,如一些系统应用、数据库服务等