软件漏洞-缓冲区溢出
概述
漏洞定义:信息系统硬件、软件、OS、网络协议、数据库等在设计上、实现上出现的可以被攻击者利用的错误、缺陷、疏漏(弱点!)
漏洞分类:
- 针对安全操作系统研究提出的漏洞分类方法
- 将安全漏洞和软件错误结合在一起的漏洞分类方法
- 多维度分类方法
- 广义漏洞分类方法
- 抽象分类方法
漏洞攻击步骤:
graph LR
A[漏洞发现]-->B[漏洞分析]-->C[漏洞利用]
漏洞造成后果:
- 以匿名身份直接获取系统最高权限
- 普通用户提升为管理员用户
- 实施远程拒绝服务攻击
- ...
漏洞的标准化研究:CVE(公共漏洞和暴露)、CWE(通用缺陷枚举)
典型漏洞类型
- 栈溢出
- 堆溢出
- 格式化串
- 整型溢出
- 释放再使用
栈溢出
原理:程序运行时,计算机会在内存区域开辟一段连续的内存块,包括代码段(.text)、数据段(.data和.bss)和堆栈段(heap、stack)
- 代码段:也称文本段,存放程序的机器码和只读数据;这个段在内存中一般被标记为只读,任何对其的写操作都会导致段错误(segmentation fault)
- 数据段:包括已初始化的(.data)和未初始化的(.bss),前者存放保存全局的和静态的未初始化变量
堆栈段:
- 堆:位于BSS内存段的上边,用来存储程序运行时分配的变量;大小不固定,可动态扩张(malloc、new)或缩减(free)
- 栈:一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等;特性是先进后出,两个基本操作是push和pop
每个进程有一个栈,这个进程中每个函数被调用时分别在栈中占用一段区域,称为栈帧
- 随着函数调用层数的增加,函数栈帧一块块地向内存低地址方向延伸的
- 随着函数调用层数的减少,即各函数调用的返回,栈帧会一块块地被遗弃而向内存的高地址方向回缩
- 各函数的栈帧大小由函数地局部变量数目决定
- 溢出中主要关注数据区和堆栈区
使用栈时两个最重要的寄存器:SP(ESP,栈顶指针)和BP(EBP,基地址/栈底指针)
函数被调用时栈地压入情况:
- !传递给Func的实参从右到左依次入栈
- call指令调用Func函数,并把call指令的下一条指令的地址(EIP中的值)作为返回地址压入栈中,之后跳转到被调用函数入口处
- 保存调用函数的栈底地址(push ebp),进行栈帧切换,把栈指针ESP拷贝到EBP,作为新的基地址(mov ebp, esp)
- 从ebp处开始存放被调用函数中的局部变量和临时变量,抬高栈顶(sub esp, xxx),变量的地址依次减小,先定义的变量先入栈;
函数调用返回时降低栈顶(add esp, xxx),恢复栈帧(pop ebp),被调函数执行完毕(ret,返会地址弹给EIP)
局部变量下面是前一个调用函数的EBP和返回地址,如果局部变量发生溢出,很可能覆盖掉EBP甚至RET,即缓冲区溢出攻击的原理
溢出漏洞的原理
溢出攻击的基本流程
graph TB
A[注入恶意数据]-->B[溢出缓冲区]-->c[控制流重定向]-->D[执行有效荷载]
溢出利用的关键技术
- 溢出点定位:探测法、反汇编分析
- 覆盖执行控制地址:执行控制地址包括返回指针、函数指针变量、异常处理结构等
- 覆盖异常处理结构:程序异常时系统中断当前线程,将控制权交给异常处理程序;Windows异常处理机制称为结构化异常处理(Structured Exception Handling)
跳转地址的确定:
- 跳转指令的选取:jmp esp、call ebx、call ecx等
- 跳转指令的搜寻范围:用户空间的任意地址、系统dll、进程代码段、PEB、TEB
- shellcode定位和跳转