防止缓冲区溢出杜绝如今最常见的程序缺陷方案
- 格式:docx
- 大小:20.48 KB
- 文档页数:11
第三讲缓冲区溢出n1 缓冲区溢出问题简介n2 几种典型的缓冲区溢出n3 防范措施缓冲区溢出1 缓冲区溢出问题简介缓冲区溢出是一种常见的漏洞.据统计,通过缓冲区溢出进行的攻击占所有系统攻击总数的80%以上.这种错误的状态发生在写入内存的数据超过了分配给缓冲区的大小的时候,就像一个杯子只能盛一定量的水,如果放到杯子中的水太多,多余的水就会一出到别的地方。
由于缓冲区溢出,相邻的内存地址空间被覆盖,造成软件出错或崩溃。
如果没有采取限制措施,可以使用精心设计的输入数据使缓冲区溢出,从而导致安全问题。
缓冲区溢出缓冲区溢出问题的历史很长一段时间以来,缓冲区溢出都是一个众所周知的安全问题, C程序的缓冲区溢出问题早在70年代初就被认为是C语言数据完整性模型的一个可能的后果。
这是因为在初始化、拷贝或移动数据时,C语言并不自动地支持内在的数组边界检查。
虽然这提高了语言的执行效率,但其带来的影响及后果却是深远和严重的。
•1988年Robert T. Morris的finger蠕虫程序.这种缓冲区溢出的问题使得Internet几乎限于停滞,许多系统管理员都将他们的网络断开,来处理所遇到的问题. •1989年Spafford提交了一份关于运行在VAX机上的BSD版UNIX的fingerd的缓冲区溢出程序的技术细节的分析报告,引起了部分安全人士对这个研究领域的重视•1996年出现了真正有教育意义的第一篇文章, Aleph One在Underground发表的论文详细描述了Linux系统中栈的结构和如何利用基于栈的缓冲区溢出。
缓冲区溢出Aleph One的贡献还在于给出了如何写开一个shell的Exploit的方法,并给这段代码赋予shellcode的名称,而这个称呼沿用至今,我们现在对这样的方法耳熟能详--编译一段使用系统调用的简单的C程序,通过调试器抽取汇编代码,并根据需要修改这段汇编代码。
•1997年Smith综合以前的文章,提供了如何在各种Unix变种中写缓冲区溢出Exploit更详细的指导原则。
一种防止堆缓冲区溢出的有效方法
缓冲区溢出的漏洞随着冯·诺依曼构架的出现就已经开始出现了。
在1988年随着莫里斯互联网蠕虫的广泛传播他们开始声名狼藉。
不幸的是,同样的这种攻击一直持续到今天。
一方面,根据程序运行存储设计的缺陷,攻击者通过各种渠道能够准确无误的攻击目标地址,并让自己的恶意代码来肆意运行,以破坏计算机系统安全;另一方面,对于攻击者的攻击,我们没有有效的方法从根本上来制止。
到目前为止,防范无穷无尽的缓冲区溢出攻击变种的唯一方法是对所有的应用程序进行完善的设计、编码和测试。
相比于其他因素,缓冲区溢出属于攻击中最典型的一类攻击。
对于缓冲区溢出,我们可以分为两类,一类是基于栈缓冲区的溢出,另一类是基于堆的缓冲区溢出,虽然基于栈的攻击仍然是更加频繁和重要,并取得了一定的进展;但基于堆的溢出最近获取了更多注意。
本文详细分析了进程的内存组织形式、堆缓冲区溢出的基本理论和堆缓冲区溢出的各种类型。
总结出堆缓冲区溢出是因为攻击者对堆中的关键数据进行篡改,或向堆缓冲区中注入恶意代码,以破坏计算机系统。
针对攻击者的攻击目标——堆元数据(堆管理结构信息),提出了一种新的堆缓冲区溢出的防范机制,其基本思想是通过分离堆元数据和堆中用户数据来防止堆缓冲区溢出,对堆元数据进行特殊存储,使得攻击者很难对其进行更改,以达到防止堆缓冲区溢出的目标。
实验表明新的堆缓冲区防范机制能够有效地防止堆缓冲区溢出。
缓冲区溢出攻击与防范实验报告——计算机网络(2)班——V200748045黄香娥1·缓冲区溢出的概念:缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据覆盖在合法数据上,理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间想匹配,这就为缓冲区溢出埋下隐患.操作系统所使用的缓冲区又被称为"堆栈". 在各个操作进程之间,指令会被临时储存在"堆栈"当中,"堆栈"也会出现缓冲区溢出。
2·缓冲区溢出的危害:在当前网络与分布式系统安全中,被广泛利用的50%以上都是缓冲区溢出,其中最著名的例子是1988年利用fingerd漏洞的蠕虫。
而缓冲区溢出中,最为危险的是堆栈溢出,因为入侵者可以利用堆栈溢出,在函数返回时改变返回程序的地址,让其跳转到任意地址,带来的危害一种是程序崩溃导致拒绝服务,另外一种就是跳转并且执行一段恶意代码,比如得到shell,然后为所欲为。
3·缓冲区溢出原理:由一个小程序来看://test.c#include "stdio.h"#include "stdlib.h"#include "string.h"void overflow(void){char buf[10];strcpy(buf,"0123456789123456789");}//end overflowint main(void){overflow();return 0;}//end main按F11进入"Step into"调试模式,如下:按F11跟踪进入overflow,让程序停在6,现在再看一下几个主要参数:esp=0x0012ff30,eip发生了变化,其它未变。
c语言缓冲区溢出原理摘要:1.缓冲区溢出概念2.C语言中可能导致缓冲区溢出的原因3.缓冲区溢出的防范方法4.总结正文:正文:缓冲区溢出是计算机科学中的一种常见错误,它在编程中可能导致严重的安全问题。
本文将解释缓冲区溢出的概念,探讨其原因、危害以及如何预防。
一、什么是缓冲区溢出缓冲区溢出是指程序中的缓冲区无法容纳输入的数据,从而覆盖了其他内存区域。
这种情况通常发生在程序没有正确处理输入数据长度的情况下,导致数据超过了缓冲区的容量。
二、缓冲区溢出的原因1.字符串操作函数:在C语言中,一些字符串操作函数(如gets、strcpy 等)没有对输入数据长度进行限制,可能导致缓冲区溢出。
2.动态内存分配:在使用动态内存分配函数(如malloc)分配内存时,如果未正确初始化或超量分配,可能导致缓冲区溢出。
3.函数调用:在调用函数时,如果传入的参数长度超过预期,可能导致缓冲区溢出。
三、缓冲区溢出的危害1.程序崩溃:缓冲区溢出可能导致程序崩溃,因为覆盖的内存区域可能包含程序的重要数据或代码。
2.数据损坏:缓冲区溢出可能导致数据损坏,因为覆盖的内存区域可能包含程序处理数据的正确结果。
3.安全漏洞:缓冲区溢出可能被恶意利用,攻击者可以通过注入恶意数据来覆盖内存中的安全关键数据,如密码、加密密钥等。
四、如何预防缓冲区溢出1.检查输入数据长度:在对输入数据进行处理之前,应检查数据长度,确保不会超过缓冲区容量。
2.使用安全的字符串操作函数:尽量使用带有长度限制的字符串处理函数,如strncpy、strncat等。
3.初始化缓冲区:在使用动态内存分配时,应初始化缓冲区,并确保分配的内存空间足够大。
4.使用边界检查:在编写程序时,对输入数据进行边界检查,确保数据长度符合预期。
常见系统漏洞的处理方案常见的系统漏洞包括操作系统漏洞、网络协议漏洞、应用程序漏洞等,针对这些漏洞的处理方案主要包括以下几点:2.定期漏洞扫描和测试:企业应建立和实施定期的漏洞扫描和渗透测试机制,通过使用专业的漏洞扫描工具或安全服务提供商,对系统进行全面的扫描和测试,及时发现并修复潜在的漏洞。
3.强化访问控制:通过完善的访问控制机制,包括身份验证、权限管理和安全策略等,限制用户的访问权限,防止未经授权的访问和操作。
应该采用最小权限原则,确保用户只能访问他们需要的资源和功能。
4.安全配置和加固:对于操作系统、网络设备、服务器和应用程序等,实施安全配置和加固,禁用或限制不必要的服务和功能,关闭或限制不必要的端口和协议,提高系统的安全性和防护能力。
5.日志管理和监控:建立完善的日志管理和监控机制,记录和分析系统的日志信息,监测和检测异常行为和安全事件。
及时发现和响应安全事件,追踪攻击者的行为,提供安全审计和溯源功能。
6.应用程序安全开发:在应用程序的开发过程中,应采用安全的开发方法和编码规范,进行安全代码审计和漏洞测试。
避免常见的应用程序漏洞,如缓冲区溢出、SQL注入、跨站脚本等,并及时修复已知的漏洞。
7.网络隔离和安全分区:将复杂的网络划分为不同的安全区域,采用防火墙、安全设备和安全策略等措施,实现网络隔离和安全分区,避免攻击者一次性获取全部资源和权限。
8.加密和安全传输:对于重要的数据和敏感信息,应使用加密技术进行保护,包括数据加密、通信加密和存储加密等。
确保数据在传输和存储过程中的安全性和完整性。
9.应急响应和恢复:建立和实施应急响应计划和措施,及时响应安全事件,快速恢复系统和服务。
及时备份和存档重要的数据和系统配置,以便在出现安全事件时能够快速恢复。
综上所述,处理系统漏洞需要综合考虑多个方面,从修复漏洞、加强安全设置、加固系统、加强访问控制、加密传输等多个层面入手,以确保系统的安全性和稳定性。
此外,定期漏洞测试和监控,加强用户意识和培训也是非常重要的。
简述缓冲区溢出攻击的原理以及防范方法
一、缓冲区溢出攻击原理
缓冲区溢出攻击(Buffer Overflow Attack)是一种非法异常的程序运行行为,它发生的目的是让受害者的程序运行出现崩溃,从而获得机器控制权限,可以获取机器中存有的敏感资料,并进行恶意操作,如发送垃圾邮件,拒绝服务攻击(DoS attack),远程控制等行为破坏网络安全。
缓冲区溢出攻击的基本原理,就是恶意程序使用某种方法,将程序缓冲区中存放的数据或者信息溢出,超出缓冲区的容量,而这种溢出的数据又存放了受害者程序控制机器的恶意命令,从而给受害者程序植入恶意代码,使恶意程序获得了机器的控制权限,进而达到攻击系统的目的。
二、防范方法
1、使用受检程序,受检程序是一种编译技术,通过对程序源代码进行类型检查、安全检查等操作,来把漏洞修复好,从而起到防止缓冲区溢出攻击的作用。
2、使用数据流分析技术,它是一种动态分析技术,可以识别出恶意代码并阻止其危害,对程序运行的漏洞进行检查,从而防止攻击者利用缓冲区溢出攻击系统。
3、实行严格的安全审计制度,对程序源码、程序诊断、数据加密技术等进行严格的审计,确保程序运行的安全性,以及防止攻击者利用缓冲区溢出攻击系统。
4、采用虚拟化技术,虚拟化技术可以在不同的安全层次上对程序进行控制,对程序运行的过程进行审查,从而防止攻击者使用缓冲区溢出攻击系统。
5、对网络环境进行安全审计,包括电脑中存在的安全漏洞,系统的安全配置,网络设备的稳定性以及系统的社会工程学攻击等,从而确保网络环境能够不被缓冲区溢出攻击所侵袭。
摘要:正文:大纲:1.引言;随着网络安全技术的飞速发展,缓冲区溢出漏洞已经成为当前最具安全威胁的漏洞之一,缓冲区溢出攻击也成为一种非常有效而常见的攻击方法。
如Internet上的第1例蠕虫(Morris)攻击,就是利用了fingerd的缓冲区溢出漏洞。
SANS评选出的2005年威胁最大的20个漏洞中,有8个跟缓冲区溢出有关。
根据CNCERT最近几周的计算机安全漏洞的统计数据,与缓冲区溢出有关的安全事件占了很大的比例。
这些都充分说明了研究缓冲区溢出的重要性。
本文主要介绍了windows下的缓冲区溢出的相关知识。
2.漏洞原因和原理;2.1 产生原因;当向一个已分配了确定存储空间的缓冲区内复制多于该缓冲区处理能力的数据时,就会发生缓冲区溢出,溢出包括堆溢出和堆栈溢出。
它与程序在内存中的分布有关,而它产生的直接原因是由于C/C++程序中的一些函数调用时,没有进行边界检查,如C函数库中的strcpy(),strcat(),sprintf(),gets()等都是不安全的。
由上面的分析可知要产生缓冲区溢出,需要有几个条件: 1) 程序编译时在堆栈上分配了固定大小的缓冲区,并且在对缓冲区进行访问时没有提供边界检查。
这条在C/C ++语言中就满足,而对于有边界检查的语言,如Pascal 等,就没有这样的溢出问题。
2) 程序调用了没有进行边界检查的函数来访问(写操作) 缓冲区,这些函数没有对访问的缓冲区的大小进行判断。
由于在C语言中,字符串以0字节来标识结尾,其中没有字符串的长度信息,所以几个没有判断字符串长度的字符串拷贝函数就是容易出现问题的函数。
这些函数有: strcat()、strcpy()、sprintf()等。
3) 即使程序使用了上面所说的问题函数也不一定会出现溢出漏洞,漏洞发生的最后一个条件是程序员由于粗心,未检查用户输入数据的长度就将其直接复制到缓冲区中去。
虽然这看起来是一件小事,很容易杜绝。
可惜的是正因为有大量粗心的程序员的存在,使得溢出漏洞变得非常的普遍。
论缓冲区溢出攻击的分析和防范策略摘要:本文主要针对缓冲区所产生的网络安全漏洞等问题进行分析和研究,并对如何维护网络的安全和稳定,保护计算机内数据信息不被外界攻击者攻击破坏而带来不便进行探讨。
并重点论述如何加强对缓冲区的注重和保护,如何对产生的网络安全问题进行及时的防范和解决。
关键词:缓冲区;缓冲区溢出;网络安全;防范策略1引言缓冲区溢出是一种常见且危害很大的系统攻击手段,通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈使程序转而执行其它指令,以达到攻击的目的。
2缓冲区溢出攻击原理分析2.1缓冲区溢出的概念和原理缓冲区是程序运行的时候机器内存中的一个连续块,它保存了给定类型的数据,随着动态分配变量会出现问题。
大多时为了不占用太多的内存,一个有动态分配变量的程序在程序运行时才决定给它们分配多少内存。
这样想下去的话,如果说要给程序在动态分配缓冲区放入超长的数据,它就会溢出了。
一个缓冲区溢出程序使用这个溢出的数据将汇编语言代码放到机器的内存里,通常是产生root 权限的地方,这就不是什么好现象了。
仅仅就单个的缓冲区溢出惹眼,它并不是最大的问题根本所在。
但如果溢出送到能够以root权限运行命令的区域,一旦运行这些命令,那可就等于把机器拱手相让了。
2.2攻击原理引起缓冲区溢出问题的根本原因是C(与其后代C++)本质就是不安全的,没有边界来检查数组和指针的引用,也就是开发人员必须检查边界(而这一行为往往会被忽视),否则会冒遇到问题的风险。
标准C 库中还存在许多非安全字符串操作,包括:strcpy() 、sprintf() 、gets() 等。
当程序写入超过缓冲区的边界时,这就是所谓的“缓冲区溢出”。
发生缓冲区溢出时,会覆盖下一个相邻的内存块。
由于C 语言本质上的不安全性,所以它允许程序随意(或者更准确地说是完全出于偶然)溢出缓冲区。
没有运行时检查来这一防止写入超过缓冲区末尾,所以程序员必须在其自己的代码中执行这一检查,否则继续下去会出现问题。
详解缓冲区溢出攻击以及防范⽅法缓冲区溢出是⼀种在各种操作系统、应⽤软件中⼴泛存在普遍且危险的漏洞,利⽤缓冲区溢出攻击可以导致程序运⾏失败、系统崩溃等后果。
更为严重的是,可以利⽤它执⾏⾮授权指令,甚⾄可以取得系统特权,进⽽进⾏各种⾮法操作。
第⼀个缓冲区溢出攻击--Morris蠕⾍,发⽣在⼗多年前,它曾造成了全世界6000多台⽹络服务器瘫痪。
⼀、缓冲区溢出的原理:当正常的使⽤者操作程序的时候,所进⾏的操作⼀般不会超出程序的运⾏范围;⽽⿊客却利⽤缓冲长度界限向程序中输⼊超出其常规长度的内容,造成缓冲区的溢出从⽽破坏程序的堆栈,使程序运⾏出现特殊的问题转⽽执⾏其它指令,以达到攻击的⽬的。
造成缓冲区溢出的原因是程序中没有仔细检查⽤户输⼊的参数,属于程序开发过程考虑不周到的结果。
当然,随便往缓冲区中填东西造成它溢出⼀般只会出现“分段错误”(Segmentation fault),⽽不能达到攻击的⽬的。
最常见的⼿段是通过制造缓冲区溢出使程序运⾏⼀个⽤户shell,再通过shell执⾏其它命令。
如果该程序属于root且有suid权限的话,攻击者就获得了⼀个有root权限的shell,可以对系统进⾏任意操作了。
缓冲区溢出攻击之所以成为⼀种常见安全攻击⼿段其原因在于缓冲区溢出漏洞普遍并且易于实现。
⽽且缓冲区溢出成为远程攻击的主要⼿段其原因在于缓冲区溢出漏洞给予了攻击者他所想要的⼀切:植⼊并且执⾏攻击代码。
被植⼊的攻击代码以⼀定的权限运⾏有缓冲区溢出漏洞的程序,从⽽得到被攻击主机的控制权。
在1998年Lincoln实验室⽤来评估⼊侵检测的的5种远程攻击中,有2种是缓冲区溢出。
⽽在1998年CERT的13份建议中,有9份是是与缓冲区溢出有关的,在1999年,⾄少有半数的建议是和缓冲区溢出有关的。
在Bugtraq的调查中,有2/3的被调查者认为缓冲区溢出漏洞是⼀个很严重的安全问题。
缓冲区溢出漏洞和攻击有很多种形式,会在第⼆节对他们进⾏描述和分类。
什么是缓冲区溢出?缓冲区以前可能被定义为“包含相同数据类型的实例的一个连续计算机内存块”。
在 C 和 C 中,缓冲区通常是使用数组和诸如 malloc() 和 new 这样的内存分配例程来实现的。
极其常见的缓冲区种类是简单的字符数组。
溢出是指数据被添加到分配给该缓冲区的内存块之外。
如果攻击者能够导致缓冲区溢出,那么它就能控制程序中的其他值。
虽然存在许多利用缓冲区溢出的方法,不过最常见的方法还是“stack-smashing”攻击。
Elias Levy (又名为 Aleph One)的一篇经典文章“Smashing the Stack for Fun and Profit”解释了 stack-smashing 攻击,Elias Levy 是 Bugtraq 邮件列表(请参阅参考资料以获得相关链接)的前任主持人。
为了理解 stack-smashing 攻击(或其他任何缓冲区攻击)是如何进行的,您需要了解一些关于计算机在机器语言级实际如何工作的知识。
在类 UNIX 系统上,每个进程都可以划分为三个主要区域:文本、数据和堆栈。
文本区域包括代码和只读数据,通常不能对它执行写入操作。
数据区域同时包括静态分配的内存(比如全局和静态数据)和动态分配的内存(通常称为堆)。
堆栈区域用于允许函数/方法调用;它用于记录函数完成之后的返回位置,存储函数中使用的本地变量,向函数传递参数,以及从函数返回值。
每当调用一个函数,就会使用一个新的堆栈帧来支持该调用。
了解这些之后,让我们来考察一个简单的程序。
清单 1. 一个简单的程序void function1(int a, int b, int c) {char buffer1[5];gets(buffer1); /* DON'T DO THIS */}void main() {function(1,2,3);}假设使用 gcc 来编译清单 1 中的简单程序,在 X86 上的 Linux 中运行,并且紧跟在对 gets()的调用之后中止。
此时的内存内容看起来像什么样子呢?答案是它看起来类似图 1,其中展示了从左边的低位地址到右边的高位地址排序的内存布局。
内存的底部内存的顶部buffer1 sfp ret a b c<--- 增长 --- [ ] [ ] [ ] [ ] [ ] [ ] ...堆栈的顶部堆栈的底部许多计算机处理器,包括所有 x86 处理器,都支持从高位地址向低位地址“倒”增长堆栈。
因此,每当一个函数调用另一个函数,更多的数据将被添加到左边(低位地址),直至系统的堆栈空间耗尽。
在这个例子中,当 main() 调用function1()时,它将 c 的值压入堆栈,然后压入 b 的值,最后压入 a 的值。
之后它压入 return (ret)值,这个值在 function1()完成时告诉 function1() 返回到 main() 中的何处。
它还把所谓的“已保存的帧指针(saved frame pointer,sfp)”记录到堆栈上;这并不是必须保存的内容,此处我们不需要理解它。
在任何情况下,function1()在启动以后,它会为 buffer1()预留空间,这在图 1 中显示为具有一个低地址位置。
现在假设攻击者发送了超过 buffer1() 所能处理的数据。
接下来会发生什么情况呢?当然,C 和 C 程序员不会自动检查这个问题,因此除非程序员明确地阻止它,否则下一个值将进入内存中的“下一个”位置。
那意味着攻击者能够改写sfp(即已保存的帧指针),然后改写 ret(返回地址)。
之后,当 function1() 完成时,它将“返回”——不过不是返回到 main(),而是返回到攻击者想要运行的任何代码。
通常攻击者会使用它想要运行的恶意代码来使缓冲区溢出,然后攻击者会更改返回值以指向它们已发送的恶意代码。
这意味着攻击者本质上能够在一个操作中完成整个攻击!Aleph On 的文章(请参阅参考资料)详细介绍了这样的攻击代码是如何创建的。
例如,将一个 ASCII 0 字符压入缓冲区通常是很困难的,而该文介绍了攻击者一般如何能够解决这个问题。
除了 smashing-stack 和更改返回地址外,还存在利用缓冲区溢出缺陷的其他途径。
与改写返回地址不同,攻击者可以 smashing-stack(使堆栈上的缓冲区溢出),然后改写局部变量以利用缓冲区溢出缺陷。
缓冲区根本就不必在堆栈上——它可以是堆中动态分配的内存(也称为“malloc”或“new”区域),或者在某些静态分配的内存中(比如“global”或“static”内存)。
基本上,如果攻击者能够溢出缓冲区的边界,麻烦或许就会找上你了。
然而,最危险的缓冲区溢出攻击就是 stack-smashing 攻击,因为如果程序对攻击者很脆弱,攻击者获得整个机器的控制权就特别容易。
为什么缓冲区溢出如此常见?在几乎所有计算机语言中,不管是新的语言还是旧的语言,使缓冲区溢出的任何尝试通常都会被该语言本身自动检测并阻止(比如通过引发一个异常或根据需要给缓冲区添加更多空间)。
但是有两种语言不是这样:C 和 C 语言。
C 和 C 语言通常只是让额外的数据乱写到其余内存的任何位置,而这种情况可能被利用从而导致恐怖的结果。
更糟糕的是,用 C 和 C 编写正确的代码来始终如一地处理缓冲区溢出则更为困难;很容易就会意外地导致缓冲区溢出。
除了 C 和 C 使用得非常广泛外,上述这些可能都是不相关的事实;例如,Red Hat Linux 7.1 中86% 的代码行都是用 C 或 C 编写的。
因此,大量的代码对这个问题都是脆弱的,因为实现语言无法保护代码避免这个问题。
在 C 和 C 语言本身中,这个问题是不容易解决的。
该问题基于 C 语言的根本设计决定(特别是 C 语言中指针和数组的处理方式)。
由于 C 是最兼容的C 语言超集,它也具有相同的问题。
存在一些能防止这个问题的 C/C 兼容版本,但是它们存在极其严重的性能问题。
而且一旦改变 C 语言来防止这个问题,它就不再是 C 语言了。
许多语言(比如 Java 和 C#)在语法上类似 C,但它们实际上是不同的语言,将现有 C 或 C 程序改为使用那些语言是一项艰巨的任务。
然而,其他语言的用户也不应该沾沾自喜。
有些语言存在允许缓冲区溢出发生的“转义”子句。
Ada 一般会检测和防止缓冲区溢出(即针对这样的尝试引发一个异常),但是不同的程序可能会禁用这个特性。
C# 一般会检测和防止缓冲区溢出,但是它允许程序员将某些例程定义为“不安全的”,而这样的代码可能会导致缓冲区溢出。
因此如果您使用那些转义机制,就需要使用 C/C 程序所必须使用的相同种类的保护机制。
许多语言都是用 C 语言来实现的(至少部分是用 C 语言来实现的),并且用任何语言编写的所有程序本质上都依赖用 C 或 C 编写的库。
因此,所有程序都会继承那些问题,所以了解这些问题是很重要的。
导致缓冲区溢出的常见 C 和 C 错误从根本上讲,在程序将数据读入或复制到缓冲区中的任何时候,它需要在复制之前检查是否有足够的空间。
能够容易看出来的异常就不可能会发生——但是程序通常会随时间而变更,从而使得不可能成为可能。
遗憾的是,C 和 C 附带的大量危险函数(或普遍使用的库)甚至连这点(指检查空间)也无法做到。
程序对这些函数的任何使用都是一个警告信号,因为除非慎重地使用它们,否则它们就会成为程序缺陷。
您不需要记住这些函数的列表;我的真正目的是说明这个问题是多么普遍。
这些函数包括 strcpy(3)、strcat(3)、sprintf(3)(及其同类 vsprintf(3))和 gets(3)。
scanf()函数集(scanf(3)、fscanf(3)、sscanf(3)、vscanf(3)、vsscanf(3) 和 vfscanf(3))可能会导致问题,因为使用一个没有定义最大长度的格式是很容易的(当读取不受信任的输入时,使用格式“%s”总是一个错误)。
其他危险的函数包括 realpath(3)、getopt(3)、getpass(3)、streadd(3)、strecpy(3) 和 strtrns(3)。
从理论上讲,snprintf()应该是相对安全的——在现代 GNU/Linux 系统中的确是这样。
但是非常老的 UNIX 和 Linux 系统没有实现 snprintf() 所应该实现的保护机制。
Microsoft 的库中还有在相应平台上导致同类问题的其他函数(这些函数包括wcscpy()、_tcscpy()、_mbscpy()、wcscat()、_tcscat()、_mbscat() 和CopyMemory())。
注意,如果使用 Microsoft 的 MultiByteToWideChar() 函数,还存在一个常见的危险错误——该函数需要一个最大尺寸作为字符数目,但是程序员经常将该尺寸以字节计(更普遍的需要),结果导致缓冲区溢出缺陷。
另一个问题是 C 和 C 对整数具有非常弱的类型检查,一般不会检测操作这些整数的问题。
由于它们要求程序员手工做所有的问题检测工作,因此以某种可被利用的方式不正确地操作那些整数是很容易的。
特别是,当您需要跟踪缓冲区长度或读取某个内容的长度时,通常就是这种情况。
但是如果使用一个有符号的值来存储这个长度值会发生什么情况呢——攻击者会使它“成为负值”,然后把该数据解释为一个实际上很大的正值吗?当数字值在不同的尺寸之间转换时,攻击者会利用这个操作吗?数值溢出可被利用吗?有时处理整数的方式会导致程序缺陷。
防止缓冲区溢出的新技术当然,要让程序员不犯常见错误是很难的,而让程序(以及程序员)改为使用另一种语言通常更为困难。
那么为何不让底层系统自动保护程序避免这些问题呢?最起码,避免 stack-smashing 攻击是一件好事,因为 stack-smashing 攻击是特别容易做到的。
一般来说,更改底层系统以避免常见的安全问题是一个极好的想法,我们在本文后面也会遇到这个主题。
事实证明存在许多可用的防御措施,而一些最受欢迎的措施可分组为以下类别:基于探测方法(canary)的防御。
这包括 StackGuard(由 Immunix 所使用)、ProPolice(由 OpenBSD 所使用)和 Microsoft 的 /GS 选项。
非执行的堆栈防御。
这包括 Solar Designer 的 non-exec 补丁(由OpenWall 所使用)和 exec shield(由 Red Hat/Fedora 所使用)。
其他方法。
这包括 libsafe(由 Mandrake 所使用)和堆栈分割方法。
遗憾的是,迄今所见的所有方法都具有弱点,因此它们不是万能药,但是它们会提供一些帮助。