3.3.4 弥补及防御措施

3.3.4 弥补及防御措施

根据竞争条件的三个基本属性,可以对与竞争相关的漏洞的缓解措施进行分类。这一节将考察:

(1)从本质上移除并发属性的缓解措施;

(2)用以消除共享对象属性的技术;

(3)通过控制对共享对象的访问来消除改变状态属性的缓解方法。

1.关闭竞争窗口

由于竞争条件漏洞只存在于竞争窗口期间,因此,最显而易见的缓解方案就是尽可能地消除竞争窗口。这一节提出若干种用于消除竞争窗口的技术。

用于消除竞争窗口的技术有很多,例如互斥缓解方案,线程安全的函数,原子操作的使用,安全地检查文件属性等。以下以线程安全的函数为例进行说明。

线程安全的函数:

在多线程应用程序中,仅仅确保应用程序自己的指令内不包含竞争条件是不够的,被调用的函数也有可能造成竞争条件。

当宣告一个函数为线程安全时,就意味着作者相信这个函数可以被并发线程调用,同时该函数不会导致任何竞争条件问题。我们不应该假定所有函数都是线程安全的,即使是操作系统提供的API。当要使用的函数必须为线程安全时,最好去查阅它的文档以确认这一点。如果必须调用一个非线程安全的函数,那么最好将它处理为一个临界区,以避免与任何其他代码调用冲突。

2.消除竞争对象

竞争条件的存在部分原因是某个对象(竞争对象)被并行的执行流所共享。如果可以消除共享对象或移除对共享对象的访问,那么就不可能存在竞争漏洞了。这一节将基于通过移除竞争对象来缓解竞争条件漏洞的概念,介绍常用的安全实践。

(1)使用文件描述符而非文件名

在一个与文件有关的竞争条件中的竞争对象通常不是文件,而是文件所在的目录。比如说symlink利用依赖于改变目录条目或是在目录树中更高层的条目,从而改变文件名所指代的文件。但一旦一个文件被打开,只要是通过其文件描述符而非文件名的目录(这是竞争的对象)对其进行访问的,该文件就不易受到symlink攻击。通过使用fchown()代替chown()、使用fstat()代替stat()、使用fchmod()代替chmod()可以消除很多与文件有关的竞争条件。必须小心使用那些不接受文件描述符的UNIX函数,包括link()、unlink()、mkdir()、rmdir()、mount()、unmount()、Istat()、mknod()、symlink()以及utime()等,并且将它们视作产生竞争条件的潜在威胁。Windows中仍有可能存在与文件有关的竞争条件,不过概率小得多,因为Windows API鼓励使用文件句柄而非文件名。

(2)共享目录

当两个或更多用户,或一组用户都拥有对某个目录的写权限时,共享和欺骗的潜在风险比对几个文件的共享访问情况要大得多。因通过硬链接和符号链接的恶意重建所导致的漏洞告诉我们最好避免共享目录。本节稍后的“控制对竞争对象的访问”将介绍在不得不使用共享目录的情况下,如何尽量减少对它们的访问。

3.控制对竞争对象的访问

竞争对象的改变状态属性规定“必须至少有一个(并发的)控制流会改变竞争对象的状态(有多个流可以对其进行访问)”。这表明,如果很多进程只是对共享对象进行并发的读取,那么对象将保持不变的状态,且不存在竞争条件(尽管可能会有机密性方面的考量,不过已经与竞争无关了)。在本节中我们将考察其他一些减少改变状态属性暴露的技术。

(1)最小特权原则

有时候,可以通过减少进程的特权来消除竞争条件,而其他时候减少特权仅仅可以限制漏洞的暴露,无论如何,最小特权原则都是一种缓解竞争条件以及其他漏洞的明智策略。

竞争条件攻击通常涉及一个策略,攻击者借以使受害代码执行本来没有(也不应该有)权限执行的函数。当然,极端的情况是当受害代码拥有root特权的时候。另外,如果正在执行竞争窗口的进程没有比攻击者更高的特权,那么攻击者可以利用的漏洞就很少了。

存在若干种方式可以将最小特权原则应用于缓解竞争条件中。

1)在任何可能的时候,避免运行具有高级特权的进程。

2)当一个进程必须使用高级特权时,应该在访问共享资源前使用setuid()、setgid()或setgroups()(linux)、CreateRestricted Token()、Adjust TokenPrivileges()(Windows)以去除这些特权。

3)当创建了一个文件后,应该将权限限制为该文件的所有者(若有必要,稍后可以通过文件描述符调整文件的权限)。某些函数,如fopen()和mkstemp(),要求调用umask()来建立创建权限(creation permission)。

(2)chroot tail

在大多数UNIX系统中还可以使用chroot jai来提供安全的目录结构。调用chroot()可以有效地建立起一套与当前系统隔离的文件目录,该套目录有自己的目录树和根。新的目录树可以预防“..”、symlink以及应用于包含目录的其他利用。安全地实现chroot jail需要相当小心。调用chroot()需要超级用户特权,代码在chroot jail中不能作为root权限执行,以免越过隔离的目录的范畴。

4.侦测工具

(1)静态分析

静态分析工具并不通过实际执行软件来进行竞争条件分析。这些工具一般依赖于用户提供的搜索信息和准则对软件进行解析。静态分析工具能报告那些显而易见的竞争条件,有时还能根据可察觉的风险为每个报告项目划分等级。

Warlock是一个早期的用于分析C程序的静态工具。Warlock依赖于由程序员提供的大量的注解(annotation)来驱动竞争条件的鉴别。ITS4是一个替代品,它使用已知漏洞(包括竞争条件在内)的数据库。分析出来的漏洞被打上标记并且报告一个安全等级。Racerx则执行控制流敏感的(controlflow-sensitive)跨过程分析(interprocedural analysis),对于大型系统,它所提供的粗略的侦测是再适合不过了。最著名的公开的领域工具有Flawfinder(http://www.dwheeler.com/flawfinder/)和RATS(http://www.securesw.com/rats)。

(2)动态分析

动态分析工具通过将侦测过程与实际的程序执行相结合,解决了静态分析工具存在的一些问题。这种方式的优势在于可以获得真实的运行时环境。只分析实际的执行流具有一个额外的好处,即可以减少必须由程序员进行分析的误报情况。动态侦测的主要缺点包括:动态工具无法侦测未执行到的路径;动态检测通常会带来巨大的运行时开销。

Eraser是一个著名的动态分析工具,它可以截获持有运行时锁的操作。Eraser基于对已获广泛认可的算法的分析发出警告,该算法用于检查持有的锁集合。Eraser支持对程序加标注,以预防在日后的程序运行中再次出现误报。

MultiRace使用了Eraser所使用的锁集合(lockset)算法的一个改进版本,外加一个派生自某常用的静态侦测技术的算法。这种组合声称可以减少误报的可能性。MultiRace还改善了Eraser的运行时开销。

一个众所周知的商业工具是来自Intel公司的Thread checker。Thread checker对Linux和Windows上的C++代码的线程竞争和死锁执行动态分析。

(3)混合分析

许多侦测工具既包括静态的也包括动态的,都无法侦测来自非受信进程的竞争条件。这导致产生了为侦测(有时是为预防)UNIX文件系统中的竞争条件而专门设计的工具。

RaceGuard是一个UNIX内核扩展,用于提供对临时文件的安全使用功能。RaceGuard维护着它自己的进程缓存以及临时文件。RaceGuard可以截获执行时的文件探测和打开,一旦侦测到攻击就中止操作。

由Tsyrklevich和Yee提议的一个原型工具可以检测所有的文件访问。文件状态由工具维护,每一个对文件的访问请求都通过试探法分析以确定访问操作是否会产生竞争条件。当发现文件访问攻击时,“攻击者进程”会被挂起且该事件被写入日志。

Alcatraz工具可以维护一个文件修改缓存,该缓存将实际的文件系统和不安全的访问隔离开,需要用户输入才能提交不安全的文件修改操作。

【注释】

[1]只有通过在支持该特许的计算机硬件平台上使用内存管理硬件,或通过安排内存使可写数据不被存储在与只读数据相同的页上时,内存才能被标记为只读的。

[2]一般指用来获得shell的代码,后来泛指植入进程的代码。

[3]推荐使用VC加载程序,在程序关闭前能自动暂停程序以观察输出结果。

[4]RTC选项只能用于调试环境。