“不适合人类阅读,非常水的自我笔记. ”
前言
在程序设计的过程中,开发人员有意或无意的行为,以及C/C++固有的一些特性,会使得所开发的程序中存在某些瑕疵或安全缺陷,而这些瑕疵或安全缺陷就有可能造成被攻击者所利用的漏洞。当前,在使用C/C++语言开发的程序中存在的漏洞主要分为以下五种:
后面我们对这五种安全缺陷进行分析总结。
C/C++安全缺陷分析
(1)缓冲区溢出
指向某一数据结构内存空间写入数据时,写入的输出超出了分配给该数据结构的内存空间的边界,从而覆盖了该数据结构相邻内存中的内容。例如向一个元素个数为5的数组里写6个数据。这种漏洞产生的主要原因是有两种,(1)数据溢出:是在执行从源字符串向目标字符串进行复制操作时,没有对目标字符串数组的边界进行检查,从而导致复制的字符个数超出了目标字符串数组的边界而产生漏洞;(2)数组越界:程序设计的时候,会有众多的函数调用,这些函数极大地方便了编程人员的开发工作,但是很多函数可能都没有对C/C++语言中的数据类型进行强制性的边界检查,这就会造成程序运行过程中越界操作数据元素的错误,同样产生溢出漏洞。数组越界举个简单的例子:
1
2
3
4
5
6
7
8
void main(){
int i = 0;
int a[] = {1,2,3,4,5,6,7,8,9,10};
for(i = 0; i <= 10; i++){
a[i] = 0;
printf("hello world\n");
}
}
要了解缓冲区溢出原理,首先我们要搞清楚程序运行中内存的情况,我们这里简单地说明下函数调用时会发送什么: 1、将被调用的函数的下一条指令地址压入栈(EIP)
2、将当前的EBP压入栈(保存当前的基址)
3、将ESP值赋给EBP(改变基址)
4、移动ESP指针,用以开辟空间,保存数据等
5、函数运行完毕后,EBP出栈(还原到调用前的基址)
6、将EIP出栈(返回到调用函数的下一条指令) 由此可知,缓冲区下面就是EBP和EIP,而EIP直接影响了程序后续的执行流程,正常情况保存的是函数返回的地址,如果我们在缓冲区发生异常,导致缓冲区后的EBP和EIP被修改,就会改变程序的流程走向,从而引起了缓冲区溢出攻击,将程序的控制权交了出去。
通常在函数内定义的变量是保存在缓冲区中,那么具体在缓冲区的内部一般都是从下往上顺序分配的,先定义的在下面,后定义的在上面,如:
试想一下:如果此时数组a越界了,覆盖掉了i的值会怎么样? 假设此时数组a越界了,例如上面的数组越界例子,当运行到 i = 10时,执行下面的代码:
1
2
a[i] = 0;
printf("hello world");
此时a[10]正好是变量i的地址,故 a[10] = 0会导致i的值被覆盖,变成了0,从而程序进入死循环。倘若此时直接对a[12] 进行赋值,便会直接修改EIP的地址,从而可能导致安全漏洞。
(2)指针覆写
指由于对指针没有进行很好的处理和保护,造成指针的值被修改和利用的漏洞。一般情况下,可以通过栈上的缓冲区溢出来覆盖和修改函数的返回地址,从而让程序的控制权转移至攻击者提供的恶意代码,完成攻击意图。一般需要满足以下两个条件:
(1)程序中存在缓冲区溢出漏洞, 这样可以通过缓冲区溢出来覆写指针的值;
(2)发生溢出错误的缓冲区要和被覆写的指针在同一个内存段内, 且位置最好相邻。
(3)内存管理错误
指对动态内存管理技术不正确的使用导致的漏洞。常见的内存管理错误有:
1、内存初始化错误:当用内存分配函数例如malloc()函数分配内存时,所分配的内存没有初始化,里面的内容不确定可能导致信息泄漏。而之所以调用类似malloc()的函数分配内存时,不自动初始化的原因是由于性能考虑,初始化过大的内存块会导致系统性能下降。于是C/C++语言将是否初始化的决定权交给了程序员。
2、空指针或无效指针解引用错误:当使用内存分配函数分配内存时,如果分配不成功会返回一个空指针。此外,当指针超出它所指对象的边界或所指对象被释放时,它会变成一个无效的指针。对空指针和无效指针的解引用会导致时间(Temporal Error))或空间(Spatial Error)错误,引起崩溃。
3、引用已经被释放的内存。当分配给一个对象的内存被释放时,指向该内存空间的指针应该赋值为空,或者重新指向。否则,通过该指针仍然可以访问已经被释放的内存空间,产生信息泄漏。
4、内存泄漏:当一个内存空间不再需要时却不释放,就会产生内存泄漏的错误。
(4)整数溢出
当有符号整数运算的结果超出了该整数类型所能表达的最大值时,就产生了整数溢出的漏洞。整数溢出可能导致整数计算结果与期望结果存在较大差异,从而导致计算结果错误、数据丢失和数值被错误理解等问题。
(5)格式化输出函数:
指C/C++语言中定义的一些可以接受可变数量参数的输出函数,其中一个参数成为格式化字符串,用户可以通过控制格式化字符串来控制格式化输出函数的执行。如果使用不正确可能产生以下漏洞:
1、缓冲区溢出:一般情况下,格式化输出函数会假定存在任意长度的缓冲区,如果不对目标数组进行边界检查,可能会发生溢出错误。
2、利用格式化字符串打印程序栈和内存的内容:在使用格式化输出函数时,攻击者可以使用“显示指定地址的内存”的格式化字符串来查看栈和任意地址的内存。例如,格式化转换指示符”%s”可以显示参数指针所指定的地址的内存。如果攻击者能够操作这个参数指针来引用一个特定的地址,那么格式转换符”%s”将会输出该地址内存空间的内容。通过这种方式,攻击者就可以获得程序中指定地址内存的内容,使得程序信息发生泄漏。