解密Akira勒索软件(Linux/ESXI变种2024)加密文件:使用大量GPU进行暴力破解
我最近帮助一家公司从不支付赎金的情况下恢复了被Akira勒索软件加密的数据。我将分享我是如何做到的,以及完整的源代码。
代码在这里:https://github.com/yohanes/akira-bruteforce
需要说明的是,多年来有多个勒索软件变种被命名为Akira,目前有几个版本正在传播。我遇到的这个变种从2023年底活跃至今(该公司今年被入侵)。
有一个早期版本(2023年中之前)存在漏洞,允许Avast创建解密器。然而,一旦这个漏洞被公开,攻击者就更新了他们的加密方法。我预计在我发布这篇文章后,他们会再次更改加密方式。
解密:Akira勒索软件
您可以在以下URL找到各种Akira恶意软件样本的哈希值: https://github.com/rivitna/Malware/blob/main/Akira/Akira_samples.txt
与我客户案例匹配的样本是: bcae978c17bcddc0bf6419ae978e3471197801c36f73cff2fc88cecbe3d88d1a 它被列为Linux V3版本。该样本可以在virus.exchange上找到(只需粘贴哈希值进行搜索)。 请注意,赎金消息和公钥/私钥会有所不同。
我们这样做不是因为它容易,而是因为我们以为它容易
我通常拒绝协助处理勒索软件案例的请求。然而,当我的朋友向我展示这个特定案例时,快速检查让我认为它是可解决的。
从我的初步分析中,我观察到以下情况:
勒索软件使用当前时间(纳秒级)作为种子。 在我的Linux机器上,文件修改时间具有纳秒级分辨率。 他们提供了一个部分日志(shell.log)的截图,显示了勒索软件执行的时间,具有毫秒级分辨率。
基于此,我最初的想法是:“这应该很容易——只需通过查看文件时间戳进行暴力破解。能有多难?”
我会更详细地解释,但结果比预期的要复杂:
恶意软件不依赖于单个时间点,而是使用四个时间点,每个都具有纳秒级分辨率。前两个和后两个是相关的,所以我们不能简单地逐个暴力破解时间。密钥生成很复杂,涉及每个时间戳的1,500轮SHA-256。每个文件最终都有一个唯一的密钥。 VMware VMFS文件系统仅以秒级精度记录文件修改时间。 并非所有ESXi主机在其日志文件中都具有毫秒级分辨率,有些仅以秒级精度记录日志。我仍然不确定是什么配置文件导致了这种不同的行为。 恶意软件在执行过程中使用多线程。 文件修改时间反映的是文件关闭的时间,而不是文件打开进行写入的时间。
逆向工程
代码是用C++编写的, notoriously难以阅读,但幸运的是,它没有被混淆。二进制文件是静态链接的(分析起来有点困难),但所有字符串都是明文。错误消息表明使用了Nettle库,这使得理解代码变得容易得多。
错误字符串的存在真的很有帮助
生成随机数的代码是这样的(实际代码在二进制文件的0x455f40处)
|
|
随机数生成器在yarrow256.c中实现。以下是相关代码,删除了不必要的部分。如注释中所述:
重新播种时的迭代次数,yarrow论文中的P_t。应选择使得重新播种大约需要0.1-1秒。
|
|
种子和加密
勒索软件调用随机数生成器四次:
|
|
每个generate_random调用使用当前纳秒时间戳作为种子。因此,需要识别四个唯一的时间戳。勒索软件为每个文件生成不同的密钥。
这些密钥然后使用RSA-4096加密并使用PKCS#11填充保存在文件末尾作为尾部。 文件被分成N个块,每个块的一定百分比被加密。这个百分比由勒索软件的-n参数定义。对于每个块:
前0xFFFF字节使用KCipher2加密。 剩余的字节使用Chacha8加密。
下图显示了文件如何分割。请注意,对于非常小的文件,知道Chacha8密钥和IV不是必需的。
在研究各种VMware文件类型(我稍后会深入探讨)后,我确信最重要的文件(flat VMDK和sesparse文件)具有固定的头部,我可以利用这一点来攻击加密。
其他细节
在这一点上,我没有进行更深入的分析。但我确信我可以在以后逆向工程其余的算法,特别是:
如何将文件分割成块 加密如何在块之间执行,是否继续流?
这些细节以后会很重要。然而,现在,如果我们不能成功暴力破解时间戳,其他步骤都无关紧要。
暴力破解可行性
方法如下:
生成两个时间戳(t3和t4)。 将这些时间戳转换为种子并生成随机字节。 使用这些字节作为KCipher2密钥和IV。 加密已知明文并将结果与加密文件中的已知密文进行比较。
让我们制定一个计划:
检查可行性:确定暴力破解是否足够快以实用。 识别明文:暴力破解需要已知明文。 估计种子初始化时间:我们需要知道加密种子何时初始化,至少具有秒级精度。这些知识可以将暴力破解范围减少到约10亿个值。
最简单(但低效)的方法是尝试所有可能的时间戳对,其中T4 > T3。可能的对数为:N×(N−1)/2 当N = 10亿时,这导致500万亿个可能的对。 我们需要优化这一点。首先,我们需要将所有纳秒转换为随机值:
在我的迷你PC CPU上,我估计处理速度为每秒100,000个时间戳到随机字节的计算(利用所有核心)。 这意味着将所有时间戳转换为种子值大约需要10,000秒(不到3小时)。 一旦转换,这些值可以保存以供重用。 后来,我使用GPU优化了这个过程,将转换时间从3小时减少到不到6分钟。
如果我们有一个完全确定性的机器,没有任何中断,我们可以运行恶意软件,测量它,知道T3和T4之间的确切时间。但不幸的是,我们没有这个条件:
恶意软件使用多线程, 它运行在一个不空闲的机器上,T3和T4之间的距离根据调度器和系统当时的繁忙程度而变化。 代码还调用了许多C++库,这些库分配和释放对象,使得执行时间更加不可预测。
明确地说:
我们需要枚举t3(每秒10亿个值) 我们不从t3 + 1开始,而是从t3 + 起始偏移量开始,因为我们知道播种值需要时间(在我的机器上至少一百万纳秒),这是"起始偏移量" 我们假设执行下一个代码只需要几百万纳秒(记住:由于CPU调度器可能会有中断,并且有几百万条指令被执行)。这是"偏移范围"值
我们可以做的是尝试运行与恶意软件完全相同的代码,收集计时数据,并尝试找到一个在统计上有意义的范围。使用我在上一篇文章中使用的相同技术,我没有重新创建算法并运行它,而是修改了恶意软件并在我的几台本地机器上进行了测试。运行时在不同机器之间变化很大。
我的朋友Deny去数据中心并在被感染的真实硬件上进行了测试。结果是:时间范围变化,有时变化很大。偏移量的正常范围大约在2-4百万纳秒之间(因此偏移范围是200万),但值从1.5到500万不等(总偏移范围是450万)。
我们仍然需要枚举4.5万亿对,但这似乎是可行的。如果我们有一个能够每秒运行5000万次加密的系统,这个过程将需要几百天。然而,使用16个这样的系统,我们可以在CPU上在几个月内完成。通过租用额外的机器,我们可以进一步加快这个过程。后来,我使用GPU优化了这一点,实现了显著的速度提升。
我不确定Kicpher2能有多快,但与chacha的快速比较和一些快速基准测试表明,仅使用CPU,我应该能够在我的机器上每秒至少进行数百万次Kichper操作。
如前所述,如果t3和t4正确,我们将能够解密文件的前8个字节,并且它将解密为已知明文。
接下来检查从不同VMware文件获取明文的可行性
VMWare文件类型
对于每个文件,我们需要一个明文样本:文件的前8个字节用于KCipher2(偏移0)和从偏移65,535开始的另外8个字节(仅适用于大文件)。由于KCipher2的每个块是8字节,我们应该使用8字节明文。可以使用更少的字节(通过使用位掩码),但这可能会增加误报的风险。
Flat-VMDK
这是一个原始磁盘文件。如果你幸运,这可能