当前位置: 首页 > 科技观察

Windows上地址空间布局随机化的防御机制分析(上)

时间:2023-03-14 10:32:36 科技观察

Addressspacelayoutrandomization(ASLR,又称地址空间布局随机化,addressspacelayoutrandomization)是一种防御内存损坏的计算机安全技术漏洞被利用。具体来说,地址空间配置随机加载是一种针对缓冲区溢出的安全保护技术。通过对堆、栈、共享库映射等线性区域进行随机布局,攻击者难以预知目标地址,从而防止攻击。它是一种直接定位攻击代码所在位置以防止溢出攻击的技术。所以对于攻击者来说,绕过地址空间布局随机化的防御系统是他们执行所有内存攻击漏洞的前提。这意味着打破地址空间布局随机化的研究课题也是一个比较热门的领域。另外,据我推测,以后对地址空间布局的攻击也会变得非常复杂。本文介绍了有关地址空间布局的一些基本事实,重点是Windows实现。除了描述地址空间布局如何有助于改善安全状况外,我们还旨在为防御者提供有关如何提高其程序安全性的建议,并为研究人员提供更多有关地址空间布局如何工作的信息和对该想法的见解调查其局限性。当程序错误地将攻击者控制的数据写入目标内存区域或目标内存范围之外时,就会发生内存损坏漏洞。这可能会使程序崩溃,或者更糟的是,使攻击者可以完全控制系统。尽管已努力缓解内存损坏漏洞,但该攻击已困扰苹果、谷歌和微软等大公司数十年。由于这些漏洞很难发现,一旦出现就会危及整个操作系统,安全专家设计了漏洞安全保护机制来防止程序被利用。另外,利用该漏洞安全保护机制,如果出现内存损坏漏洞,可以限制造成的损失。本文介绍了一种称为“银弹”的方法,防御者使用这种方法可以使利用漏洞变得非常困难。安全语言修复或代码重写所需的时间。不幸的是,没有任何防御是完美的,但地址空间布局随机化是可用的最佳缓解措施之一。地址空间布局随机化的工作方式打破了开发人员关于运行时程序和库在内存中的位置的假设。一个常见的例子是在面向返回编程(ROP)中使用的小工具的位置,该位置通常用于防御数据执行保护(DEP)。地址空间布局随机化混合了易受攻击进程的地址空间(主程序、其动态库、堆栈和堆、内存映射文件等),因此当时的漏洞利用负载的布局必须专门针对受害者进程的地址空间。如果攻击者编写了一个蠕虫病毒,将其带有硬编码内存地址的内存破坏漏洞随机发送到它能找到的每个设备,则传播注定会失败。只要为目标进程启用了地址空间布局随机化,漏洞利用的内存偏移量就会与地址空间布局随机化选择的内存偏移量不同。这允许易受攻击的程序崩溃而不是被利用。地址空间布局随机化是在WindowsVista中引入的,这意味着Vista之前的版本没有ASLR。更糟糕的是,他们竭尽全力在所有进程和设备之间保持一致的地址空间根本没有地址空间布局随机化,而是将DLL简单地加载到当时方便的任何位置,一个可预测但不可预测的位置两个进程或设备之间必须相同。不幸的是,这些旧版本的Windows未能实现我们所说的“地址空间布局一致性”。下表显示了WindowsXPServicePack3的一些核心DLL的“首选基地址”。WindowsDLL包含一个首选基地址,如果没有地址空间布局随机化,则尽可能使用该地址。创建进程时,Vista之前的Windows版本会在可能的情况下将程序所需的每个DLL加载到其首选基地址。例如,如果攻击者在ntdll中的0x7c90beef找到一个有用的ROP小工具,攻击者可以假设它将在该地址保持可用,直到以后的服务包或安全补丁需要重组DLL。这意味着对Vista之前的Windows的攻击可以将来自普通DLL的ROP小工具链接在一起以禁用DEP,这是这些版本中唯一的内存损坏保护机制。DataExecutionPrevention(DEP),DataExecutionPrevention,是Windows上的一种可执行空间保护策略,可以对内存进行额外的检查,以防止数据页(如默认堆页、各种堆栈页、内存池页)执行恶意代码以防止缓冲区溢出攻击。缓冲区溢出攻击的根源在于计算机没有明确区分数据和代码。当程序溢出并成功转移到数据页的shellcode时,就会成功执行恶意指令。DEP的基本原理是将数据所在的内存页标记为不可执行。当程序溢出并成功转移到shellcode尝试执行数据页上的指令时,CPU会抛出异常,而不是执行恶意指令。Microsoft从WindowsXPSP2开始就提供了DEP支持。操作系统通过设置内存页表(PageTable)中内存页的NX/XD属性标志来表示代码不能从内存中执行。当该标志设置为0时,表示允许该页执行指令,设置为1时,表示不允许该页执行指令。为什么Windows需要支持首选基地址?因为Windowsdll的设计与ELF共享库等其他设计之间存在性能折衷。由于WindowsDLL不在单独的位置,尤其是在32位计算机上,如果WindowsDLL代码需要引用全局变量,则该变量的运行时地址将被硬编码到计算机代码中。如果DLL在与预期不同的地址加载,将执行重定位以修复此类硬编码引用。如果将DLL加载为首选基地址,则不需要重定位,并且DLL的代码可以直接从文件系统映射到内存中。将DLL文件直接映射到内存有利于提高性能,因为它避免了在需要之前将DLL的任何页面读入物理内存。更喜欢基地址的一个更好的理由是确保内存中只需要一个DLL副本,没有它们,那么运行的三个程序将共享一个公共DLL,但是每个程序在不同的地址加载该DLL,然后在内存中有将是DLL的三个副本,每个都重新定位到不同的库。这否定了使用共享库的主要好处。除了安全优势之外,地址空间布局随机化通过确保加载的DLL的地址空间不重叠并且只有一个DLL副本被加载到内存中,以更优雅的方式完成相同的任务。由于地址空间布局随机化比静态分配的首选加载地址在避免地址空间重叠方面做得更好,因此手动分配首选基地址不会在支持地址空间布局随机化的操作系统上提供优化,并且在开发生命周期内不会提供任何优化.循环中不能再使用。总结一下:1.WindowsXP和WindowsServer2003及更早版本不支持地址空间布局随机化。显然,这些版本已经多年不受支持,早就应该停止在生产环境中使用了。他们可能没有意识到,完全相同的程序可能更安全,也可能不更安全,这取决于运行的操作系统版本。仍然拥有支持OSLR和非ASLR的Windows版本的开发人员应该相应地响应CVE报告,因为相同的漏洞可能无法在Windows10上利用,但可以在WindowsXP上利用。这同样适用于Windows10而不是Windows8.1或Windows7,因为ASLR在每个版本中都变得更加强大。2.审计遗留程序代码库,避免被首选加载地址误导。仍然可以使用MicrosoftVisualC++6等遗留工具维护遗留程序,这些工具包含有关首选加载地址的作用和重要性的过时文档。由于这些旧工具无法将图像标记为与地址空间布局随机化兼容,实际上对于懒惰的开发人员来说最好不要费心更改默认DLL地址,因为冲突会迫使图像重新定位到不可预测的位置!Windows加载跨进程甚至跨用户在同一位置的多个图像实例,只有重新启动才能保证所有图像的地址都有一个新的随机基地址。用于地址空间布局随机化的Linux实现中使用的ELF映像可以使用位置无关的可执行文件和共享库中的位置无关代码来提供新的随机地址空间,共享同一设备的代码即使在加载时也可以在多个进程之间切换一个不同的地址。不过,WindowsASLR不是这样工作的。相反,内核会在首次使用时为每个DLL或EXE映像分配一个随机加载地址,并且在加载DLL或EXE的其他实例时,它们会收到相同的加载地址。如果一个图像的所有实例都被卸载并且图像随后被加载,图像可能会或可能不会接收相同的基地址。只需重新启动即可确保系统范围内的所有图像都接收到最新的基地址。由于WindowsDLL不使用与位置无关的代码,因此它的代码可以在进程之间共享的唯一方法是始终将其加载到同一地址。为此,内核选择一个地址(例如32位系统上的0x78000000)并在其下方的随机地址加载DLL。如果进程加载最近使用的DLL,系统可能会重用以前选择的地址,因此该DLL的先前副本会在内存中重用。这个实现解决了给每个DLL分配一个随机地址并保证同时不重叠的DLL问题。对于EXE,不必担心两个EXE重叠,因为它们永远不会加载到同一个进程中。即使图像大于0x100000字节,加载EXE的第一个实例为0x400000和第二个实例为0x500000也没有问题,Windows只是选择在给定EXE的多个实例之间共享代码。总结一下:1.任何在崩溃后自动重启的Windows程序都特别容易受到暴力攻击,地址空间布局随机化的保护也会失败。考虑远程攻击者可以按需执行的程序,例如CGI程序,或仅在超级服务器需要时才执行的连接处理程序,例如inetd。另一种可能性是Windows服务与看门狗配对,看门狗在服务崩溃时重新启动该服务。此时,攻击者可以利用WindowsASLR的工作原理,尽可能加载EXE的基地址。如果程序崩溃并且程序的另一个副本保留在内存中,或者程序快速重新启动并且如果可能的话,接收到相同的ASLR基地址,攻击者可以假设新实例仍然加载在相同的地址,此时,攻击者将尝试使用相同的地址。2、如果攻击者能够找出任意一个进程中DLL的加载位置,那么他就知道所有进程中DLL的加载位置。假设一个运行网络服务的系统同时存在两个漏洞:一个在调试消息中泄漏指针值,但没有缓冲区溢出;一个泄漏指针,但不泄漏指针。如果泄漏的程序揭示了kernel32.dll的基地址,并且攻击者知道此DLL中一些有用的ROP小工具,则可以使用相同的内存偏移量来攻击包含溢出的程序。因此,可以将看似无关的易受攻击的程序链接在一起,首先克服ASLR,然后启动漏洞利用。3.要提升权限,可以先使用低权限帐户绕过ASLR。假设后台服务公开了一个只能供本地用户访问的命名管道,并且存在缓冲区溢出。要确定程序的主例程和DLL的基地址,攻击者只需在调试器中启动另一个副本。然后可以使用调试器确定的偏移量来开发有效负载,以利用高特权进程。发生这种情况是因为Windows在保护EXE和DLL的随机基地址时不会尝试将用户彼此隔离。本文翻译自:https://www.fireeye.com/blog/threat-research/2020/03/six-facts-about-address-space-layout-randomization-on-windows.html如有转载请注明原文地址。