首先,在继续阅读本文之前,如果大家还没有看过Yifan和我在35C3大会上的演讲的话,建议大家先观看此视频。实际上,我们在准备演讲的时候,想为读者呈现的内容有很多,但是演讲时间有限,所以,有些内容只能忍痛割舍了。例如,本文将要为读者详述的F00D加密处理器中的一个安全漏洞便是其中之一。在深入讨论该主题之前,我们先来介绍一下相关的基础知识。
在PlayStation Vita上,涉及加密处理的设备有两个,并且,这两个设备所执行的功能基本上是相同的。其中,“DMAC5”就是加密设备之一,该设备封装了ARM内核提供的各种加密操作,例如:
DMAC5设备主要由两部分组成:加密设备本身,以及相关的密钥环。其中,密钥环中含有0x20个长度为0x20字节的密钥槽。通常情况下,这个密钥环都是通过F00D进行配置的,并且,其中几个槽是直接通过ARM进行配置的。这样一来,在执行加密操作时,DMAC5就可以通过指定密钥槽来使用这个密钥环了。
除了DMAC5之外,还有另外一种加密设备,我们称之为"Bigmac",需要注意的是,该设备只能通过“F00D”处理器进行访问。实际上,Bigmac和DMAC5设备的接口几乎是一样的,它们主要的区别在于,前者只能通过F00D处理器进行访问。
和DMAC5设备一样,Bigmac设备也有一个相关的密钥环。除了Bigmac的密钥环有0x800个长度为0x20字节的槽,并且可以为每个槽指定特定的功能外,其他方面基本上没有什么区别。在规定具体的功能时,通常涉及以下几个方面:
实际上,大多数密钥槽都没有提供相应的权限,所以,根本就无法使用。此外,对于存放主密钥的密钥槽来说,是不允许进行覆盖的;对于剩下的这些槽来说,则是可配置的,可用于加密。
对于bigmac来说,它的关键用途之一便是“派生”密钥。说得更精确些,就是把经过加密处理的密钥“解密”到一个密钥槽中。这样,索尼既可以更新各种重要的密钥,同时,也不用担心将其暴露在明文中了。要想获取这些派生密钥,要么拿下用于解密这些密钥的主密钥,要么以某种方式读取密钥槽。
下面,让我们回顾一下使用bigmac进行解密的传统做法。为此,我们要指定一些重要的细节信息,具体如下所示:
现在,在正常情况下,为了对密钥进行解密,我们需要提供一个加密的密钥,将目标地址指向一个密钥槽,并将长度设置为0x10字节。所以,为了提取密钥,首先想到的就是修改目标地址,使其指向内存地址。不幸的是,用于派生密钥的所有密钥槽,都被明令禁止在目标地址未指向密钥槽的情况下进行使用。
此外,另外一种容易想到的攻击思路,就是尝试不同的长度。有趣的是,该系统貌似允许使用的长度不是分组密码块长度的倍数。在标准AES加密算法中,要求提供的数据的长度必须是块长度的倍数(在本例中是0x10)。因此,通常可以假设它们会使用填充模式来处理不可分割的块长度,同时,bigmac的输出结果与输入的长度是一致的。
在经过了一番折腾之后,我发现bigmac能够接受长度为4字节的倍数的数据块。当对4字节的数据进行解密时,我发现每次调用引擎时所返回的明文都是不同的。当对长度超过0x10字节的数据进行解密时,则不会出现这种现象。这意味着,我们输入数据中的部分字节会被前面计算的结果所替换掉,也就是进行了混合处理。
了解这些情况之后,我们就可以对这种行为进行模拟。我们不妨假设bigmac只是对剩余数据中的某些字节进行覆盖。当我们提供4个字节的数据00 00 00 00
时,具体如下图所示:
bigmac缓冲区中的剩余数据来自以前的结果。
这样,我们就知道00 00 00 00是用前一个操作中的剩余数据来进行加密的。
那么,我们如何弄清楚剩余的数据是什么呢?我们需要让bigmac内部缓冲区进入已知状态。为此,我们可以使用由0组成的密钥来解密一个完全由0组成的数据块,这样我们就能同时知晓明文、密文和密钥了。
对00数据进行解密,从而用已知的明文和密文来填充bigmac缓冲区。
现在,我们假设bigmac不会清除其内部缓冲区。下面,我们通过用0密钥解密长4字节的00 00 00 00数据来确认这一点:
包含上次处理后的剩余数据的bigmac缓冲区。
正如假设的那样,当我们传入的数据的长度小于加密块的长度时,bigmac只是一味地往里复制数据,而不会清除其内部缓冲区。然后,它会执行相应的操作,并按照我们输入数据的长度,向外拷贝数据。
读到这里,想必大家已经知道如何利用这一点了吧。下面,让我们来模拟密钥派生过程,假设有一个密钥:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D0E 0F
,它是以某种加密形式进行存储的,并且加密密钥是未知的。假设我们需要将这个加密密钥解密到某个密钥槽中。这样的话,Bigmac的内部缓冲区中就会包含相应的解密的密钥。如果我们要求Bigmac使用全0密钥来解密长4字节的数据00 00 00 00
:
部分覆盖可以降低暴力破译密钥数据的难度。
大家可以看到,当前内部缓冲区中的数据为00 00 00 00 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
,这些解密后的数据中,有4字节的0值,其后面的字节为密钥数据。需要注意的是,即使最初的密钥派生操作必须将结果存储到密钥槽中,部分解密也不会给我们带来太大的限制:我们不仅可以控制密钥、查看密文,甚至对明文也具有部分控制能力。
如果该数据段中的4个字节的内容都为0,则仅需要进行2^96次暴力破译就能破解剩余的数据,而不用进行2^128次。很明显,这是一个巨大的进步,但是,所需时间仍然是无法接受的。
为了解决这个问题,我们可以重复上面的过程,不过,这次不是写入4个字节的0值,而是写入12个字节的0值。之后,我们可以通过暴力破译来确定密钥的4个字节的内容。这样的话,我们只需要暴力破译2^32次,对于现代PC来说,这个过程只要几分钟就能搞定。
部分覆盖使得Bigmac缓冲区中只剩下4个字节的密钥数据。
之后,我们可以使用8个字节的0值来重复上面的过程,需要注意的是,此时可以借助于上一步中获得的已知值来确定密钥的另外4个字节的内容。
现在,我们可以再次使用之前的4个字节的0值,并借助暴力破译出来的值来确定另外4字节的内容。
部分覆盖允许我们获得另外32位的密钥数据块。
现在的情况是,我们在经过了3*2^32次尝试后,已经暴力破译了派生密钥的96位数据。为了破译密钥的最后32位内容,最简单的方法就是使用完整的密钥对选定的明文进行解密/加密处理。然后,通过不断尝试对明文进行解密/加密操作,直到得到预期的密文为止,这样就能够暴力破译最后32位密钥。
这样,我们就成功地恢复了派生密钥!
需要注意的是,在进行暴力破译时,可能会出现碰撞现象。实际上,这种情况我已经见识过多次了,不过不要紧,只要不予理会,遍历所有可能即可。
遗憾的是,这种密钥恢复方法无法用于破解“主密钥”。这是因为,主密钥位于一个密钥槽中,并且该密钥槽已经被锁定,所以无法进行覆盖。因此,该方法仅适用于在bigmac设备中完成加密/解密的数据。
这种派生的例子是F00D加载器的AA密钥(有关该密钥的详细信息,请参见我们的演讲视频的结尾部分)。该密钥是通过使用主密钥(插槽0x208)将头部数据的一部分解密到另一个(如0xA号)密钥槽中而得到的。这意味着当索尼改变AA密钥时,我们可以设法执行bootrom中的引导代码,并通过该攻击来获取新密钥。这种方式在Bootrom之外是不可行的,因为索尼在引导后会禁用0x208号主密钥。
这是一个有趣的漏洞,实际上,只要禁止向该设备传递非块对齐的长度值的话,就可以轻松修复该漏洞。另一个简单的解决方案是,在加密操作结束时清除内部缓冲区。由于该漏洞存在于硬件中,因此,攻击者总能够利用它来获取派生密钥。这样的话,系统中唯一可用于加密/解密的安全组件就只有主密钥了。
最后,感谢@qlutoo和proxima在这方面所做的工作,并祝大家阅读愉快。
本文翻译自lolhax.org,原文链接。
打赏我,让我更有动力~
© 2016 - 2024 掌控者 All Rights Reserved.