C代码审计之指针诡计
指针诡计(pointer subterfuge)是指通过修改指针值(函数指针、对象指针)来利用程序漏洞的方法。
发生溢出的缓冲区被用于修改函数指针或对象指针的值。
攻击函数指针使得攻击者能够直接将程序的控制权转移到由其提供的任意代码,对对象指针进行修改并赋值的能力导致任意内存写。
缓冲区循环类型
缓冲区溢出通常是由边界不足的循环引起的。通常,有以下这些循环类型:
由上界限制的循环:循环重复执行N次,其中N小于或等于p的边界,指针指定一系列对象,例如,从p到p+N-1。
由下界限制的循环:循环重复执行N次,其中N小于或等于p的边界,指针指定一系列对象,例如,从p到p-N+1。
缓冲区能被用于指针覆写的条件:
1、缓冲区与目标指针在同一个段(bss或data或栈)内。
2、上界限制、空终止符限制的循环,缓冲区往高地址增长,缓冲区比目标指针地址低。
下界限制的循环,缓冲区往低地址增长,缓冲区比目标地址地址高。
3、缓冲区界限不充分,易被缓冲区溢出利用。
ELF包含data段和bss段。data段包含已初始化的全局变量和常量,bss段包含未初始化的全局变量。
函数指针覆写
void good_function(const char *str) {...} int main(int argc, char *argv[]){ static char buff[BUFFSIZE]; static void (*fucPtr)(constr char *str); funcPtr = &good_function; strncpy(buff, argv[1], strlen(argv[1])); (void)(*funcPtr)(argv[2]); }
解析:程序对strncpy不安全的错误,当strlen(argv[1])大于BUFFSIZE时,就会发生缓冲区溢出。buff缓冲区和funcPtr函数指针都是未初始化的静态变量,位于bss段,因此可以利用缓冲区溢出将函数指针funcPtr的值覆写为攻击者控制的代码(shellcode)地址。当执行到最后一行代码,即执行由funcPtr标识的函数时,shellcode将取代good_function执行。
对象指针覆写
C和C++中的对象指针用于指向动态分配的结构、函数的引用参数、数组等。
void foo(void *arg, size_t len){ char buff[100]; long val = ...; long *prt = ...; memcpy(buff, arg, len); *prt = val; ... return; }
解析:memcpy进行无界复制(没有对源数据进行检查),当len>100时buff会发生溢出。在溢出缓冲区后,可以覆写ptr和val,接下来在*ptr=val时就会发生任意内存写。
缓解方案
导致指针诡计的根本原因是缓冲区溢出,因此预防指针诡计的最佳措施就是消除产生缓冲区溢出的条件。