作者:A. Moghimi, J. Wichelmann, T. Eisenbarth, and B. Sunar.
发布:International Journal of Parallel Programming
时间:Aug 2019.
笔记:
缓存定时攻击
1、攻击原理
共享缓存存在定时侧信道的风险(例如在处理并发任务的资源争用产生的时延)
(1)之前的工作
①CacheBleed
1)针对缓存组冲突
②MemJam
1)利用写后内存读取的错误依赖(绕过恒定时间技术)
2)假设:攻击者能够与受害者在同一物理核心内的一个逻辑处理器对上协同定位。在加密攻击中,攻击者可以测量受害者加密的时间。攻击者进一步知道受害者使用的是哪种加密实现,但她不需要知道受害者的二进制或s-Box表的偏移量。
2、解决方法
最简单的方法是最小化查找表的内存占用来减少时间差异
例如,在高级加密标准(AES)中使用单个8位S-Box而不是T-Tables使得在嘈杂的环境中对AES的缓存攻击效率低下,因为对手只能区分4条不同缓存线之间的访问。
(1)恒定时间(即控制时间差异)
①原理
1)设计密码操作的存储器访问以遵循统一的密钥无关模式
2)采用小表,并在每次查找时访问所有缓存行
②缺陷
取决于底层。MemJam指出处理器不断发展,每一个新版本都会悄悄推出新的微体系结构功能,而这种微妙功能的多样性使得全面评估变得不可能。一个很好的例子是对OpenSSL RSA分散收集实现的缓存组冲突攻击:它表明,具有缓存内级别分辨率的对手可以成功绕过依赖于缓存线粒度的恒定时间技术。
摘要:
缓存攻击利用加密实现的内存访问模式。
恒定时间实现技术已成为对抗缓存定时攻击的不可或缺的工具。这些技术设计密码操作的存储器访问,以遵循统一的密钥无关模式。
然而,恒定时间行为取决于底层架构,底层架构可能非常复杂,并且通常包含未发布的功能。CacheBleed攻击针对缓存组冲突,从而使微体系结构侧通道对手只能观察到具有缓存线粒度的内存的假设无效。
在这项工作中,我们提出了MemJam,这是一种侧通道攻击,利用写后内存读取的错误依赖性,并提供高质量的缓存内级定时通道。作为概念验证,我们演示了针对AES的恒定时间实现的首次密钥恢复攻击,以及当前“英特尔集成性能原件”(英特尔IPP)加密库中具有缓存保护的SM4实现。此外,我们通过在使用上述AES的恒定时间实现执行加密的飞地上再现AES密钥恢复结果,演示了对SGX的第一次缓存内级定时攻击。我们的结果表明,我们不仅可以使用这个侧通道来有效地攻击依赖内存的密码操作,还可以绕过所提出的保护措施。与仅限于旧代处理器的CacheBleed相比,MemJam是第一个适用于所有主要英特尔处理器(包括支持SGX扩展的最新一代处理器)的缓存内级别攻击。
引言:
在密码实现中,定时信道可以通过依赖于密钥的操作引入,这可以被本地或远程对手利用。现代微体系结构很复杂,支持各种共享资源,操作系统最大限度地实现了并发任务之间的资源共享。从安全角度来看,具有不同权限的并发任务共享相同的硬件资源,这些资源可能会暴露出可利用的定时通道。利用微体系结构时序通道的一个典型模型是间谍进程引起与受害者进程的资源争用,并测量其自身或受害者操作的时序。观察到的定时行为为对手提供了受害者资源使用模式的有力证据,从而泄露了关键的运行时数据。在共享资源中,对缓存的攻击受到了极大的关注,其实用性已在云计算等场景中得到证明。缓存攻击的一个明显特征是能够跟踪高时间和空间分辨率。因此,他们擅长利用具有秘密相关存储器访问的密码实现。这种易受攻击的实现的例子包括使用S-Box表和模幂运算的有效实现。
【S-box,即“substitution box”, 是密码学领域的一个基本组件,其功能是实现数据的非线性置换。】
依赖密钥的缓存活动的弱点促使研究人员和从业者保护密码实现免受缓存攻击。最简单的方法是最小化查找表的内存占用。在高级加密标准(AES)中使用单个8位S-Box而不是T-Tables使得在嘈杂的环境中对AES的缓存攻击效率低下,因为对手只能区分4条不同缓存线之间的访问。将小表与缓存状态规范化相结合,即在每次操作之前将所有表条目加载到缓存中,可以在异步模式下击败缓存攻击,在异步模式中,对手每次操作只能执行一次观察。更先进的侧通道,如线程调度程序的利用、对中断执行的英特尔软件保护扩展(SGX)的缓存攻击、性能下降和其他微体系结构资源的泄漏提醒我们恒定时间软件实现的重要性。实现恒定时间内存行为的一种方法是采用小表,并在每次查找时访问所有缓存行。开销是有限的,并且通过我们在现代处理器中可以实现的并行性来最小化。一些公共密码方案采用的另一种恒定时间方法是在存储器中交错乘法器,称为分散-聚集技术。
恒定时间实现有效地消除了利用明显的密钥相关泄漏的第一代定时攻击。普遍的观点是,performance penalty是唯一的不利因素,一旦paid,就没有必要再担心了。然而,这与现实相去甚远,恒定时间实现实际上可能会给人一种虚假的安全感。一个经常被忽视的事实是,恒定时间实现和相关保护是相对于底层硬件的[25]。事实上,有一些主要障碍阻碍我们获得真正的持续时间行为。处理器不断发展,每一个新版本都会悄悄推出新的微体系结构功能,而这种微妙功能的多样性使得全面评估变得不可能。一个很好的例子是对OpenSSL RSA分散收集实现的缓存组冲突攻击:它表明,具有缓存内级别分辨率的对手可以成功绕过依赖于缓存线粒度的恒定时间技术[26]。因此,在下一个处理器版本中,看似完美的恒定时间实现变得不安全——或者更糟的是,可能会发现未识别的行为,从而使早期的假设无效。
贡献
我们提出了一种名为MemJam的攻击,该攻击利用了写后读取内存的错误依赖性,并在常规和SGX环境中展示了针对两种不同加密实现的密钥恢复,这两种加密实现可以安全地抵御缓存攻击。总之:
伪依赖性攻击
对写入后读取的内存的伪依赖性进行的侧通道攻击。我们展示了如何显著降低受害者对特定内存块的访问速度,以及如何利用这种读取延迟来恢复受害者内存访问的低地址位。
对受保护的AES和SM4的攻击
利用针对缓存攻击进行保护的AES与SM4实现上的缓存内级别信息进行攻击。这些实现是从“英特尔集成性能原件”(英特尔IPP)中选择的,该原件针对安全性和速度进行了优化。
对SGX Enclave的攻击
第一次对SGX Enclave的缓存内级攻击,在连续时间AES实现中得到密钥恢复结果的支持。上述AES的恒定时间实现是SGX SDK源代码的一部分。
绕过保护
绕过显著的保护,如基于恒定时间技术[7,24]、静态和运行时分析[27,28]以及缓存架构[29,30,31,32]的建议。
我们唯一的假设是,攻击者能够与受害者在同一物理核心内的一个逻辑处理器对上协同定位。在加密攻击中,攻击者可以测量受害者加密的时间。攻击者进一步知道受害者使用的是哪种加密实现,但她不需要知道受害者的二进制或s-Box表的偏移量。
相关工作
侧信道
包括功率、电磁和定时信道在内的侧信道已经研究了几十年[1,33,34]。可以通过处理器高速缓存构建定时侧信道,以针对诸如RSA[15]、ECDSA[14]、ElGamal[9]、DES[13]和AES[2,8]之类的加密操作执行密钥恢复攻击。在多处理器系统上,攻击在共享LLC(L3,三级缓存)上——所有核心之间的共享资源——即使攻击者和受害者位于不同的核心中,也能表现良好[8]。Flush+Road、Prime+Probe、Evict+Road和Flush+Flush是针对不同对抗场景提出的一些攻击方法[2,11,12]。性能降级攻击可以提高信道分辨率[19,21]。LLC攻击在云中非常实用,攻击者可以识别特定受害者的位置[5,9]。尽管LLC攻击适用,但对核心专用资源(如L1缓存)的攻击同样重要[23,35]。系统级对抗性场景中对SGX的攻击是值得注意的例子[20,36]。还有其他共享资源,可用于构建定时信道[37]。如果分支已被受害进程占用,则分支目标缓冲区(BTB)的利用会泄漏[22,23,36]。处理器中的逻辑单元可能会泄露有关算术运算的信息[38,39]。CacheBleed提出缓存组冲突和读取后内存写入的错误依赖性作为具有缓存内粒度的侧通道[26]。然而,当前的英特尔处理器上不存在缓存组冲突泄漏,我们验证了作者的说法,即所提出的读后写错误依赖侧通道不允许有效的攻击。
防御
已经提出了软件和硬件策略,如替代查找表、数据独立的内存访问模式、静态或禁用缓存以及缓存状态规范化,以防御缓存攻击[7]。ScatterGather技术已被RSA和ECC实现所采用[24]。特别是,有人提出在AES的S盒表中引入冗余和随机性[18]。提出了一种自定义内存管理器[40]、宽松包含缓存[31]以及基于缓存分配技术(CAT)的解决方案,如Catalyst[29]和vCat[32],以抵御LLC争用。Sanctum[30]和Ozone[41]是针对缓存攻击的新处理器设计。还提出了使用性能计数器的基于检测的对策,可用于检测云环境中的缓存攻击[27,42]。MASCAT[28]被提出用代码分析技术来阻止缓存攻击。CachD[43]检测生产软件中潜在的缓存泄漏。尽管如此,这些建议假设对手无法区分缓存行内的访问。也就是说,具有缓存线内粒度的攻击被认为超出了范围。Doychev等人提出了唯一一种将全地址位视为泄漏模型的软件泄漏检测器[44]。
背景
多任务
【并发和并行它们虽然都说是"多个进程同时运行",但是它们的"同时"不是一个概念。并行的"同时"是同一时刻可以多个进程在运行(处于running),并发的"同时"是经过上下文快速切换,使得看上去多个进程同时都在运行的现象,是一种OS欺骗用户的现象。】
存储器管理子系统在所有并发任务中共享动态随机存取存储器(DRAM),其中为每个任务分配对物理存储器透明的虚拟存储器区域。每个任务都能够使用其整个虚拟地址空间,而不会干扰其他任务的内存访问。内存分配是在页面中执行的,每个虚拟内存页面都可以存储在具有虚拟到物理页面映射的DRAM页面中。逻辑处理器也在这些任务之间共享,并且每个任务逻辑处理器一次执行来自一个任务的指令,并切换到另一个任务。存储器写入和读取指令使用虚拟地址,并且虚拟地址被转换为相应的物理地址以执行存储器操作。操作系统负责页面目录管理和虚拟页面分配。OS通过执行昂贵的页面遍历来帮助处理器执行虚拟到物理地址的转换。处理器将地址转换结果保存在称为转换后备缓冲区(TLB)的存储器中,以避免操作系统引入的软件开销。英特尔微体系结构遵循多级流水线,并采用不同的优化技术,以最大限度地提高流水线阶段的并行性和多任务性[45]。在这些技术中,超线程允许每个核心运行多个并发线程,并且每个线程共享所有核心专用资源。因此,如果一个资源被一个线程占用,其他线程可能会占用剩余的可用资源。超线程被抽象为软件堆栈:操作系统和应用程序与逻辑处理器交互。
缓存
【计算机将数据从主存读入Cache时,是把要读取数据附近的一部分数据都读取进来
这样一次读取的一组数据就叫做CacheLine,每一级缓存中都能放很多的CacheLine。cache分成多个组,每个组分成多个行,linesize是cache的基本单位,从主存向cache迁移数据都是按照linesize为单位替换的。】
与内部CPU组件相比,DRAM存储器速度较慢。现代微体系结构利用高速缓冲存储器的层次结构来填补速度差距。英特尔处理器有两级核心专用缓存(L1、L2),以及在所有核心之间共享的末级缓存(LLC)。与下一级缓存相比,缓存离处理器越近,速度就越快,但也越小。缓存被组织成不同的集合,每个集合可以存储一定数量的缓存行。高速缓存行大小为64字节,是CPU之外所有内存操作的块大小。每个高速缓存行的物理地址的高位用于确定哪个集合存储/加载高速缓存行。当处理器尝试访问缓存行时,无论其在相关缓存集中是否存在,都会发生缓存命中或未命中。如果发生缓存未命中,则CacheLine将存储到所有3级缓存和已确定的集合中。当缓存中存在CacheLine时,从同一地址重新加载会快得多。在多核系统中,处理器必须在所有级别之间保持缓存的一致性。在英特尔体系结构中,CacheLine遵循写回策略,即如果一级缓存中的数据被覆盖,则所有其他级别都将更新。LLC包括L2和L1高速缓存,这意味着如果LLC中的高速缓存线被逐出,相应的L1和L2高速缓存线也将被逐出[45]。这些策略有助于避免一个处理器读取由另一个处理器变异的无效数据时出现过时的缓存数据。
一级缓存瓶颈
一级缓存端口的带宽有限,同时进行的访问将相互阻塞。这个瓶颈在超标量多处理器系统中是至关重要的。旧一代处理器采用了多个存储体作为解决该问题的方法[46],其中每个存储体可以独立操作,一次处理一个请求。虽然这部分解决了带宽限制,它造成了缓存组冲突现象,同时访问同一个缓存组将被阻止。Intel解决了Haswell一代的缓存组冲突问题[45]。各种资源中提到的另一个瓶颈是由于具有相同缓存集和偏移量的存储器地址的错误依赖性[45,46]。地址是4kB的倍数的同时读写是不可能的,而且它们会相互停止。处理器无法根据虚拟地址确定依赖关系,并且具有相同最后12位的地址有机会映射到相同的物理地址。这种同时访问可以发生在两个逻辑处理器之间和/或无序执行期间,其中存储器写入/读取可能依赖于具有相同最后12位地址的存储器读取/写入。这样的依赖关系无法即时确定,因此会导致延迟。
缓存攻击
缓存攻击可能被对手利用,因为他们与善意的用户共享系统缓存。在对手可以与同一核心上的受害者共存的场景中,她可以攻击核心私有资源,如L1缓存,例如OS对手[20,36]。在云环境中,虚拟化平台允许将逻辑处理器共享到不同的虚拟机;然而,对共享LLC的攻击具有更高的影响,因为LLC是在所有核心中共享的。在缓存定时攻击中,攻击者要么测量受害者操作的定时,例如Evict+Time[2],要么测量他自己的内存访问的定时,如Prime+Probe[8]。攻击者需要能够访问精确的时间资源,如RDTSC指令。在基本形式中,每个操作通过一次观察来执行攻击。在某些情况下,可以通过中断受害者并收集有关中间内存状态的信息来改进这些攻击。利用缓存组冲突的侧信道攻击依赖于同步资源争用。CacheBleed方法与Prime+Probe有点相似,攻击者在Prime+Pprobe中执行重复操作,并测量自己的访问时间[26]。在缓存组冲突攻击中,对手重复对同一个缓存组执行同时读取并测量其完成时间。位于同一位置的逻辑处理器上的受害者访问同一个缓存组会导致攻击者的内存读取延迟。
MemJam: 读后写攻击
MemJam利用了错误的依赖关系。当一条指令引用前一条指令的数据时,就会发生数据依赖性。在流水线设计中,如果前一条指令没有完成,依赖关系可能会导致危险和流水线停滞。在某些情况下会出现错误的依赖关系,即即使没有真正的依赖关系管道也会暂停。错误依赖性的原因是寄存器重用和算术逻辑单元(ALU)的地址空间有限。错误的依赖关系会降低指令级并行性并导致开销。处理器通过寄存器重命名方法消除了由于寄存器重用而产生的错误依赖关系。然而,在软件优化过程中还存在其他需要解决的错误依赖性[45,47]。
在这项工作中,我们重点关注一个被称为4K混叠的关键错误依赖,其中地址空间中相距4K的倍数的数据被视为依赖。4k混叠是由于一级缓存的虚拟寻址而发生的,在一级缓存中,数据使用虚拟地址进行访问,但使用物理地址进行标记和存储。多个虚拟地址可以引用具有相同物理地址的相同数据,并且确定并发存储器访问的依赖性需要虚拟地址转换。物理地址和虚拟地址共享最后12位,任何地址在最后12位不同(即距离不是4k)的数据访问都不能具有相关性。对于剩下的极少数情况,在解决依赖关系之前需要进行地址转换,这会导致延迟。请注意,潜在依赖关系的粒度,即两个地址是否被视为“相同”,也取决于微体系结构,因为依赖关系可能发生在字或缓存线粒度(即分别忽略地址的最后2位或最后6位)。由于攻击者可以通过将自己地址的最后12位与受害者进程中的安全关键数据相匹配,故意处理依赖错误的数据,因此可以利用4K混叠导致的这些罕见的错误依赖性来攻击内存。
4K混叠在许多地方都被提到是所有主要英特尔处理器上存在的优化问题[45,46]。我们验证了Yarom等人的结果。[26],这是唯一一项关于伪依赖性的安全相关工作,它利用了读后写依赖性。读取后写入暂停导致的定时泄漏不足以用于任何加密攻击。由于读-写的错误依赖性,MemJam利用了不同的通道,这会导致更高的延迟,因此很容易观察到。《英特尔优化手册》在各个章节[45]中重点介绍了读写性能开销。
当存储器write后面紧跟着一个read,它会导致重新发出read,并可能导致5个周期的惩罚【LD_BLOCKS_PPARTIAL.ADDRESS_ALIAS性能监视单元(PMU)事件统计阻止读取的次数】。写入操作在存储绑定类别下处理。与负载界限相反,自上而下的微体系结构分析方法(TMAM)【自上而下的特征化是基于事件的度量的分层组织,用于确定应用程序中的主要性能瓶颈。】将存储界限报告为周期的一部分,执行端口利用率低,性能影响小。各节中的这些描述强调了写后读取暂停被认为比读后写入暂停更重要。
内存依赖性模糊测试
我们进行了一组实验来评估两个逻辑处理器之间的内存依赖行为。在这些实验中,我们让线程A和B在相同的物理内核上运行,但在不同的逻辑处理器上运行,如图1所示。两个线程都执行内存操作;只有线程B测量其定时,从而测量引入的错误依赖性的定时影响。
读取后读取(RaR):在第一个实验中,两个逻辑线程A和B从同一共享缓存中读取,可能会相互阻塞。这个实验可以揭示缓存组冲突,正如CacheBleed[26]所使用的那样。B使用清单1执行读取测量,而A不断地从不同的内存偏移中读取,并试图引入冲突。A从三种不同类型的偏移中读取:(1)与B不同的缓存线,(2)相同的缓存线但与B不同偏移,以及(3)与B相同的缓存行和相同偏移。如图所示,图2a中验证第7代CPU上没有缓存组冲突的三种情况的直方图之间没有明显差异。
读取后写入(WaR):关于读取后写入的错误依赖性的第二个实验的直方图结果为如图2b所示,其中缓存行粒度很明显。线程A不断地从不同类型的内存偏移中读取,而线程B使用清单2来执行写测量。线程A和B之间的冲突缓存线(蓝色)和冲突偏移量(红色)的标准偏差与没有缓存线冲突的绿色条不同。这显示了高容量缓存的细粒度行为,但冲突行和偏移之间的细微差异验证了先前的结果,即偏移依赖性较弱[26]。
写后读(RaW):图2c显示了一个测量写后读的错误依赖性的实验,其中线程A不断地向不同的内存偏移写入。线程B使用清单1来执行读取测量。访问三种不同类型的偏移是可以清楚区分的。冲突的缓存线访问(蓝色)与非冲突的访问(绿色)是可区分的。更重要的是,对相同偏移量(红色)的冲突访问也与冲突缓存线访问不同,从而产生具有缓存线内粒度的侧通道。如果访问了相同的缓存线,则平均会有2个周期的惩罚,如果访问了同一偏移量,则会有10个周期的处罚。请注意,我们平台中的单词偏移量具有4字节粒度。从对抗性的角度来看,这意味着对手了解受害者存储器访问的第2-11位,其中4位(第2-5位)与缓存线内分辨率有关,因此超出了已知存在于第6代和第7代Intel处理器上的任何其他微体系结构侧通道(图5)。
弱写后读(RawW):在这个关于写后读冲突的实验中,我们对冲突线程采用了一种不那么贪婪的策略。A不是不断地向相同的偏移量写入,而是执行向相同偏移量的写入指令,其中一些间隙由其他存储器访问和指令填充。如图3所示,该通道的效率显著降低。这告诉我们,在没有额外指令的情况下,对同一偏移量进行恒定写入会更有效地造成读取访问惩罚。在这方面,我们将在攻击中使用清单3来实现最大的冲突。
写后读取延迟:在上一个实验中,我们测试了不同数量的冲突读取的执行延迟。我们创建了一个代码存根,其中包括64条内存读取指令和内存读取之间的随机指令组合,以创建更真实的计算。选择组合的方式是为了避免意外的暂停,并保持所有读取操作的并行性。我们测量当A写入冲突的偏移量时,此计算在B上的执行时间。首先,我们测量了对地址进行64次内存读取而没有冲突的计算。我们随机生成的代码存根平均需要210个周期才能执行。如图4所示,在实验的每一步中,我们都会更改一些内存偏移量,使其具有与A的冲突写入偏移量相同的最后12位地址。我们观察到,通过增加冲突读取的数量,读取访问的延迟会增加。图4显示了一些实验的结果。在所有这些方法中,B的总体执行时间在很大程度上取决于冲突读取的数量。因此,我们可以使用RaW依赖性来使用所选目标存储器地址的位2-11引入强时序行为。
(待更新)