
2.3.8 弧注入
第2.3.7节描述过,对IsPasswordOK()程序中漏洞的第一次利用,通过修改函数返回地址的方式改变了程序的控制流(在该例中,绕过了密码保护逻辑)。这种技术称为弧注入(arc injection,有时也称为return-into-libc),它将控制转移到已经存在于程序内存空间中的代码中。弧注入的利用方式是在程序的控制流“图”中插入一段新的“弧”(表示控制流转移),而不是进行代码注入。很多高明的攻击者偏爱采用这种技术,包括在栈上安装一个已有函数的地址以及相应的参数(如system()或exec(),这种方式可以用于执行命令或已经存在于本地系统上的其他程序)。当返回地址从栈中弹出时(在x86架构上通过ret或者iret指令实现),程序的控制就被“返回”到攻击者指定的函数中。通过调用system()或exec()这样的函数,攻击者可以轻易地在受攻击的机器上建立一个shell,它具有和被攻击程序相同的权限。
更糟糕的是,攻击者可以利用弧注入执行一个函数序列,每个函数都使用攻击者提供的参数。这样,攻击者就可以安装并运行类似于一个包含函数链的小程序的代码,从而增大攻击的危害性。
下面是一个有缓冲区溢出漏洞的程序。
01 #include <string.h> 02 03 int get_buff(char *user_input, size_t size){ 04 char buff[40]; 05 memcpy(buff, user_input, size); 06 return 0; 07 } 08 09 int main(void) { 10 /* ... */ 11 get_buff(tainted_char_array, tainted_size); 12 /* ... */ 13 }
user_input中的被污染数据由memcpy()函数复制到buff字符数组中。如果user_input比buff缓冲区大,那么就会发生缓冲区溢出。
有很多原因导致攻击者选择使用弧注入更甚于代码注入。因为弧注入使用那些已经存在于目标系统内存中的代码,攻击者仅仅需要提供函数地址和参数就可以发起成功的攻击。这种类型的攻击留下的痕迹很小,因此可以用于代码注入无法得逞的漏洞利用情形。因为利用代码包含完整的已存在代码,所以无法通过基于内存保护的方案来防止它,比如无法用将内存段(例如栈)属性设置为不可执行的方式来保护程序。它也可以通过恢复原有帧来防止被检测。
链式函数调用可以让攻击者拥有更大的攻击力。例如,一个严肃对待安全问题的程序员,可能会遵循最小特权原则[Saltzer 1975],即在不需要某些特权的情况下主动去除那些权限。但是,通过链式多函数调用,一个利用程序可以重新获得那些权限,例如,可以在调用system()之前调用setuid()。