吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7393|回复: 120
上一主题 下一主题
收起左侧

[PC样本分析] 独家揭秘LIVE勒索病毒家族之1.0(全版本可解密)

  [复制链接]
跳转到指定楼层
楼主
Solarsec 发表于 2024-6-5 11:30 回帖奖励
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 Solarsec 于 2024-6-7 09:39 编辑

0.前言

LIVE勒索病毒家族最早被曝光是在23年12月份,360的论坛发布了该家族的被加密样本,其加密特征为文件名后直接添加LIVE后缀,这时候还是该家族勒索病毒的1.0版本

https://bbs.360.cn/thread-16111372-1-1.html

https://mp.weixin.qq.com/s/XV0x10YV-Wrs1ZI6tNHjLA

随后的24年1月份,奇安信的威胁情报中心揭秘了该团队利用国内某防泄密软件的漏洞进行渗透并且投放勒索病毒的案例,这时候的LIVE家族已经进化到了2.0版本,因为此时的加密特征为把原文件名修改为一串数字,再添加LIVE后缀

再在今年的2月8日,火绒安全也发布了一篇该家族利用某某通电子文档安全管理系统的文件上传漏洞进行入侵并且投放LIVE2.0勒索病毒的案例,并且附上了解密的demo代码。

而我们团队在去年接触到LIVE家族至今,总共捕获到了该家族的三个版本加密器,根据其加密后文件特征我们命令为1.0、1.5、2.0版本,由于其加密器三个版本本身都存在一些bug,导致其家族自己提供的解密器也无法坐到全部完整的恢复文件,因此我们也根据其加密流程和bug特征,自己制作了能完整恢复的解密补丁工具,在接下来对LIVE家族的分析文章中也都会一一发布。

通过对该LIVE勒索病毒家族的详细分析,无论是其入侵行为还是加密工具的溯源,我们可以清晰地认识到,这是一支新兴的勒索团队。尽管他们拥有国内的攻防经验,但在勒索领域尚显稚嫩。与我们过去所了解的成熟勒索病毒组织相比,他们展现出了不同的攻击模式:

  1. 技术手段方面,与依赖弱口令爆破手段的Phobos、Mallox等老牌勒索病毒家族不同,LIVE团队借助国内最新的1day漏洞,甚至在某次攻击中,利用了一家安全厂商的安全管控平台漏洞,直接下发加密器对设备进行加密,令原本用于防护的安全设备变成了勒索的利器。
  2. 勒索软件制作方面,相较于老牌的Phobos家族,LIVE团队的制作手法较为粗糙,其加密的文件连自己的解密器都无法解开。然而,他们展现出的努力值得关注,短短三个月内就迭代更新了三个版本,预示着下一版可能将更难以破解。
  3. 勒索金额方面,LIVE团队所要求的赎金金额远高于常见的勒索病毒家族。已知的几例支付赎金的受害者,平均赎金约为10万人民币,这一高额赎金的背后是他们深入渗透、加密核心业务服务器的结果,且他们主要针对医疗、能源和大型集团企业等行业。

LIVE勒索病毒组织的出现预示着国内网络安全面临新的挑战,标志着国内勒索病毒攻击的进化进入了2.0时代。与以往主要依赖弱口令爆破和1day漏洞入侵的1.0版本相比,这一新时代的攻击采用了更为复杂的技术手段,如高级持续性威胁(APT)攻击、社会工程学以及零日漏洞利用,展现了更精细化的目标选择和攻击策略,专门针对高价值企业和组织。

本篇文章深入分析了LIVE1.0勒索病毒,我们将继续发布关于LIVE1.5和LIVE2.0勒索病毒的深度分析,并分享相应的解密工具。我们的团队专注于应急响应与勒索病毒研究。如果您或您的客户不幸成为勒索病毒的攻击目标,请随时与我们联系并提交受影响的勒索病毒样本,让我们携手合作,共同维护网络安全防线。

1.背景

近期收到大量客户反馈遭到LIVE勒索病毒攻击,该病毒于2023年12月左右开始传播,并且存在多个版本。该中招客户为某医疗单位,团队工程师通过远程连接进行分析后提取到了勒索病毒的源头。

2.溯源分析

以下为模拟客户系统的部分内网环境遭受该LIVE家族攻击的部分环境:

2.1 排查文件

发现服务器上的文件在2024/2/27 15:06被加密,排查该时段有哪些新增文件。

最终发现在该时段新建了名为systime.exe的文件。

2.2 排查日志

排查相关日志,存在大量smb爆破行为并最终在2024/2/27 15:03:54成功爆破。


并在2024/2/27 15:04:05 登录远程桌面。

实际情况攻击者是使用IP-Guard漏洞进行入侵,上述环境为模拟横向攻击的溯源。

LIVE勒索病毒利用IP-Guard漏洞进行攻击的相关文章:https://mp.weixin.qq.com/s/XV0x10YV-Wrs1ZI6tNHjLA

3.恶意文件基础信息

3.1 加密器基本信息

文件名 admin.exe
编译器 Microsoft Visual C/C++(19.33.31630)[C++]
大小 3.08MB
操作系统 Windows
架构 386
模式 32 位
类型 EXEC
字节序 LE
MD5 a4b6148a76d8190e47679242c033f598
SHA1 f20f794b0066bf4868256ac428eeac8ba2d4d837
SHA256 16d581d781495d16058c43a1e517c0ab7ca3ea8638c9124a4abde72822f8b40f

3.2 程序配置信息

MinDate: int64 1672531200
MaxDate: int64 1707955200
AppendedExtension: 'LIVE'
NoteFilename: 'FILE RECOVERY_ID_170605242653.txt'
DeleteSystemRestorePoints: false
StopServices: true
ServicesToStop: ['MSSQLFDLauncher', 'MSSQLSERVER', 'SQLSERVERAGENT', 'SQLBrowser', 'SQLTELEMETRY', 'SQLWriter', 'MSSQL', 'SQLAgent', 'MSSQLServerOLAPService']
StopProcesses: true
ProcessesToStop: ['postgresql', 'sqlbrowser', 'sqlwriter', 'sqlservr', 'sqlceip', 'SQLAGENT', 'sqlservr', 'pg_ctl', 'postgres']
DropNoteInSpecificDirectories: true
DirectoriesToDropNoteIn: ['C:\Users\Public\Documents', 'C:\Users\Public\Desktop']
IncludeFiles: false
FileSet: ['C:\bootmgr', 'FILE RECOVERY_ID_170605242653.txt']
IncludeDirectories: false
DirectorySet: ['C:\Program Files', 'C:\Program Files (x86)', 'C:\ProgramData', 'C:\Windows', 'C:\msys64', 'C:\Users\All Users']
IncludeExtensions: false
NoneSet: ['exe', 'dll', 'ink', 'ini', 'lnk', 'ico', 'sys', 'desktop', 'mui', 'live']
FastSet: @as []
IntermittentSet: @as []
FullSet: ['txt', 'log', 'doc', 'docx', 'msg', 'rtf', 'dat', 'ppt', 'pptx', 'xml', 'csv', 'xls', 'xlsx']
EncryptHiddenFiles: true
EncryptHiddenDirectories: false
BufferSize: 8192
Percent: 0.10000000000000001
Segmentation: 512.0
ChangeWallpaper: true
WallpaperName: '170605242653.jpg'

3.3 勒索信

Hello....Your file has been encrypted and cannot be used..When you see this letter, your privacy data has been backed up by us. If you do not handle it, we will publish your privacy data after the 7th.....Don't try to change or restore the file yourself, which will destroy them..If necessary, you can decrypt a test file for free. Free test decryption is only available for files less than 3MB in size.....To restore files, you need a decryption tool. Please contact us by email...Please add the file name of this document to the email and send it to me. ..¡¾FILE RECOVERY_ID xxxxxx¡¿..I will tell you the amount you need to pay. After the payment is completed, we will make the decryption tool and send it to you.....Customer service mailbox:..locked@onionmail.org..Spare mailbox: (use this mailbox after no reply in 24 hours)..liveteam@onionmail.org....You can also contact us through intermediary agencies (such as data recovery companies)....If you refuse to pay, you will be attacked constantly. Your privacy -sensitive data will also be announced on Internet.....!! We are a team that pays attention to credibility, so you can pay safely and restore data.

3.4 其他

勒索壁纸

密钥

4.加密后文件分析

4.1 加密文件结构

4.2 加密文件样式

加密文件名特征:文件名+.LIVE

加密文件目录样式**:**

勒索信+加密文件

解密后**:**

5.软件运行分析

初步来看加密器并未使用保护措施,并且也没有移除符号表,入口点为main。

5.1 加密器简要运行逻辑框图

5.2 加密器逆向分析

5.2.1 Main函数

从Main函数中可知主要逻辑在progarm_main中。

5.2.2 Progarm_main函数

首先程序会获取运行中的命令行,并且根据其参数内容进行其他功能的使用。

5.2.3 program_print_help函数(打印使用说明)

具体实现在函数program_print_help函数中有所说明,比如:指定加密的目录、删除系统还原点、停止服务等,使用方法是--参数 true/false

Progarm_main函数逻辑分析,最开始仅仅只检测cmd命令行的第一个参数是否为--self,如果需要将第二个参数即程序本身路径作为cmdline的第一个参数,以防止后续读取自身文件时,使用第一个参数不是文件本身路径而是--self参数,导致后续读取密钥与程序配置内容失败。

接下来进行读取系统时间,以用作后续的日期判断,来对程序状态进行设置。

再往下是初始化,初始化主要分为两部分:密钥初始化和程序初始化。这里就再次说一下文件的结构组成:

5.2.4 encryption_Initialize函数(密钥初始化)

密钥初始化核心为两部分,一个是从程序文件本身读取KEY,另一个是从将硬编码的IV赋值。

读取KEY:


赋值IV:

整体的密钥初始化就结束了。

5.2.5 program_Initialize函数(程序初始化)

其功能是读取勒索信前面的那部分程序配置信息,并且将其转换为key:value的形式,以便于后续功能的使用。

读取配置项**:**

转化为key:value形式**:**

文件内配置信息**:**

progarm_main函数的余下分析,接下来是一个死循环,主要针对于文件本身的第一个参数以后的其他参数进行遍历,并且判断其功能是否开启等操作,具体功能介绍可见program_print_help函数。

参数遍历完毕后,继续针对时间的比较,从文件配置信息中可以得到一个最大与最小的日期信息,是个Unix的时间戳,转换后可以得到具体时间,作用是来判断软件是否在2023-01-01 08:00:00 ~ 2024-02-15 08:00:00 之间,不在的话就不能够运行。

Mindate: 2023-01-01 08:00:00
Maxdate: 2024-02-15 08:00:00

逻辑实现**:**

从配置文件中读取勒索信的文件名称,并且从文件名中以‘_’字符进行分割包含的ID,给到progarms_id变量以便后续的使用,最后获取更换壁纸功能的状态,如果是true就会进入到progarm_ChangeWallpaper函数进行勒索壁纸的更换。

配置中的变量内容:

'NoteFilename': <'FILE RECOVERY_ID_170605242653.txt'> //勒索信文件名称
'ChangeWallpaper': <true> //壁纸更换功能状态
'WallpaperName': <'170605242653.jpg'> //勒索壁纸文件名称

代码实现**:**

5.2.6 progarm_ChangeWallpaper函数(更改壁纸)

和初始化一样,先移动FILE指针到程序文件中的勒索壁纸开头,然后读入到临时变量中。

获取程序文件的目录,进行勒索壁纸的保存。

最后写入一个更换壁纸的Powershell脚本wallpaper.ps1,并且使用system函数执行,完成更换壁纸的操作。

更换壁纸powershell脚本wallpaper.ps1内容:

Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class Wallpaper {
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
}
"@; $wallpaperPath = "C:\新建文件夹\170605242653.jpg";
 $SPI_SETDESKWALLPAPER = 0x0014;
 $UpdateIniFile = 0x01; 
 $SendChange = 0x02;
[Wallpaper]::SystemParametersInfo($SPI_SETDESKWALLPAPER, 0, $wallpaperPath, $UpdateIniFile -bor $SendChange)"@; 
$wallpaperPath = "C:\新建文件夹\170605242653.jpg"; 
$SPI_SETDESKWALLPAPER

勒索壁纸:

获取勒索信的内容,其逻辑跟初始化和获取壁纸图片方法一样,将勒索信内容赋值到info_context变量中,以便后续的使用。

对IncludeExtensions配置项的状态进行获取,来判断是否启动包含扩展的功能,这里软件中默认是False,所以他将获取NoneSet的配置项内容,即不加密的文件后缀。

IncludeExtensions配置项的状态也决定程序使用那个加密函数,这里加密函数有两个:一个是_lambda4program_encrypt;另外一个是_lambda5program_encrypt,这里的使用_lambda5program_encrypt加密函数。


程序为了保证写入勒索信和对文件进行加密顺利,将利用system函数来执行cmd命令对系统进行恢复选项的禁用并且对操作系统的引导状态策略设置为忽略所有故障。

之后就是对程序指定的服务和进程的停止操作。

进程

服务


在对指定的服务和进程进行关闭后,将开始最为关键的对文件操作的流程了。

首先是写入勒索信


因为DropNoteInSpecificDirectories配置项开启的缘故,所以勒索信会在DirectoriesToDropNoteIn配置项内容下的两个目录中写入名为NoteFilename配置项内容的勒索信文件。

DropNoteInSpecificDirectories内容为:

'DropNoteInSpecificDirectories': <true>
DirectoriesToDropNoteIn内容为:
‘DirectoriesToDropNoteIn': <['C:\\Users\\Public\\Documents', 'C:\\Users\\Public\\Desktop']>

NoteFilename内容为:

 'NoteFilename': <'FILE RECOVERY_ID_170605242653.txt'>

勒索信写入完毕后,开始对磁盘文件的遍历与加密。

首先程序作者考虑到了文件数量庞大,需要采用多线程的方式来进行加密比较有效率,甚至于在加密算法上也做了处理。

先是获取处理器数量,开启线程池,创建多线程对象,并且对其错误进行处理,其中启动函数为_lambda6gfunc函数。


这里判断如果没有错误发生的情况,会再次获取IncludeDirectories配置项内容,再次确认是否包含指定目录功能是否开启。

这里程序因为并未开启该功能,所以直接跳到对所有磁盘遍历操作。

首先是对A-Z字符做了拷贝,以供后续作为磁盘的盘符。

接着对其进行遍历路径的拼接,然后是不断的迭代,类似树的DFS,具体由progarm_Iterate函数实现。

5.2.7 progarm_Iterate函数(遍历文件路径,过滤文件,加密文件)

其参数有五个,分别为:(文件路径,配置信息,勒索信内容,勒索信长度,多线程对象)

首先该函数会打开文件路径下的目录,并且获得该目录下的所有文件的同时进行路径拼接,之后对其路径所指文件或目录进行判断是文件还是目录。

打开目录:

获取该目录下的文件并做判断:

如果是普通文件的话,先对其中的隐藏文件进行判断,然后根据EncryptHiddenFiles配置项来决定是否加密隐藏文件,EncryptHiddenFiles内容为true,所以对隐藏文件也进行加密。

这里考虑到存在NAS的缘故,所有判断了‘.‘开头的文件。

当满足了上述条件后,将最后做一次文件过滤,判断其加密文件后缀是否在Noset配置项内容中,所有过程过程由program_Filter函数实现。

5.2.7.1 program_Filter函数(文件过滤)

其参数有七个,分别为(文件名称,IncludeFiles配置项内容,FileSet配置项内容,FileSet配置项内容长度,IncludeExtension配置项内容,IncludeExtension配置项内容长度)

首先是获取文件的后缀,并且转换为小写。

之后是判断该文件后缀是否在Noset配置项内容中,Noset配置项内容:['exe', 'dll', 'ink', 'ini', 'lnk', 'ico', 'sys', 'desktop', 'mui', 'live'],若是不在则通过,若是在则不通过。

最后会多加一个判断,判断其文件是否是勒索信文件。

接着progarms_Iterate函数的内容进行分析。

在完成了文件过滤后,如果该文件符合上述所有条件,那么就需要将其丢入到线程池中,进行最后的加密了,然后还会将勒索信文件可写标志设置成true。

勒索信标志设置:

若该文件不是普通文件,而是目录,则会进入到判断路径是否是一个目录。

若该文件路径是一个目录则进行过滤,看看是否在DirectorySet配置项内容中的目录。

DirectorySet配置项内容:['C:\Program Files', 'C:\Program Files (x86)', 'C:\ProgramData', 'C:\Windows', 'C:\msys64', 'C:\Users\All Users']。

如果不在的话,就继续向下遍历,如果在,则停止遍历。

最后将判断勒索信可写标识是否为true,若可以写将判断该目录下是否已存在勒索信文件,若不存在则写入。



所有的文件遍历过程就全部分析完毕,接下来将最后分析加密流程。

因为之前提到过线程池的创建,所采用的执行函数是_lambda6gfunc函数。

5.2.8 _lambda6gfunc函数(线程执行函数)

该函数的主要作用是对文件进行加密和重命名。

一切的实现则由task_data_encrypt_and_rename函数实现。

5.2.9 task_data_encrypt_and_rename函数(文件的加密和重命名)

该函数a1是一个结构体,这里暂时只恢复了两条一个是file_path加密文件路径和config配置信息,而加密则是进入到progarm_encrypt函数中进行。

5.2.10 progarm_encrypt函数(文件加密)

前文说过文件加密的progarm_encrypt函数的实现在程序中有两个:

  1. _lambda4program_encrypt函数
  2. _lambda5program_encrypt函数(此程序选择该函数作为加密函数)

5.2.10.1 _lambda4program_encrypt函数(文件加密)

该函数有三个参数,分别为:(文件路径,配置信息,程序加密错误信息)

大致的流程:

  1. 获取加密的文件后缀;

  1. 获取FastSet配置项,是个空字符数组,并且判断文件后缀是否在当中,若是不在则通过;

  1. 获取IntermittentSet配置项,是个空字符数组,并且判断文件后缀是否在当中,若在则通过,并且获取BufferSize配置项的大小,文件采用部分加密的方式;

  1. 若是不在IntermittentSet配置项的数组中,则采用全加密;
  2. 最后加密结束,返回到task_data_encrypt_and_rename函数进行重命名操作。

5.2.10.2 _lambda5program_encrypt函数(文件加密)

该函数有三个参数,分别为:(文件路径,配置信息,程序加密错误信息)

大致的流程:

  1. 获取加密的文件后缀;

  1. 获取IntermittentSet配置项,是个空字符数组,并且判断文件后缀是否在当中,若是不在则通过;

  1. 获取FullSet配置项,为文件扩展数组(FullSet配置项内容为:['txt', 'log', 'doc', 'docx', 'msg', 'rtf', 'dat', 'ppt', 'pptx', 'xml', 'csv', 'xls', 'xlsx']),然后判断文件后缀是否在其中,若在则通过,文件加密采用全加密,由函数encryption_Full实现;

  1. 若不在FullSet配置项中,则采用部分加密的方式,而加密部分的大小则由配置项BufferSize决定,半加密函数则由encryption_Fast函数实现;

  1. 最后加密结束,返回到task_data_encrypt_and_rename函数进行重命名操作。

5.2.10.3 encryption_Full函数(全加密)

该函数整体主要做用作文件的分快读写操作,加密部分主要是由encryption_OFB128实现。

  1. 采用读写的模式打开文件流;

  2. 采用分块加密,一次加密0x1000大小的数据,直到加密完所有数据为止;

  1. 采用AES_OFB128的加密方式,由函数encryption_OFB128实现;
  2. 最后依次将其加密数据写入到原文件中。

5.2.10.4 encryption_Fast函数(部分加密)

该函数整体主要做用作文件的读写操作,加密部分主要是由encryption_OFB128实现。

  1. 采用读写的模式打开文件流;
  2. 采用一次性读取0x2000大小数据进行加密;

  1. 加密算法采用了AES_OFB128的加密方式,由函数encryption_OFB128实现;

  1. 最后将加密数据写入到原文件中。

5.2.10.5 encryption_OFB128函数(核心加密函数)**:**

该函数核心是一个异或加密,首先程序会通过encryption_Treyfer128函数处理iv。

5.2.10.6 encryption_Treyfer128函数(IV处理)

整体的处理呢大致分为四步:

  1. 将key和iv的值作为sbox的索引加到iv本身,但是需要注意sbox的长度;
  2. 将对应得到的iv的*2给到v4_Lobyte;
  3. 再将iv>>7后给到v4;
  4. 最后将v4和v4_Lobyte做按位或|操作后的值给到iv;

当全部文件加密结束后,回到task_data_encrypt_and_rename函数,将progarm_id写入到文件末尾,做文件重命名操作,以下为基本流程:

  1. 将progarm_id这8个字节的数据写入到文件末尾;

  1. 读取AppendedExtension配置项,AppendedExtension内容为LIVE;
  2. 在其文件名尾部进行拼接;
  3. 重命名该文件。


最后,在所有磁盘中的文件加密完成后将执行:

  1. 清空应用程序日志、安全日志和系统日志;
  2. 获取当前的UTC时间;
  3. 计算当前时间和另一个时间(v401)之间的差值,并将结果输出为字符串;
  4. 释放内存并清理变量。


此程序的分析到此结束,下面会发现磁盘中存在以下信息:

6.加密文件乱象的问题及解决方案

这个LIVE1.0的勒索病毒加密存在一定的bug,程序所存在的BUG原因:

因为多线程没有加锁的缘故,导致线程与线程之间出现死锁和自动销毁的现象,有的文件虽然加密过,但是文件名未被修改,多次加密后,因异或加密的特性,会出现自动解密的情况,这就会产生一些文件乱象问题,按照正常的程序流程,正常加密文件的特征是:

  1. 尾缀存在8字节的progarm_id
  2. 文件名字带.LIVE

由于这个问题会导致出现一些错误,文件无法用脚本去区分到底是加密的还是解密,比如:

  1. 尾缀存在8字节特征一次 -> 未加密情况
  2. 尾缀存在16字节特征两次 -> 加密的情况

因此我们给出的方案就是总共靠两个解密程序恢复,A程序为黑客自己制作的解密器(通过一位已缴纳赎金的受害者获取),我们将其patch之后,可以作为通用的其他受害者解密器使用;B程序为我们制作的补丁程序,若出现某文件用A程序恢复之后无法使用,证明其解密器判断出错,因此运行我们的B程序进行解密,解密之后会释放出两个文件,人工判断选择恢复正确的文件进行保留,错误的文件删除。此外由于LIVE1.0勒索病毒大部分加密的为使用了IP-Guard的系统,IP-Guard自带有文件保护,在文件解密之前,需要批量去除文件的可读权限,因此我们也制作了相关权限解除工具。最终通过这三个程序,我们可以实现百分百还原所有被LIVE1.0勒索病毒加密的文件。

受文章篇幅限制影响,我们将这一部分破解工具的制作使用教程以及相关源码放在我们的工具栏目讲解。

免费评分

参与人数 44吾爱币 +46 热心值 +36 收起 理由
bigbalck + 1 + 1 我很赞同!
FengYing6257 + 1 谢谢@Thanks!
nnzhs + 1 + 1 谢谢@Thanks!
heywood + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
soughing + 1 + 1 我很赞同!
yunhaiwuyu + 1 + 1 谢谢@Thanks!
ROY1003 + 1 + 1 谢谢@Thanks!
gouzi123 + 1 + 1 我很赞同!
lwh206 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
驿路迷茫 + 1 + 1 谢谢@Thanks!
hao9468 + 1 + 1 用心讨论,共获提升!
ouiazrael + 1 + 1 谢谢@Thanks!
hk9186 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
阳光般丶楠神 + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
leonardo86 + 1 + 1 谢谢@Thanks!
kongjianguan + 1 用心讨论,共获提升!
蒋德盼 + 1 + 1 用心讨论,共获提升!
a2f88 + 1 果然是专业!
ironmanxxl + 1 + 1 我很赞同!
soyiC + 1 + 1 用心讨论,共获提升!
chenkeai深蓝 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
WvRt + 1 + 1 鼓励转贴优秀软件安全工具和文档!
Grox9426 + 1 + 1 我很赞同!
janken + 1 + 1 热心回复!
timeni + 1 + 1 用心讨论,共获提升!
aiy1925 + 1 + 1 我很赞同!
蓝董浩 + 1 + 1 谢谢@Thanks!
wuaitomyty + 1 谢谢@Thanks!
yunji + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
goodpanda + 1 用心讨论,共获提升!
x7032360 + 1 + 1 谢谢@Thanks!
抱歉、 + 1 用心讨论,共获提升!
猪猪的快乐 + 1 我很赞同!
xiaobaixiong37 + 1 + 1 谢谢@Thanks!
asky360 + 1 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
b176877840 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
没头脑和温柚 + 1 + 1 谢谢@Thanks!
pojieit + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
kissboss + 3 + 1 用心讨论,共获提升!
daoye9988 + 1 + 1 谢谢@Thanks!
ForCifer + 1 + 1 谢谢@Thanks!
abc2018zh + 1 + 1 高手

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
fengjicheng 发表于 2024-6-8 10:36
本帖最后由 fengjicheng 于 2024-6-8 11:07 编辑

当时拿go 写过一版解密,不过看了你逆向分析,那个代码要改改了。
[Golang] 纯文本查看 复制代码
package main

import (
	"context"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"syscall"

	"github.com/gentlemanautomaton/volmgmt/usn"
	"github.com/gentlemanautomaton/volmgmt/volume"
)

var fullSet = []string{"txt", "log", "doc", "docx", "msg", "rtf", "dat", "ppt", "pptx", "xml", "csv", "xls", "xlsx"}

//var noneSet = [12]string{"exe", "dll", "ink", "ini", "lnk", "ico", "sys", "desktop", "mui", "live"}

// 查询文件大小
func getFileSize(file *os.File) (int64, error) {
	fileInfo, err := file.Stat()
	if err != nil {
		return 0, fmt.Errorf("failed to get file info:%v", err)
	}
	return fileInfo.Size(), nil
}

// 读取ProgramID
func readProgramID(file *os.File, fileSize int64) ([]byte, error) {
	bufferSize := 8
	_, err := file.Seek(fileSize-int64(bufferSize), io.SeekStart)
	if err != nil {
		return nil, fmt.Errorf("faled to seek file:%v", err)
	}
	programId := make([]byte, bufferSize)
	_, err = file.Read(programId)
	if err != nil && err != io.EOF {
		return nil, fmt.Errorf("failed to read program id:%v", err)
	}
	return programId, nil
}

// 读取文件名
func readFileName(file *os.File, fileSize int64) (int, []byte, error) {
	bufferSize := 4
	//跳转到文件名长度位置
	_, err := file.Seek(fileSize-12, io.SeekStart)
	if err != nil {
		return 0, nil, fmt.Errorf("failed to seek file:%v", err)
	}
	//从文件中读取文件名长度
	nameLenBytes := make([]byte, bufferSize)
	_, err = file.Read(nameLenBytes)
	if err != nil && err != io.EOF {
		return 0, nil, fmt.Errorf("failed to read name length:%v", err)
	}
	//计算文件名长度
	nameLen := 0
	for _, b := range nameLenBytes {
		nameLen += int(b)
	}
	//跳转到文件名位置
	_, err = file.Seek(fileSize-12-int64(nameLen), io.SeekStart)
	if err != nil {
		return 0, nil, fmt.Errorf("failed to seek file:%v", err)
	}
	//读取文件名
	bufferSize = nameLen
	fileName := make([]byte, bufferSize)
	_, err = file.Read(fileName)
	if err != nil && err != io.EOF {
		return 0, nil, fmt.Errorf("failed to read name:%v", err)
	}
	return nameLen, fileName, nil

}

func contains(s []string, str string) bool {
	for _, v := range s {
		if v == str {
			return true
		}
	}
	return false
}

// 解密文件
func decrypt(filePathTemp string, wg *sync.WaitGroup) error {
	defer wg.Done()
	var mut sync.Mutex
	mut.Lock()
	defer mut.Unlock()
	//获得目录
	baseDirPath := filepath.Dir(filePathTemp)
	file, err := os.OpenFile(filePathTemp, os.O_RDWR, os.ModePerm)
	if err != nil {
		return fmt.Errorf("open file %s,err:%v", filePathTemp, err)
	}
	//结束自动关闭
	defer file.Close()
	// 获取文件大小
	fileSize, err := getFileSize(file)
	if err != nil {
		return err
	}
	// 获得programID
	programID, err := readProgramID(file, fileSize)
	if err != nil {
		//输出programId
		println(programID)
		return err
	}

	//读取文件名
	nameLen, fileName, err := readFileName(file, fileSize)
	if err != nil {
		return err
	}

	// 计算出的文件名
	fileNameStr := string(fileName)
	//println(fileNameStr)
	//获得文件后缀
	fileExt := filepath.Ext(fileNameStr)
	fileExt = strings.TrimPrefix(fileExt, ".")

	//读取加密内容
	_, err = file.Seek(0, io.SeekStart)
	if err != nil {
		return fmt.Errorf("failed to seek file:%v", err)
	}
	var chiper []byte
	// 判断文件类型是否为全加密类型,如果是则读取所有长度
	if contains(fullSet, fileExt) {
		chiperLen := fileSize - 12 - int64(nameLen)
		chiper = make([]byte, chiperLen)
	} else {
		// 判断文件长度,如果小于加密长度
		if fileSize-12-int64(nameLen) < 0x2000 {
			// 取文件前加密内容
			chiperLen := fileSize - 12 - int64(nameLen)
			chiper = make([]byte, chiperLen)
		} else {
			chiper = make([]byte, 0x2000)
		}
	}

	_, err = file.Read(chiper)
	if err != nil && err != io.EOF {
		panic(err)
	}
	// 解密
	var encryptionKey = []byte{
		61, 33, 125, 145, 168, 153, 200, 93,
	}

	var encryptionIV = []byte{
		70, 69, 195, 247, 191, 147, 238, 160,
	}
	var message []byte
	for x := 0; x < len(chiper)/8; x++ {
		for i := 0; i < 8; i++ {
			encryptionIV[i] = (encryptionIV[i] ^ encryptionKey[i] + 13) & 0xFF
		}
		for i := 0; i < 8; i++ {
			message = append(message, chiper[i+8*x]^encryptionIV[i])
		}
	}
	for i := 0; i < 8; i++ {
		encryptionIV[i] = (encryptionIV[i] ^ encryptionKey[i] + 13) & 0xFF
	}
	// 跳转到开始位置
	_, err = file.Seek(0, io.SeekStart)
	if err != nil {
		panic(err)
	}
	_, err = file.Write(message)
	if err != nil {
		fmt.Println("写入文件失败")
	}
	//跳转至文件名位置
	_, err = file.Seek(fileSize-12-int64(nameLen), io.SeekStart)
	if err != nil {
		panic(err)
	}

	// 修改文件名
	file.Close()
	fileNameFull := filepath.Join(baseDirPath, fileNameStr)
	os.Rename(filePathTemp, fileNameFull)

	// 把恶意写入的文件尾截掉
	err = os.Truncate(fileNameFull, fileSize-12-int64(nameLen))
	if err != nil {
		fmt.Println("清理加密文件尾写入的数据截取失败")
	}
	fmt.Println(fileNameFull, "解密完成")
	return nil
}

func main() {
	// 设置协程最大数
	runtime.GOMAXPROCS(runtime.NumCPU())
	//获得所有盘符
	var wg sync.WaitGroup

	logicalDrives := GetLogicalDrives()
	for _, logicalDrivesItem := range logicalDrives {
		//循环
		files := FindFileWin(logicalDrivesItem + ":\\")
		for _, filePath := range files {
			wg.Add(1)
			//decrypt(filePath, &wg)
			go decrypt(filePath, &wg)
		}
	}

	wg.Wait()
	/*
			err := es.EverythingSetSearch("*.live")
			if err != nil {
				fmt.Printf("EverythingSetSearch:%v\n", err)
				return
			}
			err = es.EverythingSetMax(5)
			if err != nil {
				fmt.Printf("EverythingSetMax:%v\n", err)
				return
			}
			//设置需要查询的内容
			err = es.EverythingSetRequestFlags(es.EverythingRequestFileName | es.EverythingRequestPath |
				es.EverythingRequestDateCreated | es.EverythingRequestDateModified | es.EverythingRequestDateAccessed |
				es.EverythingRequestSize)
			if err != nil {
				fmt.Printf("EverythingSetRequestFlags:%v\n", err)
				return
			}
			//定义排序规则
			err = es.EverythingSetSort(es.EverythingSortDateModifiedAscending)
			if err != nil {
				fmt.Printf("EverythingSetSort:%v\n", err)
				return
			}
			//开始查询
			fmt.Println("EverythingQuery:", es.EverythingQuery(true))
			// 得到结果个数
			num, err := es.EverythingGetNumResults()
			if err != nil {
				fmt.Printf("EverythingGetNumResults:%v\n", err)
				return
			}
			var wg sync.WaitGroup
			for i := uint32(0); i < num; i++ {
				wg.Add(1)
				filePath, err := es.EverythingGetResultFullPathName(i)
				if err != nil {
					fmt.Printf("EverythingGetResultFullPathName:%v\n", err)
					return
				}
				go decrypt(filePath, &wg)

			}

		wg.Wait()
	*/

	//var wg sync.WaitGroup
	//wg.Add(1)
	//decrypt("C:\\DATA\\", &wg)

	fmt.Println("所有文件解密成功")
}

func bitsToDrives(bitMap uint32) (drives []string) {
	availableDrives := []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}

	for i := range availableDrives {
		if bitMap&1 == 1 {
			drives = append(drives, availableDrives[i])
		}
		bitMap >>= 1
	}

	return
}

// 获得所有盘符
func GetLogicalDrives() []string {
	kernel32, _ := syscall.LoadLibrary("kernel32.dll")
	getLogicalDrivesHandle, _ := syscall.GetProcAddress(kernel32, "GetLogicalDrives")

	var drives []string

	if ret, _, callErr := syscall.Syscall(uintptr(getLogicalDrivesHandle), 0, 0, 0, 0); callErr != 0 {
		// handle error
	} else {
		drives = bitsToDrives(uint32(ret))
	}
	return drives
}

// 查询Windows下文件
func FindFileWin(dir string) []string {
	list := make([]string, 0)

	vol, err := volume.New(dir + "\\")
	if err != nil {
		fmt.Println("read system volue error:", err)
		return list
	}
	defer vol.Close()
	mft := vol.MFT()
	defer mft.Close()

	iter, err := mft.Enumerate(nil, usn.Min, usn.Max)
	if err != nil {
		fmt.Println("read system mft error", err)
		return list
	}
	defer iter.Close()

	cache := usn.NewCache()
	ctx := context.Background()
	err = cache.ReadFrom(ctx, iter)
	if err != nil {
		return list
	}
	//读取到的文件名
	records := cache.Records()
	for _, record := range records {
		filesuffix := path.Ext(record.Path)
		// 搜索后缀名,不包含回收站
		if strings.ToUpper(filesuffix) == ".LIVE" && !strings.Contains(record.Path, "$Recycle.Bin") && !strings.Contains(record.Path, "System Volume Information") {
			fullFilePath := filepath.Join(dir, record.Path)
			list = append(list, fullFilePath)
		}
	}
	return list
}
推荐
 楼主| Solarsec 发表于 2024-6-9 14:20 |楼主
longbbyl 发表于 2024-6-7 14:43
护网红队现在成立最大的毒瘤

也不能太绝对了,只是出了一小部分害群之马,大部分都是有职业操守的
沙发
ghostOfTheWolf 发表于 2024-6-6 12:39
3#
shaokui123 发表于 2024-6-6 13:15
作者团队是国人?
4#
 楼主| Solarsec 发表于 2024-6-6 14:03 |楼主
ghostOfTheWolf 发表于 2024-6-6 12:39
攻击手法和红队相似??

勒索病毒是黑客的创收工具
5#
 楼主| Solarsec 发表于 2024-6-6 14:03 |楼主
shaokui123 发表于 2024-6-6 13:15
作者团队是国人?

文章作者是国人
6#
qqycra 发表于 2024-6-6 14:31
文章写的真详细
7#
yutao666 发表于 2024-6-6 14:48
挺好的,不错
8#
kkk013 发表于 2024-6-6 15:26
真强,涨了见识
9#
GFire 发表于 2024-6-6 15:30
看了这么多文章了,像这么详细的真的太少了,支持楼主持续更新下去。
10#
 楼主| Solarsec 发表于 2024-6-6 15:50 |楼主
shaokui123 发表于 2024-6-6 13:15
作者团队是国人?

推测勒索病毒组织是国人,如同文章里面引用的奇安信团队的溯源分析“攻击者在横向移动所使用的工具主要有Cobalt Strike、fscan、frp、勒索投递包等,攻击手法与护网期间的国内红队有着很高的相似性。”结合我们后期对该组织的勒索病毒加密器的分析以及攻击手法,推断该勒索病毒组织的入侵人员以及加密器的编写是国人。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-7-27 11:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表