告别ExploitShield
ExploitShield被宣传为能够提供“针对所有已知和未知0day漏洞利用的防护,在传统杀毒软件和安全产品失效时保护用户”。我发现这一主张非常非凡且令人兴奋!软件应用程序中的漏洞对全球计算机用户来说是真实存在的问题。到目前为止,我们在提供实际技术帮助个人用户防御软件漏洞方面做得相当糟糕。
在我看来,微软通过其增强缓解体验工具包(EMET)取得了最佳进展。EMET改变了操作系统的行为,增加了攻击者必须付出的努力来产生有效的漏洞利用。有博客文章详细记录了EMET的功能。
总的来说,我认为那些公开透明其方法的系统比“秘方”系统更值得信赖。EMET非常公开其方法,而ExploitShield则隐藏它们,试图从隐蔽性中获得额外的安全性。
我分析了ExploitShield系统和技术,我的分析结果如下。总结来说,该系统非常可预测,攻击者可以轻松研究它并调整攻击以克服它,而且实现本身创造了新的攻击面。经过这次分析,我不认为这个系统能够帮助个人或组织防御有能力编写自己的漏洞利用(0day或其他)的攻击者。
注意事项
我进行的分析是针对他们的“浏览器”版本。他们的“企业”版本中可能有更高级的东西,我诚实地不能说,因为我没有见过它。然而,根据我分析的实现的“基调”以及其中的实现缺陷,我怀疑这种可能性,并认为“企业”版本只是“更多相同的东西”。我欢迎被证明是错误的。
初步分析
通常我们可以使用一些优秀且免费的工具来了解软件的足迹。我喜欢使用GMER。GMER调查整个系统,并使用交叉视图技术来识别对运行程序所做的补丁。
如果你记得,从ExploitShield的营销信息中,我们看到弹出框看起来像这样:
这个截图中有一些提示,例如,为什么指定了路径?如果这真的是在阻止“漏洞利用”,它不应该达到指定文件系统上的路径的地步吗?
在以下部分中,我将详细介绍我的分析的每个阶段,因为它与ExploitShield的组件或概念相关。
ExploitShield使用设备驱动程序
ExploitShield系统的一个组件是设备驱动程序。该设备驱动程序使用操作系统支持的机制(PsSetCreateProcessNotifyRoutine)在操作系统启动进程时接收操作系统的通知。
每次进程启动时,设备驱动程序检查该进程,并可选地将其自己的用户模式代码模块加载到启动的进程中。加载用户模式代码模块的标准取决于启动的进程是否是ExploitShield正在保护的进程。
用户模式组件
用户模式组件似乎仅用于钩取/绕道特定函数。
函数钩取的行为,也称为函数绕道,涉及修改函数的开头,使得当该函数被调用时,改为调用另一个函数。MS Research关于Detours的论文相当彻底地解释了这个概念。
函数钩取通常用作实现应用程序检查器或参考监视器的方式。安全系统可以绕道一个函数,如CreateProcessA,并对CreateProcessA的参数做出基于启发式的决策。如果启发式表明行为可疑,安全系统可以采取一些行动,例如使对CreateProcessA的调用失败或终止进程。
钩取的函数
ExploitShield似乎主要通过绕道以下方法起作用:
- WinExec
- CreateProcessW/A
- CreateFileW/A
- ShellExecute
- UrlDownloadToFileW/A
- UrlDownloadToCacheFileW/A
在这里,我们可以理解ExploitShield的作者在说“经过研究数千个漏洞利用后,ZeroVulnerabilityLabs开发了一种创新的专利待批技术,能够检测受保护的应用程序是否被恶意利用”时的意思。这些是shellcode通常用于丢弃和执行其他程序的函数!
函数钩取行为
每个函数实现一个直接的启发式。在任何过程(在x86上)被调用之前,过程完成后返回的地址被推送到堆栈上。每个钩取从堆栈中检索返回地址,并询问关于返回地址属性的问题。
地址的页面权限是RX(读-执行)吗? 地址是否位于加载模块的边界内? 如果这两个测试中的任何一个失败,ExploitShield报告它发现了漏洞利用!
术语混淆
漏洞:漏洞是软件的一种属性,允许某种信任违规。漏洞的定义非常广泛。内存损坏漏洞对计算机安全产生了如此大的影响,以至于很多时候,“漏洞”被简单地用作“内存损坏漏洞”的简写,然而其他类型的漏洞确实存在,例如信息泄露漏洞或认证绕过漏洞。信息泄露漏洞有时可能比内存损坏漏洞对个人隐私更糟糕。
漏洞利用:漏洞利用是使用漏洞来影响某些行动的软件或过程,通常是执行有效载荷。
有效载荷:攻击者创建的软件,在漏洞被用于危害系统后执行。
我相信当ExploitShield使用术语“漏洞利用”时,他们实际上是指“有效载荷”。
ExploitShield的好日子
那么ExploitShield按预期功能运行的逐场播放是什么?让我们看一下,抽象出具体使用哪个漏洞利用的细节:
用户被欺骗导航到攻击者控制的恶意网页。他们不能为此受到太多责备,他们只需要犯一次这个错误,访问可能是攻击者入侵合法网站并使用它提供恶意软件的结果。
该网页包含用户浏览器中漏洞的漏洞利用。网络浏览器加载包含漏洞利用的文档,并开始解析和处理漏洞利用文档。
漏洞利用文档中的数据已被修改,使得解析文档的程序做了一些坏事。假设漏洞利用说服网络浏览器做的是用漏洞利用也提供的数据的地址覆盖存储在内存中某处的函数指针。接下来,易受攻击的程序调用这个函数指针。
现在,网络浏览器执行漏洞利用提供的代码。此时,网络浏览器已被利用。用户正在运行攻击者/漏洞利用提供的代码。此时,任何事情都可能发生。注意我们如何已经完成了这个过程的“利用”阶段,而ExploitShield还没有进入画面。
执行的代码调用其中一个钩取的函数,比如WinExec。对于这个例子,假设执行的代码是从堆上的页面调用的,所以它的权限是RWX(读-写-执行)。
如果攻击者不知道它在那里,并且没有全局代表到对攻击者来说是一个大问题,ExploitShield是很好的。如果攻击者知道它在那里,并且关心,他们可以轻松绕过它。
ExploitShield的坏日子
如果攻击者知道ExploitShield,创建不触发ExploitShield监视的警报的漏洞利用需要多少努力?我认为根本不需要太多努力。两个直接的可能性浮现在脑海中:
使用(非常)原始形式的ROP(返回导向编程)。识别加载模块中的ret指令,并将其作为返回地址推送到堆栈上。在这个地址之前将你的返回地址推送到堆栈上。ExploitShield进行的检查将通过。
使用等同于其中一个钩取函数但不是钩取函数的函数。如果CreateProcess被钩取,使用NtCreateProcess代替。
这两种方法都会击败我在ExploitShield中发现的保护。此外,这些技术将在没有ExploitShield的系统上起作用,这意味着如果攻击者关心在ExploitShield存在时绕过它,他们只需要做一次实现这些绕过的工作。
隐蔽性并不总是坏的
“通过隐蔽性实现安全”的原则经常被安全书呆子引用为安全系统应避免的负面属性。然而,只要防御系统保持隐蔽或不可预测,隐蔽性实际上使系统更安全。基于隐蔽性的防御技术的困难在于找到一个可以以低成本进行的隐蔽更改,并且攻击者在其被破坏之前无法适应,或者一个在其隐蔽性受到损害时可以以非常低的成本更改的更改。
例如,考虑微软的PatchGuard。PatchGuard通过在检测到修改时崩溃来“保护”系统。PatchGuard的操作被隐藏,微软没有发布。只要PatchGuard的操作被隐藏和保密,它就可以通过在检测到rootkit所做的修改时使系统崩溃来保护系统。
然而,PatchGuard经常被安全研究人员逆向工程和研究。每次研究人员坐下来意图绕过PatchGuard时,他们都取得了成功。有趣的是接下来发生的事情:在未来的某个时候,微软默默发布一个更新,改变PatchGuard的行为,使得它仍然实现在检测到修改时使系统崩溃的目标,但不容易受到安全研究人员创建的攻击。
在这种情况下,隐蔽性起作用。微软制作一个新的PatchGuard非常便宜,实际上内核团队可能有十个“在板凳上”等待当前部署的版本被解剖和绕过。这将内核从静态目标转变为移动目标。隐蔽性起作用,因为改变机制是微软主动进行的,改变既便宜又有效,攻击者无法轻易准备避免这些改变时。
ExploitShield引入的更改非常脆弱,不能轻易修改。也许如果ExploitShield是一个引擎,可以快速提供各种运行时更改并在每个应用程序中随机变化,这种动态会不同。
一些实现问题
正确实现HIPS是很多工作!到处都有棘手的工程决策要做,作为作者,你将自己介入一个非常棘手的安全情况。ExploitShield做出了一些不必要的实现决策。
IOCTL接口
驱动程序暴露一个所有用户都可以访问的接口。传统Windows驱动程序的最佳实践要求只有应该访问它的用户才能访问驱动程序的接口。然而,ExploitShield接口可以被整个系统访问,包括无特权用户。
驱动程序处理发送给它的消息。我没有完全发现这些消息的类型或格式,然而IOCTL处理代码充满了微妙错误的可能性。IOCTL处理代码中存在的任何错误都可能导致内核级漏洞,这将危害整个系统的安全性。
这个接口创造了额外的攻击面。
钩取逻辑
每个钩取调用一个例程来检查返回地址是否位于加载的模块中。这个例程使用一个全局模块列表,该列表仅通过调用EnumerateLoadedModules与程序员提供的回调填充一次。ExploitShield检索加载模块列表的方法中有两个错误。
第一个错误是,在填充全局列表的关键部分周围显然没有互斥。多个线程可以同时调用CreateProcessA,因此理论上用户模式逻辑可能使自己处于不一致状态。
第二个错误是模块仅枚举一次。一旦EnumerateLoadedModules被调用,一个全局标志被设置为true,然后EnumerateLoadedModules再也不会被调用。如果系统观察到对CreateProcess的调用,然后随后加载一个新模块,并且该模块有对CreateProcess的调用,安全系统将错误地将该模块标记为尝试利用。
这些缺陷都不会使用户面临任何额外的危险,它们只是表明编程实践不佳。
为什么钩取?
在ExploitShield的实现中一个特别令人困惑的决定是使用钩取!对于ExploitShield关心的每个事件(进程创建和文件写入),NT内核中存在强大的回调基础设施。确实,传统杀毒软件的作者经常因为过度热心地使用钩取而降低系统稳定性,以至于微软非常强烈地鼓励他们使用这个内核内监控API。
ExploitShield使用不必要的危险编程实践来实现通过使用合法系统服务可能实现的效果,可能暴露出对他们旨在保护的平台缺乏理解。
ExploitShield成功的不可能性
ExploitShield能做什么来改变这种动态?问题是,不多。像这样的防御系统完全依赖隐蔽性。一旦被攻击者研究,系统就失去了它们的价值。在这种情况下,一个问题是反馈循环不会通知安全软件的作者或用户攻击者已经适应了安全系统。另一个问题是系统的隐蔽性难以维持。软件必须被客户使用,所以它必须在某种意义上可用,如果它对客户可用,它很可能也可供攻击者研究。
我们有什么希望?
重要的是要注意EMET在一个重要方面与ExploitShield不同:EMET旨在破坏利用程序的行为,而ExploitShield旨在破坏在系统上执行有效载荷的行为。这些可能看起来像是细微的差别,然而可以在“攻击者有多少有效的选择”方面做出区分。当涉及到执行有效载荷时,攻击者的选择几乎是无限的,因为他们已经在执行任意代码。
在这方面,EMET通常不基于隐蔽性。EMET的作者非常愿意详细讨论他们实施的不同缓解策略,而ExploitShield的作者尚未这样做。
总的来说,我相信如果防御技术对程序或运行时行为做出确定性更改,攻击将失败,直到它适应这种技术。攻击的有效性依赖于技术的隐蔽性,以及更改是否影响漏洞、漏洞利用或有效载荷。如果攻击无法适应修改的环境,那么缓解的隐蔽性就无关紧要。
然而,如果技术不是隐蔽的,而是不可预测的呢?如果有一种防御技术会随机调整系统实现行为,同时保留程序所体验的系统语义行为,会怎样?需要的是识别系统的属性,如果更改,会影响攻击的功能,但不会改变程序的功能。
当这些属性随机变化时,攻击者的选择更少。也许他们知道一个可以超越任何实现细节排列的漏洞。如果他们不知道,然而,他们的攻击是否会成功完全取决于机会。
结论
ExploitShield是一个时间胶囊,包含了2004年必须提供的最佳基于主机的安全技术。在我看来,它不代表计算机安全格局的有意义变化。使用的技术完全依赖于隐蔽性和保密性,需要很少的工作来克服,并且只影响计算机攻击的后期阶段,即有效载荷,而不是漏洞利用。
与其他防御技术相比,ExploitShield显得不足。它使用实现不佳的技术,这些技术针对攻击的阶段,需要攻击者很少的适应来克服。一旦ExploitShield获得足够的市场吸引力,恶意软件作者和漏洞利用编写者将自动化绕过它的技术。
ExploitShield甚至增加了你的攻击面,通过安装一个内核模式驱动程序,该驱动程序将处理系统上任何用户发送的消息。该内核模式驱动程序中的任何缺陷都可能导致在你的系统中引入权限提升错误。
它用于查找shellcode的检测逻辑并非完全有缺陷,它包含一个实现错误,可能导致一些误报,然而通常调用运行时库函数,且返回地址不在加载模块的边界内,是可疑的。这个检测签名的问题在于它可以被轻松修改以实现相同的效果。此外,这个检测签名并不新颖,HIPS产品已经实施这个检查很长时间了。
这是一个遗憾,因为在我看来,这类软件仍然有一些严重的创新空间…