概述
最近,我们曾发表过一篇关于Chimera勒索软件密钥泄漏的文章(文章链接)。但是在这篇文章中,我们将会披露更多相关的技术细节,我们将会给大家介绍如何利用这些泄漏出来的密钥来解密文件。除此之外,我们还会对这些泄漏密钥的有效性进行测试。
实验数据和分析方法
一般而言,编写一款勒索软件的解密程序往往需要开发人员拥有较强的能力,他们不仅需要对加密算法有较深层次的理解,而且还得从勒索软件中寻找漏洞。不同的漏洞则意味着开发人员需要用不同的思维理念来开发破解工具。
某些时候,我们需要对有漏洞的加密算法进行一定的改造,并且开发出一款能够猜测密钥的工具(例如Petya勒索软件的破解过程)。某些时候,我们的工作重点应该放在勒索软件的对称密钥生成器上(例如DMALocker 2.0的破解过程),或者将注意力集中在勒索软件的加密算法身上(可以参考7ev3n勒索软件所采用的自定义加密算法)。
但是这一次,我们已经为大家准备好了一些现成的工具:
1. 泄漏的密钥集;
2. 由Chimera的开发者提供给该勒索软件受害者的原始解密程序;
对解密程序进行逆向分析
正如我们此前在对Chimera进行分析时所描述的,勒索软件的攻击者通常会在目标主机中留下勒索信息,而Chimera的作者向受害者提供了一个链接,他们可以自行下载一个外部解密工具。为了能够让解密工具运行,用户需要购买一个私钥,只有当私钥与公钥成功配对时,用户被加密的文件才能够成功恢复。
这个解密工具采用了.NET语言进行编写,该工具中所有的文件解密操作全部由一个外部组件来控制-一个名为“PolarisSSLWrapper.dll”的DLL文件。经过了分析之后,我们发现该组件能够提供以下两个函数:
所以我们就没有必要自己亲自去设计解密函数了,我们可以直接利用这个现成的API。
首先,我们需要先对该工具中主要的组件进行逆向分析。因为我们在进行下一步操作之前,需要弄清楚这两个函数是如何被调用的,以及该函数需要传入哪些参数。
从下面这张代码截图中,我们可以看到用于解密文件的外部函数是如何被调用的:
如上图所示,我们所关心函数是“DecryptFileWrapper”。该函数需要传入三个参数:(1)待解密文件的存储路径(以一个ASCII字符串表示);(2)购买的私钥(字节数组);(3)私钥长度。该函数的返回值是一个布尔类型的值,用来通知系统文件的解密操作是否成功。我们可以对该函数进行重构,具体如下所示:
bool _cdecl DecryptFileWrapper(char filePath, BYTE*privateKey, size_t privateKeySize);
函数会从收到的bitmessage中读取出私钥信息,并将其解码(Base64编码)成字节数组的形式:
由于Chimera的服务器在几个月前就已经下线了,所以我们无法捕获到该勒索软件的网络通信数据。但是多亏了BleepingComputer此前所发表的研究报告,我们现在可以获取到该勒索软件网络通信信息的结构。示例:
56209A92A96E9F96B0D9E6F962D0D9EF:5Zn9azBBDQQznI9znnHZBDs6+nQz6nB9/6DBa0nXbDz0aghs6fg62Rn9ZzxnDGEzRQ9tFdIZDfa05Lz+nlb6IGnzSDQz0tznrdUzGgq9Nibzx0Zusl2aHn6nzZZE6tbQQe/vzbASDuanTBL5SazSARe52QSq6BEzD5rGqzZhnaLaZrfbI6bN6A6nnnH5lgbeSAzXdz+6eNxqQt9ITziIxF+eDFBBBVZ+zHf6esQzzH2uBnQnaHzzi6tDna9Xngfh6bzQQZBfq+vFbZ9ZfvnTL6D2arAnBzb6A06Qzfn2zRD5hz5eLZzDIDR00/anblbU2bvRTH6ZGaXDeBQQ5NHGhQEAAdZBtx/VaVQsrrDZdasadBHi0RDeZz0Da6glNRz9/U59zXaaeAN0Di5eb2zQtvr5h6Fvb6tzB9THtRUGS6Qzt6BxAz/zTg6gs56h5xXzSnslBQRzngandHzFTaBHix692DLxDaziQnZBQDQRB/Zz5HXQfNzz5aad90+uHAsDBDDVzZsngtbgSHDTzA2X5zQs2tHba5z99qF66IQHqZaZD2erzuDQzzx9NlXaTZEiH2IrVGSZizxEFQzLBl6+BDDn5zuG6x6zBhfaNB56nDUXt6BnzzuvNVq2xzDTn2lSu/QrHzNbFdGSLazTh29z5Fx9D5Zt6QBBrQ+aFuNDhASgDDH20UDnQB00gBa2t6/i+Lq6eV9ZHzzDdEn2T6HXfg+BEnvr5tXB5zZL2zbtvBVxFX2QsBZ9ZrzzG6fIvnvnz6NZ5endiz0IzAQ2Dbqa6gnnsSnznsVIizznibdZIFvF/nsqbVQZB9Zn2nBQUDTQzzDT65NxHzLRvz6VzZsQV5bih+S+shaD6ASaDFzHNQD9ZVIi+ZrafBxes6zqQaBfEs6z+Igd6nZhEzDLZZN/9QbhQzlzfBf5IFL0nqt2qqnSeqgz0bzQgZ9Dq6r50SB6xHhn9DfnNa6hR0DUiubDH5z0+n60UD0Uzz6ti6faD5Sz99f6dtQN6LUfZn/RagfiqnzTXZrvBZv95a9aT9u9tE6qLH0BSNArn0I9rF2eBDQH92zFUBBBVs9e/ZrXZ2Qaz/zn6BzxQ05qtqNQTZ6AS22z6nBf/200L66zasDHrzZ9agHAx2qBNUlRaUDFszInzzaLD5IzGQeQaAU60zz6nZQvVe906uDTXGlaZDDfBQUzD/nRDrZUaa2h59Hf5gbeezTHNiHBaBDzzQBx0BX5Zz92Z2zanfSZZ/2BRnzzzQBzxa/iIxiDNb6Qdd9Bh6/FQnfeznSv5rZ6DZitTGZZUAdzgD5azVAn2G/G+EssFuhV5aBb0N/N2q+2656zgxBBDzn2+0NIZLudxTXRsDNDza0V/9gzzEaqBdZax6QDlfnQhAVIn9XZu/D+gr9+ZRqz5266IBST5E5LBZed/s0zS2QHeBrIHnznZtez+02+Q+50g+ZUD6nrbhR2f+NzB6NgZ6ID9e5hnEEQ99xlnSDZT2aQN6QBRueDRZzNaTz6bvQrGhaBaeFl96hZDZLUDIu6rAzB
其结构为:[感染者ID]:[base64编码密钥]
在对密钥进行了解码之后,我们得到了一个长度为1155字节的字节数组。
这个数组中存储的原始字节数据即为我们所需要的密钥。
解析密钥
此前泄漏出来的密钥是一堆十六进制的字符串。如果我们将这些数据转换为原始的二进制数据,就会发现这些密钥的长度均为0×483字节。这是一个非常重要的发现,因为这些密钥的格式与上文中描述的密钥是一样的,所以我们不需要对这些数据进行额外的处理了。我们所要做的,就是将这些十六进制数据转换为原始的二进制数据即可。
泄漏文件的格式还算比较整齐,每一个密钥的结尾都会另起一行。我们可以直接移除开头的一些无用数据,然后将这份文件作为输入:
更确切地来说,我们此前将这些数据描述为密钥集合,但实际上它们应该是密钥对的集合。每一对密钥的长度均为0×483字节。其中,前0×103字节的数据中包含的是公钥信息,接下来的0×380字节即为私钥信息。我们所使用的API需要这一串完整的数据作为输入,解密工具的DLL组件会将这些数据当作“私钥”来进行处理。由于公钥和私钥的信息全部包含在这些数据里面了,所以解密工具就可以自动验证密钥的匹配结果。
寻找出合适的解密密钥
正如你所见,我们已经完成了大部分的工作。剩下的最后一件事情就是确定泄漏的密钥集中是否真的存在可以解密我们文件的密钥。
在这一步操作中,我们不得不使用算法来解决我们的问题。我们的实现方式看起来与字典攻击有些类似,我们的“字典”是一套泄漏密钥的集合。为了进行验证,我们将会尝试解密其中的一份被加密的文件。我们大致的思路用下面这段伪代码来表示:
- while ((privateKey = getNextFromSet()) != NULL) {
- if(DecryptFileWrapper(encryptedFile, privateKey, privateKeyLen) == true) {
- printf("Hurray, key found!");
- storeTheFounKey(privateKey);
- return true;
- }
- }
- printf ("Sorry, your key is not in the leakedset!");
- return false;
如果你对此感兴趣的话,可以点击下方的地址下载完整的代码:
https://github.com/hasherezade/chimera_decrypt
找出了能够成功匹配的密钥之后,我们就可以解密剩下的所有文件了。
测试
测试一:
Chimera在每次运行的时候会生成一个唯一的随机密钥对。然后,它会利用bitmessage来将密钥对发送至C&C服务器上。值得注意的是,发送的数据还包括与目标用户有关的其他数据集合。
为了完成测试,我使用了一个由原始Chimera勒索软件样本生成的密钥,并将其从内存中导出。在将密钥传递给用于发送数据的函数之前,密钥的信息是可以清楚地看到的。如下面这张截图所示:
我将这些数据转换成了与泄漏密钥相同的格式(连续的十六进制字符串)。
我们事先准备好的工具可以正常运行,测试文件已经被成功解密了:
测试二:
因为在泄漏数据中我们已经得到了完整的密钥对,所以我们也可以用它们来进行测试。在实验过程中,我从其中一个泄漏密钥中截取出了公钥数据,然后将其提供给了Chimera样本。我之所以这样做,是为了模拟出一份受害者(密钥已经泄漏)被加密的文件。
接下来,我们需要用提取出的公钥替换掉Chimera自动生成的原始公钥:
现在,我们就可以尝试使用泄漏密钥和事先准备好的工具来解密受攻击的文件了:
解密成功了!我们的测试也成功证实了泄漏的数据中的确包含有真实的密钥对,这些数据并不是无效的垃圾数据。
除了上述两个测试之外,我们还进行了很多其他的测试,而且我们还对这些工具进行了编译。如果你感兴趣的话,可以访问我们的Github代码库来了解详细的信息(Github主页链接)。
总结
通过查看密钥的格式,我们就可以推测泄漏的数据中应该包含有合法的密钥数据。除此之外,我们也从其他安全研究专家那里得到了证实,泄漏密钥中的部分数据确实能够匹配他们捕获的通信样本。但是,我们已经没有办法从受害者那里得到分析样本了。Chimera在几个月前就已经彻底挂掉了,而且大多数受感染的用户可能早就已经将被加密的文件删除了。