似乎有两个主要的防御:
- 检测应用程序崩溃。盲ROP攻击的工作方式是向受攻击的应用程序反复发送查询,并观察哪些会导致其崩溃。它一点也不隐秘:它可以很容易地通过它造成的应用程序崩溃的数量来检测。因此,防御措施的一个有用部分是监视应用程序崩溃,如果看到多个崩溃,则通知sysadmin。系统管理员需要注意的是,较高的崩溃率可能意味着潜在的盲ROP攻击。不幸的是,这只提供了检测,而不是预防。
- 每当应用程序崩溃时,重新随机化。如果我们能够配置我们的系统,以便每当应用程序崩溃时,它会自动被重新随机化(也就是说,布局是重新随机的,使用ASLR的新的随机性),这将是更有效的。目前,ASLR在一段较长的时间内重复使用相同的随机性;根据操作系统的具体情况,新的随机性只在重新启动后或定期生成。盲ROP攻击利用以下事实:它可以向受攻击的应用程序发送多个请求,每次应用程序崩溃时,它都会自动重新启动,并最终得到相同的地址空间布局(ASLR随机性不会被刷新)。如果我们破坏这个假设,我们就会破坏盲ROP攻击。不幸的是,这个防御似乎有两个问题:
- 问题:我不清楚如何在现有平台上实现这一防御,甚至不知道是否有可能这样做。看来Linux和Mac只是在每次引导时重新随机化。
- 问题:在Linux和Mac上,这种防御将不适用于分叉工作进程的服务器进程;需要一些额外的东西来保护它们。问题是,许多服务器都是作为父进程构造的,这会分叉一组工作进程(子进程)来处理请求,如果工作进程(子进程)崩溃或死亡,父进程将分叉一个新进程。现在,假设处理请求的代码中存在缓冲区溢出漏洞。盲ROP攻击将多次发送触发工作人员崩溃的请求,父服务器将分叉一个新工作人员。由于父进程永远不会死,所以它始终具有相同的地址空间布局。而且,叉的工作方式是子继承与父节点相同的地址空间布局。因此,无论您做什么,所有工作人员都将在父服务器的生存期内具有相同的地址空间布局(例如,直到重新启动或由sysadmin手动重新启动服务为止)。因此,对每一个崩溃进行再随机化都无助于保护这种服务器。Windows没有fork(),因此它可能会得到更好的保护,以防止出现Blind -我对此并不十分清楚。
最终,我还不清楚如何保护或加固分叉服务器,以抵御盲ROP攻击,至少在Windows以外的平台上是这样。
还有一些可能的强化措施,我不会依赖这些措施作为唯一的防御手段,但在某些情况下,这可能会阻止盲ROP攻击:
- 启用堆栈金丝雀。如果编译器没有自动打开堆叠金丝雀,请设置适当的编译器标志,以确保应用程序是用堆栈金丝雀编译的。本文中描述的盲ROP攻击只针对堆栈溢出(堆栈分配缓冲区中的缓冲区溢出漏洞),在某些情况下可以被堆栈金丝雀阻止。如果您正在使用gcc,您可能希望启用强堆栈保护,即
-fstack-protector-strong
(详细信息) --甚至如果您偏执,甚至启用-fstack-protector-all
(但这可能会对性能产生影响)。但是,堆栈金丝雀有一个很大的局限性:它们无助于保护分叉服务器进程,因为在父进程的整个生命周期中,金丝雀将对所有工作进程保持不变。这很不幸。 - 如果方便的话,可以使用负载平衡。如果您的负载均衡器发送每个请求到不同的随机服务器,如果您有足够的后端服务器背后的负载均衡器,这可能有助于使盲ROP更难。它可能有帮助的原因是,每个请求都会转到不同的机器上,从而使用不同内存布局的应用程序的不同实例(假设您使用堆栈金丝雀和完整的ASLR,即PIE)。没有保证,但可能会有帮助。
- 使用标准的硬化措施。和往常一样,用完整的ASLR (Linux/gcc上的
-fpie
)编译。这不会阻止盲ROP,但它将防止更简单的攻击(以及更简单的盲ROP变体)无法工作。