软件漏洞原理

缓冲区溢出漏洞

缓冲区:程序在运行前会预留一些内存空间,这些空间用于临时存储I/O数据。
缓冲区溢出:计算机向缓冲区内填充的数据超过了缓冲区本身的容量,导致合法的数据被覆盖。
根据缓冲区所处的内存空间极其分配形式,可分为栈溢出和堆溢出两种。

栈溢出

栈:一种基本的数据结构,符合先进后出的原则。
栈溢出:栈是向低地址方向生长的,而变量在栈中是向搞地质方向生长的,因此,当栈里面的变量被赋予的值超过其最大分配缓冲区大小时,就会覆盖前面push到栈里的返回地址,导致函数在返回时发生错误,即栈溢出。
下列代码产生溢出的原因是:向长度只有8字节的testBuf数组强行复制了64字节的数据,导致了栈溢出。在这里插入图片描述

堆溢出

堆:一种基本的数据结构。堆是一种树结构,准确地说是一个完全二叉树。 在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。我们一般称管理堆的那部分程序为堆管理器。
堆溢出:堆里面的变量赋予超过了其分配的空间大小的值,堆链表的后续链表数据会被覆盖。
堆溢出相对比较复杂,因为各种环境堆的实现都不完全相同
堆上并不存在返回地址等可以让攻击者直接控制执行流程的数据,因此我们一般无法直接通过堆溢出来控制 EIP 。
在这里插入图片描述

防范措施

针对堆栈溢出可能造成的计算机安全问题,通常有以下这些防范措施:

  1. 强制按照正确的规则写代码
  2. 通过操作系统使得缓冲区不可执行,从而阻止攻击者植入攻击代码。但由于攻击者并不一定要通过植入代码来实现攻击,因此该方法依然有一定弱点。
  3. 利用编译器的边界检查来实现缓冲区的保护。该方法使得缓冲区溢出不可能出现,完全消除了缓冲区溢出的威胁,但代价较大,如性能速度变慢。
  4. 程序指针完整性检查,该方法能阻止绝大多数缓冲区溢出攻击。该方法就是说在程序使用指针之前,检查指针的内容是否发生了变化。

通常的代码要设置堆栈缓冲区,最好能检测堆栈运行形况,设置堆栈溢出检测算法。对于递归引起的堆栈溢出,可以采用循环处理。

整型溢出漏洞

在计算机中,整型是一个特定的变量类型。经过不同CPU架构的编译器处理后,整型和指针所占字节一般是相同的。因此,在32位系统(例如x86)中,一个整数占32位;而在64位系统(例如SPARC)中,一个整数占64位。
但是,仅这样做是不够的。因为这样做无法表示负数,所以需要引入一种机制,即通过一个变量的最高位来决定数值的正负。如果最高位置1,那么这个变量就解释为负数;如果置0,那么这个变量就解释为正数。
C语言中,基本数据类型包括短整型short、整型int以及长整型long,每种数据类型还可以分为有符号和无符号数,我们尝试用的int a = 0 这样的表达式,默认就是定义一个有符号的整型数据a。对于无符号数,需要显示声明为 unsigned int a = 0。每种数据类型都会有其相应的范围。
整数溢出从成因的角度可以分为三大类:存储溢出、计算溢出、符号问题。

存储溢出

存储溢出:使用不同的数据类型存储整型数造成。
由于len1和len2的数据类型长度不一样,在进行赋值操作时,len2无法容纳len1的全部位。
在这里插入图片描述
把short类型的len2赋值给len1,只能覆盖其低16位。
在这里插入图片描述

运算溢出

运算过程中造成整数溢出是最常见的情况,很多著名的漏洞都是由这种整型数溢出导致的,其原理就是在对整型数变量进行运算过程中没有考虑其边界范围,造成运算后的数值超出了其存储空间。
在定点计算机和浮点计算机中,上溢和下溢的概念是不完全相同的。在定点计算机中,从正方向超过了数的表示范围,称为上溢;从负方向超过了数的表示范围,则称为下溢。在浮点计算机中,浮点数的表示范围主要由阶码来决定。不论数的符号是正还是负,若阶码从正的方向超出了阶码的表示范围,称为上溢;若阶码从负的方向超出阶码的表示范围,或者尾数为“0”时,统称为下溢。一般来说,计算机对于浮点数的下溢,则自动当作“0”来处理,不输出错误信息;而产生的上溢,计算机则产生“溢出中断”,并输出溢出的错误信息,甚至停止程序的运行。

符号问题

符号问题:整型数分为有符号整型数和无符号整型数,在一般情况下,对长度变量都要求使用无符号整型数,如果忽略了符号,就可能引起溢出。
分块编码:分配一个缓冲区来存放数据,如果提交的数据大小未知,那么客户端会以一个协商好的分块大小向服务器提交数据。
Apache服务器提供对分块编码的支持,使用一个有符号变量来存储分块长度,分配一个固定大小的栈缓冲区来储存分块长度。将分块数据复制到缓冲区之前,Apache会对分块长度进行检查,如果分块长度大于缓冲区长度,apache将最多只复制到缓冲区长度的数据,否则将根据分块长度来复制数据,然而在检查时没有将分块长度转换为无符号型来比较。因此,如果攻击者将分块长度设置成负值,就会绕过上述安全检查。

如何防范

整数溢出漏洞如何防范:

  1. 形成关于特殊数据输入的意识,比如之前先确定最大和最小输入,使用合适的数据类型。
  2. 尽量避免对两个正数相加之后,再取结果比较,上例应该改成:
  3. 在使用变量申请内存,或者作为数组下标时,注意对越界的监测。

整数溢出漏洞很大程度上,是因为程序员不好的变成习惯和疏忽,养成良好的安全编程习惯,严格检查变量的赋值。

扫描二维码关注公众号,回复: 11847900 查看本文章

UAF漏洞

UAF漏洞:一块堆内存被释放了之后又被使用。又被使用指的是:指针存在(悬垂指针被引用)。
分为以下三种情况:

  1. 内存块被释放后,其对应的指针被设置为 NULL , 然后再次使用,自然程序会崩溃。
  2. 内存块被释放后,其对应的指针没有被设置为 NULL ,然后在它下一次被使用之前,没有代码对这块内存块进行修改,那么程序很有可能可以正常运转。
  3. 内存块被释放后,其对应的指针没有被设置为 NULL,但是在它下一次使用之前,有代码对这块内存进行了修改,那么当程序再次使用这块内存时,就很有可能会出现奇怪的问题。

悬垂指针:一类不指向任何合法的或者有效的(即与指针的含义不符)的对象的指针。比如一个对象的指针,如果这个对象已经被释放或者回收但是指针没有进行任何的修改仍然执行已被释放的内存,这个指针就叫做悬垂指针

简单讲就是第一次申请的内存空间在释放过后没有进行内存回收,导致下次申请内存的时候再次使用该内存块,使得以前的内存指针可以访问修改过的内存。

具体过程如图:A先后调用BCD者3个子函数,B会把A的某个资源释放,D判断不严谨,即使在B把A的资源释放后依然引用他(例如某个指针),这使D引用危险的悬空指针。因此,利用方法是:让A调用B,B会把A的某个指针释放,执行C,C赶紧申请分配内存,企图占用刚才被释放的空间,控制这块内存,D被调用,由于检查不合格,调用了已经被释放的指针,而该指针所对应的内存空间实际上已经在C中被重用,导致漏洞被利用。
在这里插入图片描述
如何防范UAF漏洞,通过其原理可以知道,UAF漏洞是因为指针使用不当产生的,即指针被释放后又被后续的函数调用,因此在编程过程中,开发人员在使用指针之前要判断指针是否有效。

猜你喜欢

转载自blog.csdn.net/weixin_43916678/article/details/107180322