各位老铁们好,相信很多人对服务器溢出都不是特别的了解,因此呢,今天就来为大家分享下关于服务器溢出以及方舟传出可靠缓冲区溢出的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!
一、网络中什么是溢出啊
溢出
目录·【简介】
·【内存溢出】
·为什么会出现内存溢出问题
·如何解决溢出内存问题
·【缓冲区溢出】
·缓冲区溢出分类
·为什么缓冲区溢出如此常见
·防止缓冲区溢出的新技术
【简介】
溢出是*利用操作系统的漏洞,专门开发了一种程序,加相应的参数运行后,就可以得到你电脑具有管理员资格的控制权,你在你自己电脑上能够运行的东西他可以全部做到,等于你的电脑就是他的了。在*频频攻击、在系统漏洞层出不穷的今天,作为网络管理员、系统管理员的我们虽然在服务器的安全上都下了不少功夫:诸如,及时的打上系统安全补丁、进行一些常规的安全配置,但是仍然不太可能每台服务器都会在第一时间内给系统打上全新补丁。因此我们必需要在还未被*之前,通过一些系列安全设置,来将*者们挡在“安全门”之外。
【内存溢出】
内存溢出已经是软件开发历史上存在了近40年的“老大难”问题,象在“红色代码”病*事件中表现的那样,它已经成为*攻击企业网络的“*”。
如在一个域中输入的数据超过了它的要求就会引发数据溢出问题,多余的数据就可以作为指令在计算机上运行。据有关安全小组称,操作系统中超过50%的安全漏洞都是由内存溢出引起的,其中大多数与微软的技术有关。
微软的软件是针对台式机开发的,内存溢出不会带来严重的问题。但现在台式机一般都连上了互联网,内存溢出就为*的*提供了便利条件。
为什么会出现内存溢出问题
导致内存溢出问题的原因有很多,比如:
(1)使用非类型安全(non-type-safe)的语言如 C/C++等。
(2)以不可靠的方式存取或者复制内存缓冲区。
(3)编译器设置的内存缓冲区太靠近关键数据结构。
下面来分析这些因素:
1.内存溢出问题是 C语言或者 C++语言所固有的缺陷,它们既不检查数组边界,又不检查类型可靠*(type-safety)。众所周知,用 C/C++语言开发的程序由于目标代码非常接近机器内核,因而能够访问内存和寄存器,这种特*大大提升了 C/C++语言代码的*能。只要合理编码,C/C++应用程序在执行效率上必然优于其它高级语言。然而,C/C++语言导致内存溢出问题的可能*也要大许多。其他语言也存在内容溢出问题,但它往往不是程序员的失误,而是应用程序的运行时环境出错所致。
2.当应用程序读取用户(也可能是恶意攻击者)数据,试图复制到应用程序开辟的内存缓冲区中,却无法保证缓冲区的空间足够时(换言之,假设代码申请了 N字节大小的内存缓冲区,随后又向其中复制超过 N字节的数据)。内存缓冲区就可能会溢出。想一想,如果你向 12盎司的玻璃杯中倒入 16盎司水,那么多出来的 4盎司水怎么办?当然会满到玻璃杯外面了!
3.最重要的是,C/C++编译器开辟的内存缓冲区常常邻近重要的数据结构。现在假设某个函数的堆栈紧接在在内存缓冲区后面时,其中保存的函数返回就会与内存缓冲区相邻。此时,恶意攻击者就可以向内存缓冲区复制大量数据,从而使得内存缓冲区溢出并覆盖原先保存于堆栈中的函数返回。这样,函数的返回就被攻击者换成了他指定的数值;一旦函数调用完毕,就会继续执行“函数返回”处的代码。非但如此,C++的某些其它数据结构,比如 v-table、例外事件处理程序、函数指针等,也可能受到类似的攻击。
好,闲话少说,现在来看一个具体的例子。
请思考:以下代码有何不妥之处?
void CopyData(char*szData){
char cDest[32];
strcpy(cDest,szData);
//处理 cDest
...
}
奇怪,这段代码好像没什么不对劲啊!确实,只有调用上述 CopyData()才会出问题。例如:这样使用 CopyData()是安全的:
char*szNames[]={"Michael","Cheryl","Blake"};
CopyData(szName[1]);
为什么呢?因为数组中的姓名("Michael"、"Cheryl"、"Blake")都是字符串常量,而且长度都不超过 32个字符,用它们做 strcpy()的参数总是安全的。再假设 CopyData的唯一参数 szData来自 socket套接字或者文件等不可靠的数据源。由于 strcpy并不在乎数据来源,只要没遇上空字符,它就会一个字符一个字符地复制 szData的内容。此时,复制到 cDest的字符串就可能超过 32字符,进而导致内存缓冲区 cDest的溢出;溢出的字符就会取代内存缓冲区后面的数据。不幸的是,CopyData函数的返回也在其中!于是,当 CopyData函数调用完毕以后,程序就会转入攻击者给出的“返回”,从而落入攻击者的圈套!授人以柄,惨!
前面提到的其它数据结构也可能受到类似的攻击。假设有人利用内存溢出漏洞覆盖了下列 C++类中的 v-table:
void CopyData(char*szData){
char cDest[32];
CFoo foo;
strcpy(cDest,szData);
foo.Init();
}
与其它 C++类一样,这里的 CFoo类也对应一个所谓的 v-table,即用于保存一个类的全部方法的列表。若攻击者利用内存溢出漏洞偷换了 v-table的内容,则 CFoo类中的所有方法,包括上述 Init()方法,都会指向攻击者给出的,而不是原先 v-table中的方法。顺便说一句,即使你在某个 C++类的源代码中没有调用任何方法,也不能认为这个类是安全的,因为它在运行时至少需要调用一个内部方法——析构器(destructor)!当然,如果真有一个类没有调用任何方法,那么它的存在意义也就值得怀疑了。
如何解决溢出内存问题
下面讨论内存溢出问题的解决和预防措施。
1、改用受控代码
2002年 2月和 3月,微软公司展开了 Microsoft Windows Security Push活动。在此期间,我所在的小组一共培训了超过 8500人,教授他们如何在设计、测试和文档编制过程中解决安全问题。在我们向所有程序设计人员提出的建议中,有一条就是:紧跟微软公司软件开发策略的步伐,将某些应用程序和工具软件由原先基于本地 Win32的 C++代码改造成基于.NET的受控代码。我们的理由很多,但其中最根本的一条,就是为了解决内存溢出问题。基于受控代码的软件发生内存溢出问题的机率要小得多,因为受控代码无法存取系统指针、寄存器或者内存。作为开发人员,你应该考虑(至少是打算)用受控代码改写某些应用程序或工具软件。例如:企业管理工具就是很好的改写对象之一。当然,你也应该很清楚,不可能在一夜之间把所有用 C++开发的软件用 C#之类的受控代码语言改写。
2、遵守*规则
当你用 C/C++书写代码时,应该处处留意如何处理来自用户的数据。如果一个函数的数据来源不可靠,又用到内存缓冲区,那么它就必须严格遵守下列规则:
必须知道内存缓冲区的总长度。
检验内存缓冲区。
提高警惕。
让我们来具体看看这些“*规则”:
(1)必须知道内存缓冲区的总长度。
类似这样的代码就有可能导致 bug:
void Function(char*szName){
char szBuff[MAX_NAME];
//复制并使用 szName
strcpy(szBuff,szName);
}
它的问题出在函数并不知道 szName的长度是多少,此时复制数据是不安全的。正确的做法是,在复制数据前首先获取 szName的长度:
void Function(char*szName, DWORD cbName){
char szBuff[MAX_NAME];
//复制并使用 szName
if(cbName< MAX_NAME)
strcpy(szBuff,szName);
}
这样虽然有所改进,可它似乎又过于信任 cbName了。攻击者仍然有机会伪造 czName和 szName两个参数以谎报数据长度和内存缓冲区长度。因此,你还得检检这两个参数!
(2)检验内存缓冲区
如何知道由参数传来的内存缓冲区长度是否真实呢?你会完全信任来自用户的数据吗?通常,答案是否定的。其实,有一种简单的办法可以检验内存缓冲区是否溢出。请看如下代码片断:
void Function(char*szName, DWORD cbName){
char szBuff[MAX_NAME];
//检测内存
szBuff[cbName]= 0x42;
//复制并使用 szName
if(cbName< MAX_NAME)
strcpy(szBuff,szName);
}
这段代码试图向欲检测的内存缓冲区末尾写入数据 0x42。你也许会想:真是多此一举,何不复制内存缓冲区呢?事实上,当内存缓冲区已经溢出时,一旦再向其中写入常量值,就会导致程序代码出错并中止运行。这样在开发早期就能及时发现代码中的 bug。试想,与其让攻击者得手,不如及时中止程序;你大概也不愿看到攻击者随心所欲地向内存缓冲区复制数据吧。
(3)提高警惕
虽然检验内存缓冲区能够有效地减小内存溢出问题的危害,却不能从根本上避免内存溢出攻击。只有从源代码开始提高警惕,才能真正免除内存溢出攻击的危胁。不错,上一段代码已经很对用户数据相当警惕了。它能确保复制到内存缓冲区的数据总长度不会超过 szBuff的长度。然而,某些函数在使用或复制不可靠的数据时也可能潜伏着内存溢出漏洞。为了检查你的代码是否存在内存溢出漏洞,你必须尽量追踪传入数据的流向,向代码中的每一个假设提出质疑。一旦这么做了,你将会意识到其中某些假设是错误的;然后你还会惊讶地叫道:好多 bug呀!
在调用 strcpy、strcat、gets等经典函数时当然要保持警惕;可对于那些所谓的第 n版(n-versions) strcpy或 strcat函数——比如 strncpy或 strncat(其中 n= 1,2,3……)——也不可轻信。的确,这些改良版本的函数是安全一些、可靠一些,因为它们限制了进入内存缓冲区的数据长度;然而,它们也可能导致内存溢出问题!请看下列代码,你能指出其中的错误吗?
#define SIZE(b)(sizeof(b))
char buff[128];
strncpy(buff,szSomeData,SIZE(buff));
strncat(buff,szMoreData,SIZE(buff));
strncat(buff,szEvenMoreData,SIZE(buff));
给点提示:请注意这些字符串函数的最后一个参数。怎么,弃权?我说啊,如果你是执意要放弃那些“不安全”的经典字符串函数,并且一律改用“相对安全”的第 n版函数的话,恐怕你这下半辈子都要为了修复这些新函数带来的新 bug而疲于奔命了。呵呵,开个玩笑而已。为何这么说?首先,它们的最后一个参数并不代表内存缓冲区的总长度,它们只是其剩余空间的长度;不难看出,每执行完一个 strn...函数,内存缓冲区 buff的长度就减少一些。其次,传递给函数的内存缓冲区长度难免存在“大小差一”(off-by-one)的误差。你认为在计算 buff的长度时包括了字符串末尾的空字符吗?当我向听众提出这个问题时,得到的肯定答复和否定答复通常是 50比 50,对半开。也就是说,大约一半人认为计算了末尾的空字符,而另一半人认为忽略了该字符。最后,那些第 n版函数所返回的字符串不见得以空字符结束,所以在使用前务必要仔细阅读它们的说明文档。
3、编译选项/GS
“/GS”是 Visual C++.NET新引入的一个编译选项。它指示编译器在某些函数的堆栈帧(stack-frames)中插入特定数据,以帮助消除针对堆栈的内存溢出问题隐患。切记,使用该选项并不能替你清除代码中的内存溢出漏洞,也不可能消灭任何 bug。它只是亡羊补牢,让某些内存溢出问题隐患无法演变成真正的内存溢出问题;也就是说,它能防止攻击者在发生内存溢出时向进程中插入和运行恶意代码。无论如何,这也算是小小的安全保障吧。请注意,在新版的本地 Win32 C++中使用 Win32应用程序向导创建新项目时,默认设置已经打开了此选项。同样,Windows.NET Server环境也默认打开了此选项。关于/GS选项的更多信息,请参考 Brandon Bray的《Compiler Security Checks In Depth》一书。
所谓定点数溢出是指定点数的运算结果的绝对值大于计算机能表示的最大数的绝对值。浮点数的溢出又可分为“上溢出”和“下溢出”两种,“上溢出”与整数、定点数的含义相同,“下溢出”是指浮点数的运算结果的绝对值小于机器所能表示的最小数绝对值,此时将该运算结果处理成机器零。若发现溢出(上溢出),运算器将产生溢出标志或发出中断请求,当溢出中断未被屏蔽时,溢出中断信号的出现可中止程序的执行而转入溢出中断处理程序。<BR><BR>
例如:有两个数0.1001111和0.1101011相加,其结果应为1.0111010。由于定点数计算机只能表示小于1的数,如果字长只有8位,那么小数点前面的1,会因没有触发器存放而丢失。这样,上述两个数在计算机中相加,其结果变为0.0111010。又如,有两个数0.0001001和0.00001111相乘,其结果应为0.00000000111111,若字长只有8位,则结果显示为0.0000000,后面的1个0和6个1全部丢失,显然这个结果有误差。计算机的任何运算都不允许溢出,除非专门利用溢出做判断,而不使用所得的结果。所以,当发生和不允许出现的溢出时,就要停机或转入检查程序,以找出产生溢出的原因,做出相应的处理。
【缓冲区溢出】
缓冲区是用户为程序运行时在计算机中申请的一段连续的内存,它保存了给定类型的数据。缓冲区溢出指的是一种常见且危害很大的系统攻击手段,通过向程序的缓冲区写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其他的指令,以达到攻击的目的。更为严重的是,缓冲区溢出攻击占了远程网络攻击的绝大多数,这种攻击可以使得一个匿名的Inter用户有机会获得一台主机的部分或全部的控制权!由于这类攻击使任何人都有可能取得主机的控制权,所以它代表了一类极其严重的安全威胁。
缓冲区溢出攻击的目的在于扰乱具有某些特权运行的程序的功能,这样可以使得攻击者取得程序的控制权,如果该程序具有足够的权限,那么整个主机就被控制了。一般而言,攻击者攻击root程序,然后执行类似“exec(sh)”的执行代码来获得root的shell。为了达到这个目的,攻击者必须达到如下的两个目标:在程序的空间里安排适当的代码;通过适当地初始化寄存器和存储器,让程序跳转到事先安排的空间执行。根据这两个目标,可以将缓冲区溢出攻击分为以下3类。
二、方舟传出可靠缓冲区溢出
缓冲区溢出的目的在于扰乱具有某些特权运行程序的功能,这样就可以让攻击者取得程序的控制权,从而进行缓冲区溢出攻击行为。假如该程序具有足够的权限,那么整个主机甚至服务器就被控制了。
解决缓冲区溢出方法:1.植人法:攻击者向被攻击的程序输人一个字符串,程序会把这个字符串放到缓冲区里。这个字符串所包含的数据是可以在这个被攻击的硬件平台运行的指令流。在这里攻击者用被攻击程序的缓冲区来存放攻击代码。
2.利用已经存在的代码:很多时候攻击者所要的代码已经存在于被攻击的程序中了,攻击者所要做的只是对代码传递一些参数,然后使程序跳转到想要执行的代码那里。比方说,攻击代码要求执行“ex-ec("/bin/sh")",而在libc库中的代码执行“exec(arg)",其中arg是一个指向字符串的指针参数,那么攻击者只要把传人的参数指针改为指向“/bin/sh",然后跳转到libc库中相应的指令序列即可。
三、服务器故障和服务器资源共享故障的区别
前者包含后者
服务器软件故障是在服务器故障中占有比例最高的部份,约占70%,解决的过程必须更加深思熟虑。导致服务器出现软件故障的原因有很多,最常见的是服务器BIOS版本太低、服务器的管理软件或服务器的驱动程序有BUG、应用程序有冲突及人为造成的软件故障。下面分别举例说明各类软件故障的维修方法。
有一台HP LH6000R服务器,配置为双PIII XEON 700带2M高速缓存的CPU、512M内存。开机后,系统日志报电压调节模块异常(VRM)的错误,报错的信息是:“Voltage Regulator Module(VRM) over/under-voltage 2.88V/0V”。从表面来看,极有可能是服务器的电压调节模块或其它硬件出现故障,极容易导致维护人员认为是硬件故障。维护人员立刻使用其它LH6000R上的硬件来测试,发现即使使用新的配件,此服务器依然报VRM错。就在一筹莫展的时候,维修工程师带来了最新的CPU管理板(CPU Management Control)的固件(FIRMWARE),于是升级了CPU管理板块的FIRMWARE后,服务器恢复立即正常。
FIRMWARE升级方法是,在服务器的N*IGATOR(导航光盘)中提取CPU管理板(CMC)FIRMWARE的刷新程序,程序为FLASH.EXE,然后将从网上的LH6KC.BIN(CPU管理板的FIRMWARE)拷贝到一张DOS启动盘上,用这张盘启动服务器。然后在DOS下运行”FLASH/CMC A:LH6KC.BIN”,刷新完成后重新启动服务器后即可。这种升级方法也适合刷新系统BIOS等,只是FLASH命令的参数不同以及更新FIRMWARE及BIOS文件名不同,参数请参考服务器的说明。
任何一款服务器的FIRMWARE及BIOS都会有不同的BUG,因为BUG在所难免,所以我们不能错误地认为服务器的BIOS程序就很完善,而应该经常更新服务器的FIRMWARE及BIOS,只是在升级之前应该小心谨慎,错误的升级方法会导致严重的后果。
目前流行的中高档服务器都拥有强大的管理程序,为客户提供了方便的管理途径;服务器也拥有各种操作系统下的驱动程序,方便了客户在各种操作系统中的使用。但是,世上任何一款程序都会有一些BUG,这些BUG将影响用户使用。但是服务器厂商总是会在第一时间内开发出新的程序,客户只需要及时更新这些程序就可以避免这类故障。
当服务器的软件故障为此类时,表现的现象也不尽相同。一般来说,管理程序BUG会导致系统速度变慢,CPU占用率变高,无法正常使用某些功能等;驱动程序的BUG会导*机、与某些软件有冲突,磁盘工作不稳定等。查看管理程序是否出错的最好的办法就是在系统中首先禁止此类管理工具,再观察服务器是否还是异常。由于管理工具是随着系统启动而启动的,所以应首先避免它的启动。以WINDOWS NT4为例,就首先在管理工具服务中禁用某些服务器软件服务,再修改注册表中的启动项即可。如果是驱动程序有问题的话,就以安全模式进入系统,看是否正常。但是需要注意的是,在安全模式中,系统速度变慢是正常的(特别是磁盘I/O方面)。
服务器的管理人员就应该经常在服务器网站上最新的管理工具程序及驱动程序。这样会减少很大一部份软件故障的发生。
相比之下,软件冲突造成的故障判断比较困难,需要管理人员有比较丰富的经验以及敏锐的观察力。
曾经有一位朋友告诉我说,他有一台浪潮的服务器无法安装SQL SERVER 2000,已经重装N次NT了,排除是系统故障。而这唯一的服务器又将作为非常重要数据库服务器,因此非常着急。于是我陪着朋友去了他的公司查看。
这台服务器所在的机房是非常标准、完善的机房,我检查了这台服务器的情况,发现并没有硬件上的故障,于是排除了光驱读盘力差的可能。但是,朋友刻的SQL SERVER 2000光盘引起了我的怀疑,我让他拿出了正版的SQL SERVER安装,结果还是不行。
在安装的过程中,没有出现丝毫错误,可就是在运行的时候会自动退出,没有任何提示。但是,我在管理工具中的事件查看器的系统日志中却发现了一条信息:windata.exe导致一个无效的数据溢出。Windata是朋友自己编写的一个程序,而且是随操作系统启动而启动的程序。我立即结束掉这个进程后,再运行SQL一切正常。
对于此类软件故障,操作员最好先查看有关的日志,看看系统中是否有可疑的进程。目前的服务器无论是高端还是低端,对于SQL等标准程序的支持是相当可靠的,所以排除的重点就是结束可疑进程。
还有一种软件故障是人为因素造成的,它一般是人为误操作(包括没按操作流程的操作)、意外关机(包括电源突然不供电)或非正常关闭应用程序造成的。
人为误操作因素只要加强管理都可以避免此类故障发生。在这里就详细说明意外关机或非正常关闭程序造成故障的方法。
正常关闭系统程序非常重要,尤其是WEB服务器。我的一个朋友就是因为没有正常关闭系统程序而经历了一次数据损坏甚至丢失的经历。我的朋友是使用的HP web hosting server liance,因此我向他提供了一些使用规则。
这些方法对于服务器的维护非常有效,主要包括了正确的关闭系统程序、怎样避免数据丢失以及非正常关闭系统后的恢复方法。下面以我朋友的HP web hosting server liance为例(使用的是UNIX,但思路对于其它操作系统均有效)。
正确关机的过程包括通过按动Power键来使系统断电,你应该一直按住电源开关持续几秒钟才能使系统进入正常的关闭过程中。
另外,为了避免数据丢失,你应该按照如下的步骤操作:
·经常备份Web Hosting Server Appliance的数据,可以通过网络管理界面来完成。
·安装第二块硬盘并与原来的硬盘设置成镜像,
一旦Server Apliance未能正确关闭,并无法重起,请按如下操作恢复:
1.当liance已经断电时,连接一条非modem的串口线(可在机盒中找到)到背面的控制口上。
2.连接串口线的另一头到一台运行Windows的PC的串口上。
3.运行超级程序(HyperTerminal),并设置端口的参数为19200, n-8-1, Flow control- None.你可以看到liance的控制提示,并要求你输入管理员口令。
4.重起liance,等到提示“LILO boot:”,按住Tab键5秒钟,直到提示变为“boot:”。
5.敲入"emergency"并回车。此时需要耐心等待几分钟。然后,登录提示又将出现,此时,LCD屏又能正常工作了。
6.在LCD屏上选择一个随机的密码(此密码只是用于紧急恢复时用)
翻至Defaults…并按右箭头键选中。
翻至Root Password…并按右箭头键选中。
翻至Random并按右箭头键选中,会提示一个随机产生的密码。
记下此密码。
翻至Yes并按右箭头键选中,系统密码会立刻更改。
7.回到超级的控制屏,登录liance,用"root"用户名和刚才的密码,此时会出现“#”提示。
8.为修复分区,请按如下方法操作:
对于sa1100,按顺序输入:
[…]#: fsck/dev/hda5
[…]#: fsck/dev/hda6
[…]#: fsck/dev/hda7
对于sa1120,按顺序输入:
[…]#: fsck/dev/sda5
[…]#: fsck/dev/sda6
[…]#: fsck/dev/sda7
当所有的分区都被修复后,应回到“#”提示符下。
9.输入“reboot”重新启动系统。
如果系统仍无法启动,请记录下控制屏显示的内容并求助技术支持。
对于服务器的软件故障,只要平时管理员注意维护,应该是可以避免的。