具有物理访问权限的攻击者能够攻击具有DMA直接内存访问权限的许多完全修补的计算机上的固件。一旦在UEFI/EFI运行时服务中获得代码执行,就可以使用这个立足点来控制正在运行的Linux系统。
Linux 4.8内核完全随机化内核的物理内存位置。在内存充足的计算机上,内核很可能被随机分配到4GB以上。这意味着,只有32位寻址(4GB)的DMA攻击硬件(如PCILeech)无法直接到达Linux内核。
由于EFI运行时服务通常位于4GB以下,因此它们提供了一种进入高内存EFI引导系统上的Linux的方法。
请参阅下面的视频,以获取攻击的外观示例。
什么是EFI运行时服务?PC上的UEFI,Mac上的EFI,是现代的BIOS。UEFI是统一可扩展固件接口的缩写。UEFI负责检测硬件和配置设备,以便将控制权移交给应加载的操作系统。
UEFI的两个主要组件是引导服务和运行时服务。在操作系统的早期调用ExitBootServices。之后,启动服务不再使用。
即使在加载并运行操作系统之后,EFI运行时服务也会继续存在。它们提供了操作系统可以调用的各种功能。UEFI规范指定了一组固定的函数,运行时服务应该向操作系统提供这些函数,如下所示。
最初,运行时服务功能的位置通过运行时服务表传递给操作系统。每个函数的物理地址是64位/8字节长,并以小尾数形式存储在内存中。不过,目前为止,所有系统的内存地址都在32位范围内。物理内存地址似乎也完全是静态的,即在重新启动之间不会发生随机化。
Linux稍后会将这些地址映射到虚拟地址空间,并用相应的虚拟地址覆盖表中的初始地址。下面并排显示了初始表和由Linux修改的表的示例。
如果我们使用DMA用自己的代码覆盖EFI运行时服务,该怎么办?或者,如果我们覆盖EFI Runtime Services表中的函数指针以指向先前插入的攻击代码,会更好吗?
当操作系统调用EFI运行时服务时(例如当它读取EFI变量时),代码在目标系统上执行。代码执行是在一个特殊的上下文中获得的,在这个上下文中,较低内存中的页在虚拟地址和物理地址之间以1:1的比例映射。Linux内核可以在其正常的虚拟地址访问。在ring0/supervisor模式下执行。
但是,不可能调用Linux内核中的所有函数。由于Linux为运行时服务设置了特殊的EFI上下文,一些函数将失败。解决方法是在Linux内核中修补一个“随机”钩子函数。当一个“正常”内核线程命中钩子时,将获得“正常”内核代码执行。。。
目标系统已经在一台8GB内存的联想T430和一台32GB内存的英特尔NUC骷髅峡谷上进行了测试。ExpressCard插槽用于在NUC上的T430上获得DMA访问。在“BIOS”设置中,Thunderbolt模式被设置为“Legacy”-通过Thunderbolt3/USB-C启用DMA访问。
T430非常直行。只需插入PCILeech设备并发出PCILeech.exe kmdload-kmd linux_x64_efi命令。PCILeech将搜索EFI Runtime Services表并挂接它。系统将要求用户执行一些操作,从而调用运行时服务-触发钩子。
核反应堆是不同的。PCILeech将在找到目标之前遇到无法读取的内存,并将失败。幸运的是,运行时服务表的位置是静态的,在重新启动之间不会改变。这适用于所有测试系统。找到位置的最简单方法是在同一个系统或类似系统上以EFI模式通过USB引导live Linux。启动后,发出命令cat/sys/firmware/efi/runtime以查看运行时服务表的物理地址。一旦地址被称为0x3b294e18,我们就可以发出命令pcileech.exe kmdload-kmd linux_x64_efi-min 0x3b294000-max 0x3b295000。
NUC设置。在表面攻击计算机上显示NUC EFI运行时服务表。
注意:要亲自尝试,请查看Github的PCILeech。
从攻击总是有效的角度来看,攻击不是100%稳定的。有时搜索运行时服务表会失败,有时很难触发对运行时服务的调用。不过,目标系统崩溃很少见。
结论基于Slinux4.8的操作系统,如Ubuntu16.10,不再完全受PCILeech的保护。
即使您的笔记本电脑可能没有ExpressCard插槽,但如果拧下后盖,它将有一个用于WiFi卡的迷你PCIe或M.2插槽。。。
操作系统无法通过将内存中所有感兴趣的内容置于32位/4GB地址限制之上来保护自己免受DMA攻击。32位硬件(如PCILeech)可能会攻击4GB以下的固件代码和数据。其他恶意硬件也可能进入64位地址空间。
操作系统应该通过启用VT-d来保护自身和固件(如运行时服务)免受恶意设备的攻击。