使用GPU暴力破解Akira勒索软件加密文件的技术解析

本文详细介绍了如何通过逆向工程分析Akira勒索软件的加密机制,利用GPU并行计算能力暴力破解加密密钥,成功恢复被加密文件而不必支付赎金的全过程。

使用GPU暴力破解Akira勒索软件加密文件

我最近帮助一家公司从不支付赎金的情况下恢复了被Akira勒索软件加密的数据。我将分享我是如何做到的,并附上完整的源代码。

代码地址:https://github.com/yohanes/akira-bruteforce

需要说明的是,多年来有多个勒索软件变种被命名为Akira,目前有几个版本正在传播。我遇到的这个变种从2023年底活跃至今(该公司今年被入侵)。

解密Akira勒索软件

您可以在以下URL找到各种Akira恶意软件样本哈希: https://github.com/rivitna/Malware/blob/main/Akira/Akira_samples.txt

与我客户案例匹配的样本是: bcae978c17bcddc0bf6419ae978e3471197801c36f73cff2fc88cecbe3d88d1a 它被列为Linux V3版本。该样本可以在virus.exchange上找到(只需粘贴哈希值进行搜索)。

逆向工程

代码是用C++编写的,虽然 notoriously 难以阅读,但幸运的是没有被混淆。二进制文件是静态链接的(分析起来有点困难),但所有字符串都是明文。错误消息表明使用了Nettle库,这使理解代码变得容易得多。

随机数生成代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void generate_random(char *buffer, int size)
{
    uint64_t t = get_current_time_nanosecond();
    char seed[32];
    snprintf(seed, sizeof(seed), "%lld", t);
    struct yarrow256_ctx ctx;
    yarrow256_init(&ctx, 0, NULL);
    yarrow256_seed(&ctx, strlen(seed), seed);
    yarrow256_random(&ctx, size, buffer);
}

随机生成器在yarrow256.c中实现。相关代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void yarrow256_seed(struct yarrow256_ctx *ctx,
           size_t length,
           const uint8_t *seed_file)
{
  sha256_update(&ctx->pools[YARROW_FAST], length, seed_file);
  yarrow256_fast_reseed(ctx);
}

void yarrow256_fast_reseed(struct yarrow256_ctx *ctx)
{
  uint8_t digest[SHA256_DIGEST_SIZE];
  unsigned i;
  
  sha256_digest(&ctx->pools[YARROW_FAST], sizeof(digest), digest);
  yarrow_iterate(digest);
  aes256_set_encrypt_key(&ctx->key, digest);
  memset(ctx->counter, 0, sizeof(ctx->counter));
  aes256_encrypt(&ctx->key, sizeof(ctx->counter), ctx->counter, ctx->counter);
}

#define YARROW_RESEED_ITERATIONS 1500

static void yarrow_iterate(uint8_t *digest)
{
  uint8_t v0[SHA256_DIGEST_SIZE];
  unsigned i;
  
  memcpy(v0, digest, SHA256_DIGEST_SIZE);
  
  for (i = 0; ++i < YARROW_RESEED_ITERATIONS; )
    {
      uint8_t count[4];
      struct sha256_ctx hash;
      
      sha256_init(&hash);
      WRITE_UINT32(count, i);
      sha256_update(&hash, SHA256_DIGEST_SIZE, digest);
      sha256_update(&hash, sizeof(v0), v0);
      sha256_update(&hash, sizeof(count), count);
      sha256_digest(&hash, SHA256_DIGEST_SIZE, digest);
    }
}

种子和加密

勒索软件调用随机生成器四次:

1
2
3
4
generate_random(chacha8_key, 32);
generate_random(chacha8_nonce, 16);
generate_random(kcipher2_key, 16);
generate_random(kcipher2_key, 16);

每个generate_random调用都使用当前纳秒时间戳作为种子。因此,需要识别四个唯一的时间戳。勒索软件为每个文件生成不同的密钥。

暴力破解可行性

方法如下:

  1. 生成两个时间戳(t3和t4)
  2. 将这些时间戳转换为种子并生成随机字节
  3. 使用这些字节作为KCipher2密钥和IV
  4. 加密已知明文并将结果与加密文件中的已知密文进行比较

最简单(但效率低下)的方法是尝试所有可能的时间戳对,其中T4 > T3。可能的对数为:N×(N−1)/2,其中N=10亿,结果是500万亿对。

我们需要优化这一点。首先需要将所有纳秒时间戳转换为随机值:

  • 在我的迷你PC CPU上,估计处理速度为每秒10万次时间戳到随机字节的计算(利用所有核心)
  • 这意味着将所有时间戳转换为种子值大约需要10,000秒(不到3小时)
  • 转换后,这些值可以保存以供重复使用
  • 后来,我使用GPU优化了过程,将转换时间从3小时减少到不到6分钟

创建暴力破解器

计划看起来很可靠,下一步是实现代码。我需要确认加密过程是否与恶意软件完全相同。

为了测试这一点,我修补了恶意软件代码,使gettime函数返回常量值0,确保测试期间结果可预测且一致。

KCipher2

我专注于KCipher2,因为并非所有文件都使用Chacha8密钥,特别是小文件。虽然KCipher2是一种标准加密算法,但它并不广为人知,我找不到它的优化实现。

CUDA

我不是CUDA编程专家。大约10年前,我短暂尝试过,但当时无法为我工作的公司找到实际用例。

为了加速开发,我请ChatGPT(o1)将代码移植到CUDA。代码编译成功但产生了错误结果。原来ChatGPT稍微修改了常数表中的数字。手动修正这些值后,代码开始工作。

手动优化

我通过删除不必要的代码进行了一些手动优化以提高性能:

  • 只需要第一个块进行暴力破解,因此无需处理额外的块
  • 代码简化为仅加密零块,减少不必要的处理
  • 由于只需要结果的前8个字节,因此忽略其余输出以最小化计算

最终速度

我相信GPU专家仍然可以找到进一步优化我的代码的方法。目前,我在RTX 3090上对KCipher2实现了约15亿次加密/秒。

对于测试10亿个值和单个偏移,大约需要0.7秒,包括检查匹配的时间(每批最多32个匹配)。

运行暴力破解

从成本角度来看,RTX 4090是此任务的绝佳选择,原因如下:

  • 不需要大内存
  • 不需要浮点运算
  • RTX 4090提供高数量的CUDA核心,增强处理速度
  • RTX 4090的租赁价格相对于其他高端GPU较低

Runpod

要暴力破解1秒(10亿纳秒),偏移范围为200万,将需要7天。RTX 4090(在撰写本文时)的成本为0.69美元/小时。暴力破解单秒将花费约116美元。租用16个GPU将在约10小时内完成工作,成本相同,但速度更快。

恢复步骤

要在不支付赎金的情况下恢复文件,并不像运行通用解密器那么简单。您需要:

  1. 获取文件的时间戳
  2. 获取文件的密文和明文
  3. 租用GPU

结论

很可能99.9%的情况下,当您遭遇勒索软件时,没有密钥将无法恢复。但如果您幸运,有时可能找到解决方案。解决这个问题花费的时间比我预期的要长得多,我以为需要一周,但花了将近三周才恢复完整的VM文件集。

我希望我的经验和代码对其他人有用。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计