文章总结: 本文详细分析了国内排名第一的Mallox勒索病毒家族的加密机制和破解思路。通过逆向分析发现,该病毒使用CryptGenRandom函数获取随机数种子,结合梅森旋转算法和Curve25519密钥交换生成加密密钥,并采用AES和ChaCha20算法对文件进行加密。破解思路主要从55个固定字节的数据入手,通过获取C盘序列号、CPUID和计算机名称等信息恢复加密密钥,实现文件解密。文章提供了具体的代码实现和恢复效果对比,对勒索病毒应急响应具有重要参考价值。
综合评分: 91
文章分类: 恶意软件,漏洞分析,逆向分析
【病毒分析】全网首发!袭扰国内top1勒索病毒家族Mallox家族破解思路及技术分享
原创
索勒安全团队
solar应急响应团队
2024年12月18日 10:00
浙江
引言:根据360安全卫士官方论坛的最新统计数据,本月国内勒索软件受害者设备所中病毒家族中Mallox家族占比22.38%居首位。https://weishi.360.cn/dnzs/12640.html
1. 背景
1.1 前情提要
Mallox家族的加密器是solar团队处理的众多案例中,分析次数最多的家族之一。本次分析的对象是2022年10月时间段的mallox后缀加密器。通过对样本的深入分析,我们捕获到了一些关键线索,这些线索为破解提供了重要思路。在下文中,我们将详细分享这些发现和分析过程,基于本篇思路制作的恢复工具会在下一篇工具分享中进行发布,敬请期待!
1.2 家族介绍
关于Mallox家族信息可参考本篇文章【病毒分析】Mallox勒索家族新版本:加密算法全面解析,详情内容部分可移步solar官网处介绍https://www.solarsecurity.cn/family?id=3
1.3 支持的文件类型
基于本篇文章提到的破解思路,针对Mallox家族的不同版本后缀,可以通过逆向分析获取相关密钥和算法进行恢复:
| mallox版本后缀 | 时间段 | HASH |
| — | — | — |
| .mallox | 2022年10月到2023年3月 | 1f793f973fd906f9736aa483c613b82d5d2d7b0e270c5c903704f9665d9e1185 |
| .xollam | 2023年1月 | 10f96f64659415e46c3f2f823bdb855aab42d0bfced811c9a3b72aea5f22d880 |
| .bitenc | 2023年1月 | a340ef5adb00a2bf1a0735600491ca98ac8045b57db892dedc27575a53b25056 |
| .malox | 2023年4月到2023年7月 | 0427a9f68d2385f7d5ba9e9c8e5c7f1b6e829868ef0a8bc89b2f6dae2f2020c4 |
| .maloxx | 2023年6月 | 03596723ecb5be777b1e3ec5deb414dc78155a30a466b2b25a4574f4db9a568a |
| .malloxx | 2023年8月 | 4e4592a5dec8a0fc12bbd6f955cd01936aa80d7b8d54db5258140a8a1c460c66 |
| .mallab | 2023年9月到2023年10月 | d1969e0ec464e8b09e54ea35fbe181ea391ed3674e0b35b0f6ee92821478e76c |
| .ma1x0 | 2024年2月 | 003ea0712cd31a75f5dfb6a23d2d12ea1e615f7601574ade09b6b61e3b7ed2b0 |
2. 逆向分析
2.1 前置知识
在分析如何破解之前,需要提前了解一点关于mallox家族加密的内容。
mallox家族最为显眼的就是这部分,采用CryptGenRandom函数的方式获取随机数种子,那怕最近的一些变种版本,都是沿用此类函数来进行密钥生成。
在获取到了4个字节的随机数之后,就会将该随机数当做是梅森旋转算法的随机数种子
接着会初始化一个public_key,然后与后面的一组55个字节大小的内容(C盘序列号+CPUID+固定内容+计算机名称)的sha256进行一个cure25519算法的密钥生成。这里命名public_key和55字节大小的数据的sha256产生的share_key为user_private_key
这里命名public_key和55字节大小的数据的sha256产生的share_key为user_private_key,接着再利用这个生产的私钥产生一个对应的公钥,然后再用这个私钥和黑客的hack_public_key生成一个共享密钥share_key。
这里具体可以参考Babyk家族分析中的算法分析那部分的内容【病毒分析】BabyK加密器分析-Windows篇。
OK,到了目前为止,我们大致上了解了整体的流程如下:
(注意:这里我并未将梅森算法和下面的流程连到一起,因为他们其实并不相干)
接下来,我们去掉mallox章节的算法分析部分的内容,直接来看看,加密文件所使用的key到底是怎么生成的?
(注意:这里如果看不懂,请看上一章节mallox算法分析部分的内容【病毒分析】Mallox勒索家族新版本:加密算法全面解析)
首先,文件的加密函数在StarAddress函数中,并且梅森旋转算法中,生成随机数的部分也在这里,其中这个v9就是我们利用上面的梅森算法生成的256组每组4字节大小随机数,至于这个sub_E43C34、sub_E4142B和sub_E434函数,在这里就是对随机数进行变形的,所以就先不提,继续向下看。
进入到enc_file函数,来看文件加密的具体过程。首先会先获取一下后续AES加密算法的IV,获取方法就是从传入的256组随机数中,取后4组数据,然后一组数据是4个字节组成,一共16个字节。
然后继续接着上面的v7的位置从后往前再取10组数据,一共40个字节,当做ChaCha20加密算法的Key和Nonce。
接着就是开始初始化一下ChaCha20的Key和Nonce还有AES的IV。
调用AES-128-CTR加密算法,来对40个字节的ChaCha20的Key和Nonce数据进行加密。
到此,我们再做一次总结:
但是这里有个问题,AES的KEY去哪里了???
其实AES的KEY在AES_init函数中才会被用到,这里看一下具体内容
在aes_init函数的初始化部分可以看到key的部分,主要取了上面密钥生成部分,最后share_key的sha256的前16个字节的内容。
好了,我们将此过程补充完整:
最后,我们将要用Cha20Cha20的密钥去加密文件了。当然了,遵循传统勒索软件的加密规则“既要加密的牢,还要加密的快”,依然会采用根据文件大小来进行加密部分的计算:
1 文件大小 < 0x2710 字节,文件不加密
2 文件大小>=0x2710字节并且<= 0x19000字节,文件全部加密
3 文件大小>0x19000字节采用条状加密。
这里也补充一下条状加密的部分的规则,因为mallox算法分析那篇确实漏掉了。
首先会将待加密的文件分成10个块,每块大小这里我称之为block_size。之后,循环十次,每次只加密每个块的前enc_size大小的内容,如图所示:
了解了加密的结构以后,这里就将这个enc_size和block_size的计算公式放出来:
enc_size = 15 * (文件大小 / 10 / 100) & 0xfffff000
block_size = 文件大小 / 10
但是这里还会对enc_size的大小做处理,假如,enc_size的大小,如果小于0x1000(4096)字节大小时,则会将其强行扩充到0x1000(4096大小)。
以上就是全部的条状加密规则了。
加密完毕文件以后,将会在文件末尾写入100个字节大小的数据,其中包括了如下内容:
OK,到目前为止我们总结一下整个过程:
2.2 破解思路分析
以上我们分析了从密钥生成到文件加密的全部过程,包括了每个数据的产生和由来。所以分析可以知道,破解的思路大致上只有两种:
1 从随机数上下手,获取随机数种子
2 从55个固定的字节下手
这里我们先采用第二种方式,针对55个固定字符进行分析。
这55字节部分内容主要包括:
其中固定值部分是针对于每个mallox的变种后缀样本,定制生成的,所以会不一样。
这里我们来模拟一下这个过程:
package main
/*
#cgo CFLAGS: -std=gnu11
#include <stdint.h>
// 使用GNU内联汇编调用CPUID
// 输入: op (EAX输入值)
// 输出: eax, ebx, ecx, edx
void cpuid(uint32_t op, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) {
uint32_t a, b, c, d;
#if defined(__x86_64__) || defined(__i386__)
__asm__ volatile (
"cpuid"
: "=a"(a), "=b"(b), "=c"(c), "=d"(d)
: "a"(op)
);
#else
// 非x86架构下这里可能需要不同实现或直接报错
a = b = c = d = 0;
#endif
*eax = a;
*ebx = b;
*ecx = c;
*edx = d;
}
*/
import "C"
import (
"encoding/binary"
"fmt"
"os"
"syscall"
"unsafe"
)
func uint32ToBytes(num uint32, bigEndian bool) []byte {
// 创建一个字节切片
byteArray := make([]byte, 4)
if bigEndian {
// 使用大端字节序
binary.BigEndian.PutUint32(byteArray, num)
} else {
// 使用小端字节序
binary.LittleEndian.PutUint32(byteArray, num)
}
return byteArray
}
// GetVolumeSerialNumber 返回指定盘符的卷序列号
func GetVolumeSerialNumber(drive string) (uint32, error) {
kernel32 := syscall.NewLazyDLL("kernel32.dll")
pGetVolumeInformationW := kernel32.NewProc("GetVolumeInformationW")
var (
volumeNameBuffer = make([]uint16, syscall.MAX_PATH+1)
volumeSerialNumber uint32
maxComponentLength uint32
fileSystemFlags uint32
fileSystemNameBuffer = make([]uint16, syscall.MAX_PATH+1)
)
drivePtr := syscall.StringToUTF16Ptr(drive)
ret, _, err := pGetVolumeInformationW.Call(
uintptr(unsafe.Pointer(drivePtr)),
uintptr(unsafe.Pointer(&volumeNameBuffer[0])),
uintptr(len(volumeNameBuffer)),
uintptr(unsafe.Pointer(&volumeSerialNumber)),
uintptr(unsafe.Pointer(&maxComponentLength)),
uintptr(unsafe.Pointer(&fileSystemFlags)),
uintptr(unsafe.Pointer(&fileSystemNameBuffer[0])),
uintptr(len(fileSystemNameBuffer)),
)
if ret == 0 {
return 0, err
}
return volumeSerialNumber, nil
}
func get_disk_serial(disk_path string) ([]byte, error) {
serial, err := GetVolumeSerialNumber(disk_path)
if err != nil {
fmt.Println("获取C盘序列号失败:", err)
return nil, err
}
return uint32ToBytes(serial, false), err
}
func get_cpuid() ([]byte, []byte, []byte, []byte) {
var eax, ebx, ecx, edx C.uint32_t
C.cpuid(0, &eax, &ebx, &ecx, &edx)
return uint32ToBytes(uint32(eax), false), uint32ToBytes(uint32(ebx), false), uint32ToBytes(uint32(ecx), false), uint32ToBytes(uint32(edx), false)
}
func get_hostname() ([]byte, error) {
hostname, err := os.Hostname()
if err != nil {
fmt.Println("获取计算机名称失败:", err)
return nil, err
}
return []byte(hostname), err
}
func get_whole_password() []byte {
password, err := get_disk_serial("C:\\")
if err != nil {
return nil
}
eax, ebx, ecx, edx := get_cpuid()
hostname, err := get_hostname()
if err != nil {
return nil
}
password = append(password, eax[:]...)
password = append(password, ebx[:]...)
password = append(password, ecx[:]...)
password = append(password, edx[:]...)
//这里到时候可以写个判断,来根据不同的加密版本的固定值进行选择拼接
password = append(password, uint32ToBytes(uint32(0x0FADACE8), false)...)
password = append(password, uint32ToBytes(uint32(0x9AB979DD), false)...)
password = append(password, uint32ToBytes(uint32(0x4EAFA9C2), false)...)
password = append(password, uint32ToBytes(uint32(0x00), false)...)
password = append(password, uint32ToBytes(uint32(0x00), false)...)
password = append(password, hostname[:]...)
return password
}
func main() {
password := get_whole_password()
fmt.Printf("55个固定字节:%x", password)
}
通过测试发现可以恢复这55个固定的字节,得出的数据与我们从勒索内存中所得的数据一样。但是因为要获取本机的用户名、CPU_ID和磁盘序列号,所以解密有一个前提就是只能在被加密的机器上运行才可以恢复。
接着就是利用这55个字节来和程序自带的Public_Key进行Cure25519的密钥交换,生成user_public_key。
先写一个Cure25519的密钥交换:
func get_key(private_key, public_key []byte) []byte {
//// Curve25519: 计算共享密钥
// 将公钥转换为 [32]byte 类型
var publicKey [32]byte
copy(publicKey[:], public_key)
// 将私钥转换为 [32]byte 类型
var privateKey [32]byte
copy(privateKey[:], private_key[:])
// Curve25519: 计算共享密钥
var sharedKey [32]byte
curve25519.ScalarMult(&sharedKey, &privateKey, &publicKey)
return sharedKey[:]
}
再来实现以下对应的生成:
hash := sha256.Sum256(password)
fmt.Printf("55个固定字符的Sha256:%x\n", hash)
public_key1, err := hex.DecodeString("CD20FA7A55F384AAFF27C1456636BD6374329E217B8066811A4F204591B8C1FF")
if err != nil {
fmt.Println("hex to bytes error!")
}
share_key := get_key(hash[:], public_key1)
fmt.Printf("User_Public_Key:%x\n", share_key)
接下来发现,已经能和勒索程序中的内容对应了。
之后就是一系列的重复操作,最终可以恢复到share_key和user_public_key,然后利用user_public_key与程序末尾的user_public_key进行校验,如果一样,说明share_key可以用来解密,如果不一样,则说明该勒索文件并不是在本机上进行的加密行为。
以上就是该破解的全部过程,后续就是正常的恢复算法等内容,比较简单,这里就不再继续了。
2.3 测试结果
2.3.1 恢复前
2.3.2 恢复后
视频
3. 病毒分析概览
mallox家族的加密过程主要通过生成随机数种子、利用梅森旋转算法和对特定数据(如C盘序列号、CPUID等)进行SHA-256哈希处理,形成私钥和公钥,最终生成共享密钥来加密文件。文件加密过程中,采用了AES和ChaCha20等加密算法,并根据文件大小决定加密方式(完整加密或分块加密)。破解思路主要集中在获取随机数种子和分析55个固定字节的数据。
以下是solar安全团队近期处理过的常见勒索病毒后缀:
| 出现时间 | 病毒名称 | 相关文章 |
| — | — | — |
| 2020年1月 | .mkp | 【病毒分析】揭秘.mkp后缀勒索病毒!Makop家族变种如何进行可视化加密? |
| 2024年5月 | .moneyistime | 【病毒分析】使用中文勒索信及沟通:MoneyIsTime 勒索家族的本地化语言转变及其样本分析 |
| 2024/9/29 | .lol | 【病毒分析】全网首发!全面剖析.LOL勒索病毒,无需缴纳赎金,破解方案敬请期待下篇! 【工具分享】.LOL勒索病毒再也不怕!完整破解教程分享+免费恢复工具首发 |
| 2024/6/21 | .MBRlock | 【病毒分析】假冒游戏陷阱:揭秘MBRlock勒索病毒及其修复方法 |
| 2024/6/1 | .steloj | 【病毒分析】Steloj勒索病毒分析 |
| 2024/5/27 | .TargetOwner | 【病毒分析】技术全面升级,勒索赎金翻倍,新版本TargetOwner勒索家族强势来袭? |
| 2024/5/17 | .Lockbit 3.0 | 【病毒分析】Lockbit家族Lockbit 3.0加密器分析 【成功案例】RDP暴露引发的蝴蝶效应:LockBit组织利用MSF工具及永恒之蓝漏洞进行勒索入侵 【成功案例】lockbit家族百万赎金不必付!技术手段修复被加密的数据库,附溯源分析报告 【病毒分析】繁体勒索信暗藏玄机!要价50万RMB赎金的Lockbit泄露版分析 |
| 2024/5/13 | .wormhole | 【病毒分析】Wormhole勒索病毒分析 |
| 2024/4/9 | .bianlian | 【病毒分析】新兴TOP2勒索软件!存在中国受害者的BianLian勒索软件解密原理剖析 |
| 2024/3/20 | .locked | 【病毒分析】locked勒索病毒分析 |
| 2024/3/11 | .Live1.5 | 【病毒分析】独家揭秘LIVE勒索病毒家族之1.5(全版本可解密) |
| 2024/3/8 | .Live2.0 | 【病毒分析】独家揭秘LIVE勒索病毒家族之2.0(全版本可解密) |
| 2024/3/6 | .Elbie | 【病毒分析】phobos家族Elbie变种加密器分析报告 |
| 2024/3/1 | .lvt | 【病毒分析】交了赎金也无法恢复–针对国内某知名NAS的LVT勒索病毒最新分析 |
| 2024/2/26 | 0.27 | 【病毒分析】phobos家族2700变种加密器分析报告 【成功案例】某集团公司的Phobos最新变种勒索病毒2700解密恢复项目 |
| 2024/1/18 | ._locked | 中国人不骗中国人?_locked勒索病毒分析 |
| 2024/1/15 | .faust | 【病毒分析】phobos家族faust变种加密器分析 |
| 2024/1/15 | .DevicData | 【病毒分析】DevicData勒索病毒分析 |
| 2024/1/2 | .jopanaxye | 【成功案例】某集团公司的Phobos最新变种勒索病毒jopanaxye解密恢复项目 |
| 2023/12/1 | .live1.0 | 【病毒分析】独家揭秘LIVE勒索病毒家族之1.0(全版本可解密) |
| 2023/9/5 | .CryptoBytes | 【独家破解】揭秘境外黑客组织的20美元锁机病毒:深度逆向分析+破解攻略!赎金?给你付个🥚 |
| 2023/8/28 | .mallox | 【病毒分析】mallox家族malloxx变种加密器分析报告 |
| 2023/8/2 | .rmallox | 【病毒分析】mallox家族rmallox变种加密器分析报告 【成功案例】间隔数月双团伙先后利用某ERP0day实施入侵和勒索的解密恢复项目 |
| 2023/1/10 | .DevicData-Pa2a9e9c | 【病毒分析】DevicData家族扩散:全球企业和机构成为勒索病毒头号攻击目标! |
| 2023年初 | .halo | 【病毒分析】全网首发!以国内某安全厂商名字为后缀的勒索病毒分析 |
| 2021/5/1 | .mallox | 【病毒分析】Mallox家族再进化:首次瞄准Linux,勒索新版本全面揭秘! |
| 2021年1月初 | .babyk | 【病毒分析】BabyK加密器分析-Windows篇 【病毒分析】Babyk加密器分析-NAS篇 【病毒分析】 Babyk加密器分析-EXSI篇 【病毒分析】Babuk家族babyk勒索病毒分析 【成功案例】利用多款国产内网渗透工具勒索数十台虚拟机的babyk解密恢复项目 |
| 2020/5/18 | .consultraskey-F-XXXX | 【成功案例】解决浙江xx电子有限公司的勒索病毒 |
| 2019/5/1 | .src | 【病毒分析】Phobos家族新变种 .SRC深度分析:揭示持续演变的勒索新威胁 |
勒索攻击作为成熟的攻击手段,很多勒索家族已经形成了一套完整的商业体系,并且分支了很多团伙组织,导致勒索病毒迭代了多个版本。而每个家族擅用的攻击手法皆有不同,TellYouThePass勒索软件家族常常利用系统漏洞进行攻击;Phobos勒索软件家族通过RDP暴力破解进行勒索;Mallox勒索软件家族利用数据库及暴力破解进行加密,攻击手法极多防不胜防。
| 时间 | 相关文章 |
| — | — |
| 2024/12/12 | 【攻击手法分析】勒索病毒如何轻松绕过安全设备防线:第二篇-流量致盲,无声突破 |
| 2024/12/11 | 【攻击手法分析】勒索病毒如何轻松绕过安全设备防线:第一篇-驱动漏洞一击致命 |
而最好的预防方法就是针对自身业务进行定期的基线加固、补丁更新及数据备份,在其基础上加强公司安全人员意识。
| 时间 | 相关文章 |
| — | — |
| 2024/6/27 | 【教程分享】勒索病毒来袭!教你如何做好数据防护 |
| 2024/6/24 | 【教程分享】服务器数据文件备份教程 |
如果您想了解有关勒索病毒的最新发展情况,或者需要获取相关帮助,请关注“solar专业应急响应团队”。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:solar应急响应团队 索勒安全团队《【病毒分析】全网首发!袭扰国内top1勒索病毒家族Mallox家族破解思路及技术分享》