软件防御:缓解常见利用技术
在我们系列的前几篇文章中,我们描述了各种缓解改进,旨在防止特定类别的内存安全漏洞被利用,例如涉及堆栈损坏、堆损坏、不安全列表管理以及引用计数管理不当的漏洞。这些缓解措施通常与特定的开发人员错误相关联,例如写入超出堆栈或堆缓冲区的边界、未能正确跟踪引用计数等。因此,这些缓解措施通常试图在攻击者进一步利用过程中(例如,在他们获得指令指针控制权之前)检测此类错误的副作用。
另一种缓解利用的方法是专注于打破可应用于许多不同类别的内存安全漏洞的技术。这些缓解措施可能具有更广泛的影响,因为它们适用于在利用许多漏洞的过程中进一步使用的技术。例如,一旦攻击者通过任意漏洞获得指令指针的控制权,他们本质上需要知道有用可执行代码的地址来设置它。这就是众所周知的缓解措施如数据执行保护(DEP)和地址空间布局随机化(ASLR)发挥作用的地方——这两者已经在Windows上支持多个版本。当结合使用时,这些缓解措施已经证明,即使攻击者获得了指令指针的控制权,也可以使许多类别的内存安全漏洞的利用变得非常困难。
近年来,攻击者越来越被迫适应利用那些使用广泛缓解措施(包括DEP和ASLR)的应用程序中的漏洞。正如我们之前的博客文章所解释的,存在DEP和ASLR都可以被绕过的场景,攻击者越来越专注于提高他们这样做的能力也就不足为奇了。同样,攻击者更加关注寻找类别的漏洞,例如释放后使用问题,这些漏洞可以在尝试开发漏洞利用时给予他们更大的灵活性。鉴于这些趋势,我们在Windows 8和Windows 8.1中投入了大量注意力来改进缓解措施的鲁棒性,这些措施打破了适用于许多类别漏洞的利用技术。特别是,这篇博客文章将涵盖ASLR的一些值得注意的改进,例如消除可预测的地址空间映射、增加地址空间中存在的熵量,并在可能的情况下使地址空间信息泄露更加困难。
强制ASLR
出于兼容性原因,可执行映像(DLL/EXE)必须通过Visual C++链接器提供的/DYNAMICBASE标志表明它们希望被ASLR随机化。如果可执行映像未使用/DYNAMICBASE链接,Windows内核将尝试在其首选基地址加载映像。这可能导致可执行文件可靠地加载到内存中的可预测位置。虽然Windows上ASLR的这一限制是设计上的,但现实世界中软件漏洞的利用越来越依赖于未启用ASLR支持的可执行映像。
为了通用地缓解这个问题,运行在Windows 8(或安装了KB 2639308的Windows 7)上的应用程序可以选择启用称为强制ASLR的安全功能。启用后,此功能强制所有可重定位映像在应用程序加载时被随机化,包括那些未使用/DYNAMICBASE链接的映像。这旨在防止可执行映像加载到内存中的可预测位置。如果需要,应用程序还可以选择阻止加载不可重定位的映像。
由于强制ASLR功能将导致未启用ASLR支持的可执行映像被随机化,因此可能会遇到兼容性问题。此外,用于强制重定位未使用/DYNAMICBASE构建的可执行映像的方法可能会由于页面共享减少而影响性能。这是因为强制ASLR本质上模拟了基地址冲突的行为,因此可能由于写时复制而产生内存成本。因此,强制ASLR功能在Windows 8上运行的应用程序中默认未启用。相反,应用程序必须显式启用此功能。
强制ASLR功能已默认为关键应用程序启用,例如Internet Explorer 10+、Microsoft Office 2013和Windows Store应用程序。这意味着试图利用通过这些应用程序可访问的漏洞的攻击者将无法依赖非随机化的可执行映像。例如,我们最近启用HXDS.DLL的ASLR的安全更新不会显著影响启用强制ASLR的应用程序的安全状况,因为这个非ASLR DLL已经被随机化。展望未来,攻击者在利用完全启用ASLR或使用强制ASLR的应用程序时,很可能需要依赖特定漏洞的地址空间信息泄露。
自底向上和自顶向下随机化
应用程序进行的虚拟内存分配可以以三种方式之一分配基地址:自底向上、自顶向下或基于。自底向上方法从地址空间的底部开始搜索空闲区域(例如VirtualAlloc默认),自顶向下方法从地址空间的顶部开始搜索(例如使用MEM_TOP_DOWN的VirtualAlloc),而基于方法尝试在提供的基地址分配内存(例如使用显式基地址的VirtualAlloc)。在实践中,应用程序分配的大部分内存将使用自底向上分配方法,很少看到应用程序使用基于方法分配内存。
在Windows 8之前,自底向上和自顶向下分配未被ASLR随机化。这意味着通过VirtualAlloc和MapViewOfFile等函数进行的分配没有熵,因此可以放置在内存中的可预测位置(除非应用程序行为非确定性)。虽然某些内存区域有自己的基随机化,例如堆、栈、TEB和PEB,但所有其他自底向上和自顶向下分配未被随机化。
从Windows 8开始,所有自底向上和自顶向下分配的基地址被显式随机化。这是通过随机化给定进程的自底向上和自顶向下分配的起始地址来实现的。这样,地址空间内的碎片被最小化,同时实现了随机化所有非显式基于的内存分配基地址的好处。
出于兼容性原因,应用程序必须表明它们支持自底向上和自顶向下随机化。应用程序可以通过将其EXE与/DYNAMICBASE链接来实现这一点。
高熵随机化
Windows上64位和32位应用程序之间的主要区别之一是提供给进程的虚拟地址空间的大小。其EXE与/LARGEADDRESSAWARE标志链接的64位应用程序在Windows 8中接收8 TB(在Windows 8.1中为128 TB)的虚拟地址空间,而32位应用程序默认仅接收2 GB。32位应用程序可用的有限地址空间对ASLR在随机化内存映射位置时可以应用的熵量施加了实际限制。由于64位应用程序默认不受这些限制,因此可以显著增加ASLR使用的熵量。Windows 8中的ASLR实现充分利用了这一机会,为64位应用程序启用高熵。提供更高程度的熵可以进一步降低攻击者编写的漏洞利用的可靠性,并使攻击者更不可能正确猜测或暴力破解地址。
高熵自底向上随机化
此功能在自底向上分配的起始地址中引入了1 TB的方差。这相当于24位熵,或1/16,777,216的机会正确猜测起始地址。由于堆、栈和大多数其他内存区域是自底向上分配的,这使得传统的地址空间喷洒攻击不切实际(例如堆和JIT喷洒)。这是因为今天的系统没有足够的内存来喷洒即使实现小程度可靠性所需的量。此外,由于为应用程序启用了高熵自底向上随机化功能,被强制ASLR功能随机化的可执行映像接收高程度的熵。因此,依赖地址空间喷洒的64位应用程序漏洞的利用首先需要披露至少一个自底向上分配的地址,以确定数据可能相对于该地址放置的位置。
出于兼容性原因,此功能默认禁用,必须按应用程序启用。这是因为一些64位应用程序在处理4 GB以上的指针(超出位31设置的重要位)时可能存在潜在的指针截断问题。启用此功能的64位应用程序在分配自底向上内存时保证接收4 GB以上的内存地址(除非4 GB以上地址空间不足)。64位应用程序可以通过将其EXE与Visual Studio 2012提供的/HIGHENTROPYVA链接器标志链接来启用对此功能的支持。使用Visual Studio 2012及更高版本构建时,此标志默认为本机应用程序启用。
高熵自顶向下随机化
此功能在自顶向下分配的起始地址中引入了8 GB的方差。这相当于17位熵,或1/131,072的机会正确猜测起始地址。如果已启用自顶向下随机化(由EXE是否与/DYNAMICBASE链接控制),64位进程自动接收自顶向下分配的高熵。
高熵映像随机化
在Windows 8之前,64位可执行映像接收与随机化32位可执行映像时使用的相同熵量(8位,或1/256的机会正确猜测)。从Windows 8开始,在大多数情况下,应用于64位映像的熵量已显著增加:
- 基于4 GB以上的DLL映像:19位熵(1/524,288的机会正确猜测)
- 基于4 GB以下的DLL映像:14位熵(1/16,384的机会正确猜测)
- 基于4 GB以上的EXE映像:17位熵(1/131,072的机会正确猜测)
- 基于4 GB以下的EXE映像:8位熵(1/256的机会正确猜测)
由于映像的基地址存在熵差异的原因再次是出于兼容性原因。Windows内核当前使用映像的首选基地址作为提示来决定映像是否支持基于4 GB以上。基于4 GB以下的映像可能未在它们被重定位到4 GB以上的场景中进行测试,因此可能存在潜在的指针截断问题。因此,Windows内核尽最大努力确保这些映像加载在4 GB以下。由于这些约束,Windows 8和Windows 8.1中的绝大多数64位EXE和DLL已基于4 GB以上,以确保它们受益于最高可能的熵程度。Visual C++工具链生成的64位映像也默认基于4 GB以上。
地址空间信息泄露强化
ASLR的有效性本质上依赖于攻击者无法发现内存中对象的位置。在某些情况下,攻击者可以利用程序中的漏洞来披露进程的地址空间布局信息。例如,攻击者可以使用漏洞读取他们通常无法访问的内存,从而发现内存中DLL的地址。虽然披露地址空间信息的机制通常取决于被利用的应用程序和漏洞,但攻击者已经确定了一些通用方法。在Windows 8中,我们已采取措施消除和破坏已知的地址空间信息泄露向量,尽管这些变化绝未解决地址空间信息泄露带来的普遍问题。
从SharedUserData中移除映像指针
Windows使用称为SharedUserData的内部数据结构来有效地将某些信息从内核传送到系统上的所有进程。出于效率和兼容性原因,SharedUserData所在的内存地址在系统上的所有进程和所有Windows版本(包括Windows 8)中是一致的(0x7ffe0000)。自Windows XP Service Pack 2以来,此内存区域包含指向系统DLL(NTDLL.DLL)的指针,这些指针已用于启用高效的系统调用调用等。映像指针存在于内存中已知固定位置的事实被指出在某些类型的地址空间信息泄露上下文中是有用的。在Windows 8(以及现在安装了MS13-063的先前版本)中,所有映像指针已从SharedUserData中移除以缓解此类攻击。这些指针的移除有效地缓解了后来披露的影响Windows 8之前版本的DEP/ASLR绕过(涉及LdrHotPatchRoutine)。
消除可预测的固定内存映射
确保所有形式的内存分配都具有某种基级别的熵具有消除地址空间中否则可预测的内存映射的效果。在某些情况下,攻击者可能能够利用漏洞读取内存中任意位置的内容。在这些情况下,攻击者必须能够预测或发现他们希望读取的对象的地址(通常通过堆喷洒)。Windows 8中对ASLR的改进使攻击者更难以可靠地做到这一点,特别是在64位上。因此,任何依赖于从内存中指定位置读取的地址空间信息泄露在Windows 8上通常会更困难且更不可靠。然而,应该注意的是,32位地址空间的大小对此影响施加了实际限制,特别是在攻击者能够用所需内容填充地址空间的大部分的情况下。
内核地址空间信息访问限制
虽然前面的部分重点介绍了对用户模式应用程序的ASLR的改进,但我们在Windows 8.1中也投入了资源来强化Windows内核,防止向较低特权的用户模式进程披露内核地址空间信息。这些改进的大部分侧重于限制低完整性进程访问某些系统和进程信息类,这些类有意暴露内核地址空间信息。此外,某些内核地址从共享桌面堆中移除,并添加了虚拟机监控程序辅助限制,以限制通过可用于查询GDT/IDT描述符表基地址的指令暴露内核地址。由于这些改进,沙盒应用程序如Internet Explorer 11、Microsoft Office 2013和Windows Store应用程序都被阻止通过这些接口发现地址。这意味着攻击者更难以利用本地内核漏洞作为逃脱这些沙盒的手段。
结论
Windows 8和Windows 8.1中对ASLR的改进解决了攻击者在利用漏洞时一直在利用的各种限制。由于这些改进,我们预计攻击者将继续越来越依赖地址空间信息泄露作为绕过ASLR的手段。迫使攻击者依赖信息泄露具有为攻击者在利用现代应用程序中的内存安全漏洞时需要满足的条件添加另一个昂贵复选框的效果。
- Matt Miller