搜档网
当前位置:搜档网 › 缓冲区溢出漏洞攻击和检测技术研究

缓冲区溢出漏洞攻击和检测技术研究

南京航空航天大学

硕士学位论文

缓冲区溢出漏洞攻击和检测技术研究

姓名:程恩

申请学位级别:硕士

专业:计算机应用技术

指导教师:秦小麟

20061201

摘要

本文对网络安全领域中的缓冲区溢出攻击和检测技术进行了研究。首先分

析了相关课题的研究背景,并针对当前该领域国内外的研究现状和发展趋势进

行了分析,在此基础上阐述了本课题的研究内容。接着从缓冲区溢出攻击原理,

攻击的类别和漏洞检测技术三个方面进行了深入而系统的研究。

由于最近几年基于硬件保护方案的缓冲区溢出检测成为研究的发展趋势,

本文将检测技术的研究重点放在硬件层次,并跟踪了国外相关的研究。

Minos项目提出了一种从另外一个角度分析缓冲区溢出漏洞攻击的新方法,

但是它没有完善其思想模型。本文对其进行了分析并提出了基于控制流完整性

模型,做了模型的实例分析并给出了结论。

另一方面,将返回地址保护技术引入到硬件中实现是一个重要手段。针对

存在的这些方法先做了指令执行过程的详细分析,然后针对相关的安全指令方

案做了深入的研究,发现了其中的不足点,设计了可以绕开保护检测的例程。

在此基础之上提出了一种新的安全指令,最后做了相应的功能性测试和性能测试。最后给出本文的结论和进一步研究的工作。

关键词:缓冲区溢出,静态检测技术,动态检测技术,控制流完整性,安全指令集,硬件保护

-I-

The thesis makes a thorough and systemic study of techologies of buffer overflow attacks and detections in the network security field.

Firstly, it analyzes not only research background of relative project, but also the research status and developing trends of home and abroad in this field. it put forward the research content of the project base on the previous analyses.

Secondly, the thesis makes thorough research on techologies of buffer overflow attack theory, attack kinds, detection of vulnerabilities from three aspects.

Since the buffer overflow detection based on the hardware protection plot has become the trend in the couple of years. This paper lays the effort on the hardware level,and introduces the study of some reletive institutions.

The project Minos put forwards a novel detection for the buffer overflow attacks, but this model is not well developed. In this paper, we discuss these deficiencies, propose an improved model based on the Control Flow Integrity, and give out the practical analysis of the model as well as the conclusion.

Besides, The return address protection based hardware is an important strategy. Thus, we firstly introduce the implementation of the exist methods in detail. And then, we discuss the respective Secure Instructions, represent the examples which could pass the protect detection, and point out the shortcomings of those rules.

In order to solve these problems, we design a set of new Secure Instructions, and their function and performance are all tested.

Finally, we draw a conclusion for this paper and future direction in this field is discussed.

Keywords:Buffer Overflow,Static Analysis,Dynamic Analysis,Control Flow Integrity,Secure Instructions,Hardware Protection

-II-

图1.1 CERT漏洞建议报告 (1)

图2.1 攻击前后内存状态 (7)

图2.2 活动记录攻击时栈帧示意图 (8)

图2.3 活动记录攻击示意代码 (8)

图2.4 数据存储示意代码 (9)

图2.5 HEAP/BSS攻击时内存示意图 (10)

图2.6 HEAP/BSS攻击示意代码 (10)

图2.7 函数指针攻击时栈帧示意图 (11)

图2.8 函数指针攻击示意代码 (11)

图2.9 jmp_buf数据结构 (12)

图2.10 长跳转攻击示意代码 (13)

图2.11 格式化字符串读攻击示意代码 (14)

图2.12 格式化字符串写攻击示意代码 (14)

图3.1 ROC 曲线示意图 (19)

图3.2 静态检测工具综合性能 (19)

图3.3 StackGuard 栈帧 (21)

图3.4 VC++.NET带编译/GS选项的栈帧 (22)

图3.5 ProPolice栈帧 (23)

图3.6 ProPolice示意代码 (23)

图3.7 Libsafe栈帧 (24)

图4.1 带缓冲区溢出漏洞检测技术的虚拟机 (29)

图4.2 The Minos Architecture (30)

图4.3 攻击前主客体状态 (33)

图4.4 信息流传递示意图 (33)

图4.5 攻击后主客体状态 (34)

图5.1 SECURE CALL算法 (38)

图5.2 SECURE RET算法 (38)

图5.3 堆栈帧指针攻击示意图 (38)

图5.4 Previous FP攻击时受保护的栈帧 (39)

图5.5 针对前栈帧指针的攻击代码 (40)

图5.6 SOME_SERVICE程序流向示意图 (40)

-I-

缓冲区溢出漏洞攻击和检测技术研究

-II- 图5.7 CALL Instruction based Protection 算法 (41)

图5.8 RET Instruction based Protection 算法 (41)

图5.9 保护前后的栈帧结构 (42)

图5.10 功能型测试代码 (43)

图5.11 性能测试代码 (44)

承诺书

本人郑重声明:所呈交的学位论文,是本人在导师指导下,独立进行研究工作所取得的成果。尽我所知,除文中已经注明引用的内容外,本学位论文的研究成果不包含任何他人享有著作权的内容。对本论文所涉及的研究工作做出贡献的其他个人和集体,均已在文中以明确方式标明。

本人授权南京航空航天大学可以有权保留送交论文的复印件,允许论文被查阅和借阅,可以将学位论文的全部或部分内容编入有关数据库进行检索,可以采用影印、缩印或其他复制手段保存论文。

(保密的学位论文在解密后适用本承诺书)

作者签名:

日期:

南京航空航天大学硕士学位论文

-1-

第一章 绪论

1.1 课题背景

在过去的十几年中,缓冲区溢出漏洞一直引起许多严重的安全性问题,使得一个攻击者有机会获得一台主机的部分或全部的控制权。2001年六月在红色病毒爆发前的一个月,微软公开宣称他们新的Windows XP 操作系统经过详细完整的代码审查不会存在缓冲区溢出漏洞。四个月之后又一个缓冲区溢出的漏洞被发现存在于Universal PNP 中,现在这种漏洞频繁的出现。根据CERT 漏洞建议报告[27]可以看出缓冲区溢出漏洞占了已发现漏洞中相当大的一部分。

缓冲区溢出攻击之所以在安全攻击中占有这么大的比重,是因为缓冲区溢出的缺陷普遍存在并且非常容易被探测到。攻击者利用它能够准确地获得自己想要的东西,他插入攻击代码并使其运行在一定的特权下,最坏的情况,攻击者能够重新引导任何程序,甚至控制整个主机。

图1.1 CERT 漏洞建议报告

2006年5月22日赛门铁克安全机制应变中心今公布侦测出一只瞄准微软

Word2003应用程序而来的零时攻击行为(zero-day exploit )。

“零时攻击”根据赛门铁克的定义,指的是该攻击针对系统厂商尚未公布修补程序的弱点。这种情况下,漏洞的发现和攻击会发生在同一天。作为缓冲区溢出攻击的标准防御手段,Norton Antivirus 或Snort [6]采用的做法是探测攻击数据的数字签名来阻止攻击。可惜,这些方法有点像一个抢劫者打劫了数千家银行后才贴出他的画像。因为事先没有攻击样本,所以对于Zero-Day 攻击无法做出

5

10

15

20

25

30

35

40

19961997199819992000200120022003

缓冲区溢出漏洞攻击和检测技术研究

防御。由于之前并不知道漏洞的存在,所以没有办法防范攻击。尽管业界已尽了最大努力,但没有哪个防火墙能保护主机避免针对网络服务的“零时攻击”。防火墙对这种攻击方式无能为力,因为攻击者传输的数据分组并无异常特征,没有任何欺骗(这就是Nimda、SQL Slammer可以顺利穿透防火墙的原因)。另外可以用来实施缓冲区溢出攻击的字符串非常多样化,无法与正常数据有效进行区分。缓冲区溢出攻击不是一种窃密和欺骗的手段,而是从计算机系统的最底层发起攻击,因此在它的攻击下系统的身份验证和访问权限等安全策略形同虚设。

另一方面,造成严重的破坏作用的漏洞陆续被发现,那些提供安全服务的公司要不断的与全世界的黑客们进行赛跑。究其根源,会发现攻击者与防御者之间在进行着一场不对称的博弈,攻击者可以利用扫描、探测等一系列技术手段全面获取攻击目标的信息,而防御者对他所受到的安全威胁一无所知。为了扭转这种不对称局面。最近几年,蜜罐技术[2] 的主动防御思想开始吸引了一些安全研究人员的注意。通过布置一些作为诱饵的主机、网络服务以及信息诱使攻击者对他们进行攻击,这样可以对攻击行为进行监控和分析,了解攻击者所使用的攻击工具和攻击方法,推测攻击者的意图和动机,从而能够让防御者清晰地了解他们所面对的安全威胁。

目前在操作系统和上层应用软件方面的缓冲区溢出检测研究已经几年来没有突破性研究成果。所有的这些可能说明了也许缓冲区溢出问题不是由于软件开发人员对于软件开发缺乏责任和努力。缓冲区的溢出与程序在内存中的组织分布有关,在冯.诺依曼计算机体系结构中程序的数据和执行代码混合存储,对数据的修改可造成程序代码的修改,是其设计的计算机模型导致的后果,只不过C/C++语言为它提供了这个方便之门。

基于硬件仿真的虚拟机技术正好为上述的思想提供了技术实现手段。虚拟机(Virtual Machine)是提供用户可在单一的桌面上同时运行不同的操作系统,和进行开发、测试、部署新的应用程序的一种软件。虚拟机还可在一部实体机器上,通过连接多个操作系统来模拟完整的网络环境。虚拟机在一台实体机器上同时运行了多种操作系统与应用程序,这些操作系统与应用程序共用硬件装置,但在逻辑上各自独立运行互不干扰。虛拟层映射实体的硬件资源到自己本身的虛拟机器资源,因此每个虛拟机器都有各自的CPU,内存,硬盘,I/O设备等,所以虛拟机器完全等同于一个标准的x86计算机。

1.2 本课题的研究内容

由于缓冲区溢出存在的普遍性和破坏的巨大性,如何有效的检测出缓冲区-2-

南京航空航天大学硕士学位论文

溢出漏洞得到了许多研究机构的重视。本课题首先研究缓冲区溢出漏洞攻击方法,它们包括:

●活动记录(Activation Records)

●堆(HEAP/BSS)

●函数指针(Function Pointer)

●长跳转(SETJMP/LONGJMP )

●格式化字符串攻击(Format String Attack)

其次,研究目前现已存在的缓冲区溢出漏洞检测技术,它们包括:

●漏洞静态检测技术

●漏洞动态检测技术

由于最近几年基于硬件保护方案的缓冲区溢出检测成为研究的发展趋势,本课题将检测技术的研究重点放在硬件层次,并跟进了国外相关研究。一个是提出了一种从另外一个角度分析缓冲区溢出漏洞攻击的新方法的Minos项目,另外一个是将返回地址保护技术引入到硬件中实现的安全指令技术。

1.3 本课题操作系统平台及开发工具简介

◆硬件环境

Intel P4 2.4GHz CPU/512MB SDRAM/80GB IDE Disk

◆软件环境

Red Hat Linux 9.0,QEMU 0.80+Red Hat Linux 9.0

◆开发工具

Linux下GNU工具链以及集成开发环境Eclipse3.0+CDK

◆所使用的操作系统平台及工具简介

⑴操作系统:Red Hat Linux 9.0

Linux是一个遵循POSIX(标准操作系统界面)标准的免费操作系统,具有BSD和SYSV的扩展特性(表明其在外表和性能上同常见的UNIX非常相象,但是所有系统核心代码已经全部被重新编写了)。它的版权所有者是芬兰籍的Linus B.Torvalds先生。Red Hat Linux操作简便,配置快捷,独有的RfM模块功能使得软件的安装极为容易。Red Hat Linux被公认为最易使用的Linux版本,同时它也是目前使用最多的Linux操作系统。9.0版本是其一个较新的版本,使用Red Hat Linux9.0的用户占有非常大的比例。选用Red Hat Linux9.0为主要的实验平台的原因是因为这个版本的Linux操作系统目前的使用率最高,具有普遍性和针对性。

-3-

缓冲区溢出漏洞攻击和检测技术研究

⑵集成开发环境:Eclipse

Eclipse的前身是IBM的Visual Age for Java(简称VA4J)。把这个项目免费赠送给Eclipse社团(https://www.sodocs.net/doc/f213141742.html,)前,IBM已经投入超过四千万美元进行研发。如今,IBM通过附属的研发机构Object Technologies International(简称OTI),继续领导着Eclipse的开发。

Eclipse是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。幸运的是,Eclipse附带了一个标准的插件集,包括Java开发工具(Java Development Tools,JDT)。Eclipse还包括插件开发环境(Plug-in Development Environment,PDE),这个组件主要针对希望扩展Eclipse的软件开发人员,因为它允许他们构建与Eclipse环境无缝集成的工具。开发C/C++程序,装一个C开发工具(C Development Toolkit,简称CDT)插件代替JDT即可

⑶编译器工具:GNU GCC

GCC是一种“编译器”(compiler),把程序的“源代码”(即程序设计人员用C这类高端语言所写的指令)转换成电脑能够理解的“二进位”(binary)指令。几乎所有的开放源代码软件都以GCC 为基础。Linux系统下的GCC(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。GCC是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。GCC编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件。

⑷调试工具:GNU GDB

GNU GDB是GNU开源组织发布的一款功能强大的用来调试 C 和 C++ 程序调试工具。它使你能在程序运行时观察程序的内部结构和内存的使用情况. 以下是 gdb 所提供的一些功能:

◆它使你能监视你程序中变量的值.

◆它使你能设置断点以使程序在指定的代码行上停止执行.

◆它使你能一行行的执行你的代码.

1.4 QEMU模拟器

QEMU [1]是一套由Fabrice Bellard所编写的模拟操作系统的自由软件。它与Bochs,PearPC近似,但其具有某些后两者所不具备的特性,如高速度及跨平台的特性。经由kqemu这个非自由的加速器,QEMU能模拟至接近真实电脑

-4-

南京航空航天大学硕士学位论文

的速度。QEMU的仿真速度约为实际机器的 25%,约为 Bochs的 60倍。VMware 和 Virtual PC 则比 QEMU 快一点,但因为不是开源软件无法修改其功能。国外有的研究机构采用Bochs虚拟机仿真,不过已经在考虑转到QEMU 上,因为QEMU确实比Bochs性能优越很多。QEMU的主要部分是在LGPL 下发布的,而其系统模式模拟则是在GPL下发布;而kqemu这个加速器则是在免费但不开放源代码条件下发布的。使用kqemu可使QEMU能模拟至接近实机速度,但其在虚拟的操作系统是Windows 98或以下的情况下是无用的。

● QEMU主要运作模式

⑴ User mode模拟模式

亦即是用户模式。QEMU能启动那些为不同CPU编译的Linux程序。而Wine及Dosemu是其主要目标。

⑵ System mode模拟模式

亦即是系统模式。QEMU能模拟整个电脑系统,包括中央处理器及其他周边设备。它使得为系统源代码进行测试及纠错工作变得容易。也可以用来在一部主机上虚拟数个不同虚拟主机。

● QEMU的优点

⑴可以模拟 IA-32 (x86),AMD ,MIPS R4000,升阳的 SPARC sun3 与PowerPC (PReP 及 Power Macintosh)架构;

⑵支持其他架构,不论在主机或虚拟系统上;

⑶增加了模拟速度,某些程序甚至可以实时运行;

⑷可以在其他平台上运行Linux的程序;

⑸可以储存及还原运行状态(如运行中的程序);

⑹可以虚拟网络卡。

● QEMU的缺点

⑴对微软Windows及某些主机操作系统的不完善支持(某些模拟的系统仅能运行) ;

⑵对不常用架构的支持并不完善;

⑶除非使用kqemu加速器,否则其模拟速度不及其他虚拟软件,如VMware;

⑷比其他模拟软件难安装及使用。

-5-

缓冲区溢出漏洞攻击和检测技术研究

-6- 第二章缓冲区溢出漏洞原理与类别分析

2.1 缓冲区溢出发展历史

缓冲区溢出(buffer overflow)是安全的头号公敌,据报道,有50%以上的安全漏洞和缓冲区溢出有关。C/C++语言对数组下标访问越界不做检查,是引起缓冲区溢出问题的根本原因。

缓冲区溢出攻击可追溯到1988年臭名昭著的Morris蠕虫(fingerd缓冲区溢出攻击),但其真正为众人所知是在1996年11月AlephOne发表的经典文章“Smashing the stack for fun and profit” [12]。

1998年,Dildog在Bugtrq邮件列表中以Microsoft Netmeeting为例子详细介绍了如何利用Windows的溢出,这篇文章最大的贡献在于提出了利用栈指针的方法来完成跳转。返回地址指向的代码无论是在出问题的程序中还是在动态链接库中,都包含了用来利用栈指针完成跳转的汇编指令。Dildog提供的方法避免了由于进程线程的区别而造成栈位置不固定。Dildog还有另外一片经典之作“The Tao of Windows Buffer Overflows” [13]。

1999年,IIS4.0远程攻击代码的作者dark spyrit AKA Barnaby Jack [14]在Phrack Magzine(第55期)上提出了使用系统核心DLL中的“jmp esp”的指令来完成到Shellcode跳转的天才想法,从此开创了Win32平台下缓冲区溢出的新思路,大量Windows平台下的缓冲区溢出漏洞也被利用。近年来著名的蠕虫如Code-Red,SQL Slammer,Blaster和Sasser都是通过缓冲区溢出攻击获得系统管理员权限后进行传播。

Litchfield在1999年为Windows NT平台创建了一个简单的shellcode,并详细讨论了Windows NT的进程内存和栈结构,以及基于栈的缓冲区溢出,且以rasman.exe作为研究实例,给出了提升权限创建本地shell的汇编代码。

1999年,来自w00w00安全小组的M. Conover [15]写了基于堆的缓冲区溢出的教程,开头写道:“基于HEAP/BSS的溢出在当今的应用程序中已经相当普遍,但很少有被报道”。他注意到当时的保护方法,例如非执行栈,不能防止基于堆的溢出,并给出了大量的例子。

2001年六月在红色病毒爆发前的一个月,微软公开宣称他们新的Windows XP操作系统经过详细完整的代码审查不会存在缓冲区溢出的漏洞。四个月之后又一个缓冲区溢出的漏洞被发现存在于Universal PNP中,现在这种漏洞频繁的出现。

南京航空航天大学硕士学位论文

-7-

因为缓冲区溢出的缺陷普遍存在并且非常容易被探测到。攻击者利用它能够准确地获得自己想要的东西,注入攻击代码并使其运行在一定的特权下,攻击者能够重新引导任何程序,甚至控制整个主机,所以缓冲区溢出攻击在安全攻击中占有很大的比重。

2.2 缓冲区溢出基本原理

缓冲区是程序运行时在计算机内存中的一块连续存储区域,可以保存相同数据类型的多个实例。缓冲区溢出是指当计算机程序向缓冲区内填充的数据位数超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上的行为。

缓冲区溢出攻击的原理是通过往程序缓冲区写入超出其边界的内容,造成缓冲区溢出,使得程序转而执行攻击者指定的代码,通常是为攻击者打开远程连接的ShellCode ,以达到攻击目标。图2.1显示的攻击前后的内存状态。

TOP of Stack

Return Address

Buf

(256 bytes)

Attack Other variables 图2.1 攻击前后内存状态

缓冲区溢出漏洞攻击的目的在于扰乱目标程序的运行流程,以使得攻击者可以取得程序的控制权。要使攻击者攻击成功,通常有两个关键点:第一,存在能够被攻击的数据结构,第二,要有被执行的攻击代码。攻击代码可以是攻击者注入程序中的,也可能是直接利用系统已经存在的函数。最常见的具有极大威胁的攻击代码是通过缓冲区溢出后得到系统shell ,再通过shell 执行其它命令。

在C/C++语言中,常会遭到的攻击与程序运行时用到的数据结构有关,比如函数活动记录,函数指针,C 函数库中的setjmp()和longjmp()函数用到的保存程序运行时栈帧信息的数据结构jmp_buf ,其存储位置可能在数据段,静态存储区,堆栈段,堆里。

针对格式化字符串攻击也会引起安全性问题。严格地说格式化字符串漏洞并不是缓冲区溢出,但是它会导致与缓冲区溢出同样的问题。

下面将详细深入分析这几种最根本的攻击方式。

缓冲区溢出漏洞攻击和检测技术研究

-8-

2.3 活动记录(Activation Records)

这类的缓冲区溢出被称为堆栈溢出攻击(stack smashing attack),是目前常用的缓冲区溢出攻击方式。

每当一个函数调用发生时,调用者会在堆栈中留下一个活动记录,它包含了函数结束时返回的地址。攻击者通过溢出这些变量,通过改变程序的返回地址,当函数调用结束时,程序就跳转到攻击者设定的地址,而不是原先的地址。

堆栈(stack)是一种基本的数据结构,具有先进后出的性质。在Intel X86平台上,调用函数时实际参数(arguments )、返回地址(return address )、局部变量(local variables )都位于栈上,栈是自高向低增长(先入栈的地址较高),栈指针(stack pointer )寄存器ESP 始终指向栈顶元素。

所以对于返回地址和前堆栈指针攻击要求函数体内有局部变量buffer ,有对buffer 进行的操作且不进行数组边界检查的处理函数。典型的攻击示意图和攻击代码见图2.2和图2.3。

图2.2 活动记录攻击时栈帧示意图

copy (char

* msg)

{

char buffer[512];

strcpy (buffer,msg);

}

main (int argc,char * argv[])

{

if (argc>1)

copy (argv[1]);

}

图2.3 活动记录攻击示意代码

南京航空航天大学硕士学位论文

2.4 堆(HEAP/BSS)

堆(Heap) 段,是由应用程序动态分配的内存区。在Linux 系统中,通常为用malloc(),calloc()和realloc()函数开辟的内存单元。

静态存储区(BSS 段)包含未被初始化的数据,在程序运行的时候才被分配。在被写入数据前,它始终保持全零。

在大部分的系统中,HEAP段和BSS段是从低端地址向高端地址增长的。图2.4说明了哪些数据是存放在栈段,哪些数据是存放在HEAP段或BSS 段中。

static int GLOBAL_CONSTANT = 1; /* 存储在data 段 */

static int global_variable; /* 存储在BSS 段*/

/* argc 和argv 存储在栈段 */

void main (int argc , char **argv)

{

int local_dynamic_c_variable ; /* 存储在栈段 */

static int local_static_variable ; /* 存储在BSS 段 */

int *buf_pointer = (int *)malloc(32) ; /*存储在堆段 */

}

图2.4 数据存储示意代码

操作系统中,大部分的内存区是在内核一级被动态分配的,但HEAP段是由应用程序来分配的,它在编译的时候被初始化。 BSS段(静态存储区)用于存放程序的静态变量,这部分内存都是被初始化为零的。由于在考虑缓冲区溢出攻击时,HEAP和BSS段具有相近的特性,因此下面将要提到的“基于堆的溢出” 既包含HEAP段的溢出,也包含BSS段的溢出。本文把静态存储区溢出也算作一种堆溢出。

在大部分的系统(包括Linux系统)中,HEAP段是向上增长的(向高地址方向增长),所以如果一段程序中先后声明两个静态变量,则先声明的变量的地址小于后声明的变量的地址,如下面代码所示的字符数组变量buffer[512]和函数指针变量fptr。当buffer发生溢出时就会覆盖了函数指针,这样就改变程序的执行流程,使它跳转到攻击代码。典型的攻击示意图和攻击代码见图2.5和图2.6。

-9-

缓冲区溢出漏洞攻击和检测技术研究

-10-

图2.5 HEAP/BSS攻击时内存示意图

static char buffer[512];

static int(*fptr) (const char* str); Int good(const char* str)

{

...

}

copy(char

* msg)

{

fptr=(int(*)(const char* str))good; strcpy(buffer,msg);

(void)(*fptr)(buffer);

}

main(int argc,char* argv[])

{

if (argc>1)

copy(argv[1]);

}

图2.6 HEAP/BSS攻击示意代码

2.5 函数指针(Function Pointer)

函数在编译时被分配了一个入口地址,这个地址被称为函数的指针。定义一个指针变量指向一个函数,这个变量被称为函数指针(Function Pointers)。函数指针中存放的是这个函数的入口地址。例如,“int (*fptr)(const char* str)”声明了参数为const char*,返回值为int的函数指针变量fptr。当通过函数指针调用一个函数时,主调函数在保存了函数的参数和返回地址后,跳转到函数指针指向的地址继续执行。

南京航空航天大学硕士学位论文

-11-

可以利用缓冲区溢出漏洞覆盖或改写某一函数指针的内容。在某一时刻,当程序通过这个函数指针调用函数时,进程将按照修改的函数指针执行。程序的执行流程就会改变。函数指针可以位于任何空间内(包括堆栈和堆),并且可以指向任何地址空间,因此利用函数指针进行流程跳转具有非常大的灵活性。典型的攻击示意图和攻击示意代码见图2.7和图2.8。

图2.7 函数指针攻击时栈帧示意图

int good (const char

* str)

{

...

}

copy (char * msg)

{

int (*fptr)(const char * str);

char buffer[512]

fptr=(int (*)(const char * str))good;

strcpy (buffer,msg);

(void )(*fptr)(buffer);

}

main (int argc,char * argv[])

{

if (argc>1)

copy (argv[1]);

}

图2.8 函数指针攻击示意代码

缓冲区溢出漏洞攻击和检测技术研究

2.6 长跳转(SETJMP/LONGJMP )

C/C++语言中的goto语句也称为无条件转移语句,不过goto语句是本地的,它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点。为了解决这个限制,C函数库提供了setjmp()和longjmp()函数,它们分别承担非局部标号和goto作用。头文件申明了这些函数。Longjmp() 和setjmp()都要用到一种类型为jmp_buf 的变量,这种变量保存的是地址信息,VC7.0编译器对其在X86下的定义如图2.9。

typedef struct __JUMP_BUFFER

{

unsigned long Ebp;

unsigned long Ebx;

unsigned long Edi;

unsigned long Esi;

unsigned long Esp;

unsigned long Eip;

unsigned long Registration;

unsigned long TryLevel;

unsigned long Cookie;

unsigned long UnwindFunc;

unsigned long UnwindData[6];

}

图2.9 jmp_buf数据结构

如果能在longjmp()函数执行以前覆盖掉jmp_buf,就能重写寄存器EIP。因此当longjmp()函数恢复保存的堆栈栈帧后,程序就可能跳到指定的地方去执行。至于跳转地址,可以是堆栈中,也可以是堆中。典型的攻击示意代码见图2.10。

-12-

南京航空航天大学硕士学位论文

static char buffer[16];

jmp_buf jumper;

int flag;

void g( )

{

flag = 1;

/* 这里可能导致jmpbuf被覆盖 */

strncpy(buffer,argv[1],strlen(argv[1]));

longjmp(jumper,1);

}

void f( )

{

g( );

}

int main(int argc,char* argv[])

{

int value;

flag = 0;

value = setjmp(jumper);

if (value ! = 0)

exit (value) ;

f() ;

return 0;

}

图2.10 长跳转攻击示意代码

2.7 格式化字符串攻击(Format String Attack)

严格地说格式化字符串漏洞并不是缓冲区溢出,但是它会导致与缓冲区溢出同样的问题。在C语言里格式化字符串系列函数按照一定的格式对数据进行输出,可以输出到标准输出,如printf()函数,也可以输出到文件句柄,字符串等。格式化函数包括fprintf(),printf(),sprintf(),snprintf(),vfprintf(),vprintf(),vsprintf(),vsnprintf()等等。

针对格式化字符串的攻击可以归为两类。第一个类别称为是读攻击。以最简单的printf()为例,int printf(const char *format, arg1,arg2,…),printf()函数并不能确定数据参数arg1,arg2…究竟在什么地方结束,不知道参数的个数,只会根据format中的打印格式的数目依次打印堆栈中参数format后面地址的内容,由于参数个数不固定造成访问越界数据。用这种方法获得有价值的数据,并可以为覆盖关键数据做好前期准备。攻击示意代码见图2.11

-13-

缓冲区溢出漏洞攻击和检测技术研究

-14-

图2.11 格式化字符串读攻击示意代码

第二个类别称为是写攻击,造成该安全问题的原因是通过 %n 格式指示符写入的一定数量的字节将被格式字符串函数写入到参数提供的指针中。

%n 功能是将已经输出的字符个数写到参数指定的内存单元中。比如,printf("%d %n \n", i , &num)。如果函数中并没有&num 参数,而用户提供的格式化字符串包括%n 字符,那么printf()函数就会把实参“i ” 后面内存区域的四个字节作为指针,并把读到的字符个数写到指针所指向的内存中,破坏了其它不相关数据,比如函数返回地址,堆栈帧指针等可以改变程序流程的敏感数据,引发安全问题。攻击示意代码见图2.12。

图2.12 格式化字符串写攻击示意代码

int main (void)

{

int i=1

char buf[]="test";

printf ("%s %d \n",buf,i);

printf ("%s %d %d \n",buf,i);

return 0;

}

copy (char * format)

{

int i = 1

printf (format,i)

}

main (int argc,char * argv[])

{

copy ("%d\n");

copy ("%d %n \n");

}

相关主题