Meltdown: Reading Kernel Memory from User Space
-
摘要:
- 计算机系统的安全性从根本上依赖于内存隔离,通过标记地址是否可用,隔离用户地址空间和内核地址空间
- 熔断利用现代处理器上的乱序执行产生的一些微架构上的副作用,能够读取任意的内存空间
- 熔断独立于OS,并且不依赖于软件漏洞,直接破坏了地址空间隔离和半虚拟化环境提供的所有安全保证,所有在此基础上的安全机制也都被破坏
- 论文证明了KAISER建立的防御机制能够阻碍熔断
-
介绍:
- 现代处理器中,内核进程和用户进程之间的隔离通常使用一个supervision bit来实现,通过该位来决定是否可以访问内核的地址空间。当进入内核代码是,该位设置为1,表示可以访问内核数据,在切换到用户进程时,则清除该位。在这种方式中,内核空间可以被简单的映射到每个进程的地址空间,同时在模式切换(用户到内核)时,内存的映射方式并不会改变
- 熔断是一种新的攻击方式,提供了一种简单的方式,允许用户进程读取机器的整个内核内存,包括映射到内核区域的所有物理内存,直接打破了内存隔离。攻击有效的根本原因就是乱序执行带来的副作用
- 引发安全的一个根本现象:乱序执行的CPU允许非特权进程将数据从特权地址数据加载到CPU的寄存器,同时允许该寄存器的值被进一步的使用,例如作为索引访问数组。尽管CPU最终发现发生这次数据访问的指令不该被执行,但是这期间执行指令的某些影响却没有被完全消除
- 论文发现乱序执行过程中的存储查找会影响cache的状态,并且不会被回退,因此可以利用这种状态改变实现cache的侧信道攻击
- 整体攻击过程的描述:攻击者通过在乱序执行流中读取特权内存数据,并通过微体系结构的隐蔽信道(cache的flush+reload)将数据传输到外界。在接收端,重构寄存器的值。从而实现转存整个内核空间的数据
- KAISER最初是为了防止针对KASLR的侧信道攻击而开发的,但是也能够保护系统避免熔断攻击
-
地址空间
-
每个进程有自己的虚拟地址空间,也只能访问自己空间内的数据。每个虚拟地址空间被分为一个用户和一个内核部分。
-
CPU只能够在特权模式运行的情况下,才能够访问内核地址空间。(在转换表中的user-accessible属性可以实现这个功能)
-
内核地址空间不仅有内核自己使用的内存映射,而且还需要对用户页面进行操作,例如使用数据填充用户页面,因此整个物理内存通常会被映射到内核地址空间中。
-
在linux和OS X中,该映射为直接映射。Windows通过维护多个分页池(可以映射)、非分页池(常驻内存)、系统缓存等结构来管理内核空间,虽然没有直接将所有物理内存映射到内核空间,内核空间中仍会包含物理内存的很大一部分内容。
-
-
cache攻击
- 主要利用cache在访问时(hit/miss)的时间差异来进行攻击。
- 攻击手段:evict+time,prime+probe,flush+reload
- Flush+reload攻击的粒度是cache line,主要利用LLC的共享性。攻击者使用clflush指令频繁的刷新目标内存位置(所有cache层次都被清除)。通过测量重新加载数据所需要的时间,判断数据是否同时被另一个进程加载到缓存中。
-
一个简单的示例:
- 理论上,代码中的access行为永远都不会执行,由于异常处理,但是乱序执行可能会已经执行了访问数据的执行,因为它不存在对异常的依赖
- access指令尽管在异常发生后会进行回退,但是cache的状态这个时候可能就已经发生了变化,之后就可以利用cache的侧信道攻击,得到data的实际信息
- 当数据data乘以4096,则访问的数据将会分散到整个数组中,距离为4KB,此时数据到内存页的映射即为单射。此时如果页内的cache line如果被cached,则数据的具体值将可以得到。此时预取程序由于不能够跨越页面边界访问数据,所以此时不会出现预取的影响
//该异常为用户程序访问内核空间的地址,取到的数据将会是data raise_exception(); //the line below is never reached access(probe_array[data*4096]);
-
x86平台上的熔断实现的核心指令序列
;rcx=kernel address ;rbx=probe array retry: ;读取内核数据,利用CPU的乱序执行特性,在非法内存访问和异常发生之间的短暂空挡中执行指令 mov al,byte [rcx] ;data*4KB,进行散列映射 shl rax,0xc ;重试逻辑:如果读取0,重试(防止噪声偏差)