译者:myswsun
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
0x00 前言
本文中,我们将通过一些发现和工具来解密NotPetya/Petya。使用我们提供的工具,几乎所有的MFT能在数分钟内恢复。
0x01 解密MFT
毫无疑问,MFT是NTFS文件系统最重要的数据结构之一。它包含了文件系统中的文件列表,及元数据(如日期,文件大小,磁盘位置等)。没有MFT的话,任何试图恢复文件的动作都将徒劳。通过利用NotPetya/Petya中的Salsa20加密算法实现中的漏洞来解密MFT是唯一的可能。基本上,它可以是一种已知明文攻击和多次攻击的组合。注意这种攻击不意味着Salsa20可以被破坏,它只影响了具体的实现。
0x02 如何解密MFT
为了恢复MFT,首先,需要从受感染的硬盘中提取出MFT。这可以通过磁盘取证实现,通过将受感染的硬盘附加到未受感染的系统中,或者通过live CD引导受感染的计算机。
假设使用前者,如使用名为disk.dd的磁盘取证工具,通过下面的命令提取出MFT(存为文件mft.raw):
第一个命令(mmls)返回分区表。通常,NFTFS主分区的起始扇区是63或者2048。第二个命令(icat)输出NTFS分区的MFT到文件mft.raw中。
一旦提取了加密的MFT,可以使用工具decryptPetya.py来解密它。
取决于MFT的大小和加密的记录的数量,解密时间大约在2~30分钟之间。
DecryptPetya.py的命令行选项如下:
根据提供的选项,脚本输出解密后的MFT(文件名为[mft].decrypted_strict 或者 [mft].decrypted_relaxed)。默认情况下,是relaxed解密。无论如何,它都会尝试解密输入的密文。通过使用“-strict”选项可以使得结果更加可靠。
0x03 手动分析解密后的MFT记录
很少情况下需要恢复第一个或最后几个MFT记录。可以从decryptPetya.log文件获得帮助。日志文件尽可能的输出指定密文对应的明文。下面提供了一个例子。
在默认用例中,decryptPetya.py已经填充了明文。这个例子中,0.9876的确定性的字节0x00被写入到解密后的记录中。
0x04 进一步分析MFT
一旦符合了MFT结构,可以进一步分析,例如使用类似的工具(analyzeMFT.py),或patch到硬盘中。
0x05 技术细节
那么,Petya(也被NotPetya作者拷贝了)的作者到底犯了什么错误,导致可以解密MFT呢?
之前关于Petya和NotPetya的报告中表明了他们共享了不完善的Salsa20加密算法。代码与GitHub中找到的开源实现非常类似。尽管源代码的实现是正确的,但是Petya的作者在转换16位代码时犯了几个错误。这些错误使得Petya/NotPetya有漏洞,可以使用已知明文攻击来解密大部分MFT。
0x06 Salsa20流位置漏洞
本节描述了NotPetya/Petya中的流位置漏洞。NotPetya/Petya使用一定数量的扇区,而不是使用要加密的流数据中的字节偏移,这对这种方式的安全性有严重影响。
NotPetya/Petya使用一个修改版本的Salsa20,其是一个流对称加密算法,映射一个256位密钥、一个64位nonce以及一个64位流位置到一个512位的输出。主要的加密功能位于s20_crypt()函数中。由于position参数,这个算法可以在常量时间内在密钥流中寻找任意偏移。这个算法依赖伪随机数生成器(PRNG)来生成用于与明文异或操作的密钥流。下图是Salsa20算法的概述:
Salsa20 PRNG函数(s20_expand()),有下面4个参数:
1.一个立即数(-1nvalid s3ct-id),区别于原始算法中的立即数(expand 32-byte k)
2.一个32字节的随机生成的密钥
3.一个8字节的随机生成的nonce
4.一个8字节整数,表示流位置,但是实际上是要加密的数据的扇区号
s20_crypt()的反汇编代码如下:
正如红色标记所示,用于从磁盘中读取的扇区和s20_crypt()函数中的流偏移是同一个变量。之后扇区号用于作为PRNG函数s20_expand()的输入,如下面反汇编所示:
实际上,使用的是扇区号,而不是实际的流位置(字节),使得NotPetya/Petya能被已知明文攻击。考虑到下面参数的例子:
32字节密钥:
Nonce:
下个加密的扇区号:0x00600061
使用函数号/64来作为参数(如0x18001)调用S20_expand_key()函数。
密钥流如下:
通过下面的公式计算得到密钥流的索引:
因此,真正的XOR流起始于密钥流的索引0x21:
接下来,扇区0x00600062被加密了。因为扇区号只增加1,异或结果为:
因此,如果NotPetya/Petya加密扇区i,64字节密钥状态为FF D7 83 CF […],对于扇区i+1的密钥状态是D7 83 CF B2 […]。这个可以用于针对NotPetya/Petya实施已知明文攻击。背后的原因是一次已知明文攻击可以提取出密钥流。用x表示明文,k表示密钥流,那么E(x) = x ⊕ k。如果x是已知的,密钥能通过再次异或恢复:E(x) ⊕ x = (x ⊕ k) ⊕ x = k。这利用了异或的特性。一旦知道了密钥流,密文的其他部分也能解密。
0x07 利用流位置漏洞
在上节中,我们看到了如果我们只要了底层明文,我们能通过异或简单的计算出原始密钥流。由于密钥流的重复性,这能用于推导出大部分密钥流。首先,需要解密MFT。它几乎是已知明文攻击的一种完美的数据结构。通常,MFT包含连续的MFT记录列表。每个MFT记录有下面的结构:
通常,每个记录是1024字节,等于2个扇区。有了上面的知识,很明显,所有的记录的密钥流字节中有重叠。下图是重叠的地方:
虽然第一个密钥流只有一些重叠,随着索引变大情况会发生变化,可以手机更多的样本。直到MFT末尾,早前的记录会消失,剩余的记录再次积累到相关少量的样本。
有两种不同的方法来利用密钥流:
1.MFT头,假设MFT头的前几个字节是常量
2.Byte histogram,可以计算指定扇区出现明文的可能性
想法是遍历MFT,模拟NotPetya/Petya实现的扇区处理。针对每个MFT记录,收集数据结构中潜在的密钥流。如果找到相同的密钥流,更新可能性。在解析MFT之后,分析数据结构,只有满足确定条件的才置为密钥流。
0x08 MFT头攻击
在大部分记录中,前8个字节是46 49 4C 45 30 00 03 00。在这种假设下,能直接推导密钥流。然而,只有明文的前8个字节恢复了,这种攻击不能解密整个MFT。而且,每个密钥流索引最多可以收集4个样本。
因此,我们进一步使用这种方式来使得可能字节的范围扩大的方法,称为byte histogram。
0x09 Byte Histogram攻击
实际上MFT记录中的大部分元素有固定的大小可以被利用。从这个事实中看,相同数据类型出现于每个记录中相同的偏移位置。为了获得指定偏移的字节的存在性,在MFT记录中每个偏移的histogram能通过从Windows安装中收集的一系列110万的记录计算得到,只选择最多10个不同值的histogram或者明显偏向某一确定值的histogram。
使用这种方法,可以从每个MFT记录中收集更多的密钥流。
0x0A 测试
在VM中的Windows XP中测试。使用NotPetya感染VM,并创建感染的快照,但是还没加密系统。提取MFT,并启动VM,使得NotPetya继续运行。在那之后,再提取加密的MFT。干净的MFT包含27984的记录。
0x0B 解密MFT
我们将两种攻击应用于加密的MFT,并比较。得到下面的结果:
尽管MFT头方法能正确恢复大约56000个密钥流,byte histogram也能恢复大约56000字节。两种攻击都没出错,这对于MFT取证分析来说是个好消息,因为结果数据是可靠的。关于MFT的解密,有532个记录不能使用MFT头方式来解密,只有9个记录不能使用byte histogram来解密。
另外,记录每个位置的可能性,显示了重要的不同点,如下:
结果清楚表明byte histogram方式能针对每个密钥流索引收集更多潜在的明文样本,因此增加了密钥流结果的可靠性。当一个位置不能解析,byte histogram方式表明了可以更细粒度的可选方案,来完成半自动恢复MFT记录。
0x0C 总结
解密NotPetya/Petya加密的计算机是个挑战。本文中,我们展示了解密MFT是可能的,这是解密整个磁盘的前提。在将来我们的目标是解密整个文件系统,它很有挑战。