《安全编程技术》实验一 缓冲区溢出

一、实验目的与要求:

(一)实验目的

该实验为验证性实验,实验目的如下:

  • 掌握缓冲区溢出的基本原理。
  • 掌握预防缓冲区溢出的方法,并且在实际编程中严格遵循这些方法。

(二)实验要求

  • 本实验一人一组,编程语言为C。
  • 要求学生掌握缓冲区溢出的基本原理,并熟练掌握C语言编程。
  • 要求学生能够编程实现一个简单的缓冲区溢出实例。
  • 实验报告要求:
  • 实验报告要求包括实验目的、实验要求、实验内容、实验结果分析和实验体会等,重点在于实验内容和实验结果分析。实验报告要求上传和打印。
  • 理论联系实际,认真分析实验中缓冲区溢出的原因,给出预防和防御缓冲区溢出的方法。
  • 写出实验过程中的心得和体会。
  • 实验报告撰写规范请见附。

 

二、实验内容、步骤及结论

(一)实验内容

  • 理解缓冲区溢出的基本原理。
  • 利用函数strcpy()编程实现一个简单的缓冲区溢出实例。
  • 总结预防缓冲区溢出的方法。

(二)代码

#include <stdio.h>
#include <string.h>

int verify(char *password);

int main() 
{
	char password[8];
	printf("Please input your password:\n");
	/*
	只要输入的密码保证前8位和第17到第24位是一致的,都可以实现缓冲区溢出的攻击
	876543210000000087654321
	aaaaaaaa12344321aaaaaaaa0000
	*/
	scanf("%s",password);
	if(verify(password)) 
		printf("OK\n");
	else
		printf("NO\n");
	return 0;
}

int verify(char *password) 
{
	int verify = 0;
	int flag = 1;
	char PASSWORD[8] = {'1','2','3','4','5','6','7','8'};
	char buffer[8] = {0};
	strcpy(buffer,password);
	for( int i = 0; i < 8; i++) 
	{
		if(buffer[i]!=PASSWORD[i]) 
		{
			flag = 0;
			break;
		}
	}
	if(flag == 1)
		verify = 1;
	return verify;
}

(三)运行

该段代码旨在构建一个类似登录验证的程序。在函数verify中,预先设置PASSWORD为“12345678”,同时定义了8个单位的字符数组buffer[8]。使用strcpy函数将传入的password的值复制到buffer中,之后将buffer与PASSWORD中的内容进行对比,如果8位字符都一致,则返回1,否则返回0。

在主函数中,输入password的值,使用verify进行验证,如果返回值为1,则输出“OK”,否则输出“NO”。

  1.对上述漏洞程序进行攻击,如下:

   

由上图可知,当输入密码是“876543210000000087654321”或“aaaaaaaa12344321aaaaaaaa0000”时,主函数打印了 “OK”,实现了缓冲区溢出攻击。

  2.实验结果分析

  • 当不进行缓冲区攻击时,只输入正常的8位密码,该程序可以进行字符的校验:

   

输入密码“12345678”,则打印“OK”,除此之外都会打印“NO”,符合预期的设想。

  • 如果进行缓冲区溢出攻击,在输入密码时额外输入一些字符,比如上面使用的“876543210000000087654321”,则可以实现攻击。

下面使用GDB,来分析攻击的原理。

(Linux中输入“8765432187654321”,

Windows中输入“876543210000000087654321”)



在第29行strcpy(buffer,password)处设置断点,打印buffer和PASSWORD的地址和数值,在数值方面buffer值目前均为0,PASSWORD的值是“12345678”;而从地址上看,发现PASSWORD的起始地址与buffer刚好相差8字节,(Windows中相差16字节,输入“876543210000000087654321”)所以当向buffer中写入超过16字节内容时,会对PASSWORD的值产生影响。


当进入比较判断时,再次查看buffer和PASSWORD的值,因为buffer拷贝了password前8个字符,所以变成了“87654321”;另一方面,在进行strcpy(buffer,password)时,超出8个字节以后的内容,也就是最后8位“87654321”,覆盖了PASSWORD原来的内容,这样就会导致buffer内容与PASSWORD内容一致,所以在主函数中会打印出“OK”。
根据上面的原理,还可以输入其他密码,都可以成功攻击。

比如输入“aaaaaaaa12344321aaaaaaaa0000”,只要输入的密码保证前8位和第17到第24位是一致的,都可以实现缓冲区溢出的攻击。


最后还有一点,在编译c文件时,需要额外加上 -z execstack -fno-stack-protector ,目的是为了关闭Linux系统对栈溢出的保护机制,并且使得栈中的代码可以执行。如果没有加上上面的语句,在进行缓冲区溢出攻击时,系统会自我终止,无法达成溢出攻击的目的。

 

三、实验体会

在进行程序设计时,为了防范缓冲区溢出攻击,可以采用哪些方法?

  • 缓冲区溢出攻击是一种非常常见的信息安全攻击手段,在各种操作系统、应用软件中广泛存在。缓冲区溢出攻击的方式多种多样,比如对堆段进行攻击、对堆栈段进行攻击等。另外,缓冲区溢出攻击带来的危害严重,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
  • 缓冲区溢出攻击的一般原理是通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。攻击者主要通过在程序的地址空间里安排适当的代码,让程序跳转到入侵者安排的地址空间执行来实现攻击。
  • 上面的实验代码主要是实现了简单的缓冲区溢出攻击,跳过登录阶段的密码验证过程。要实现更深程度的攻击,可以考虑覆盖某个子函数的返回地址,将覆盖后的结果指向准备好的攻击程序的地址,比如打开一个新的shell,对系统进行其他操作。
  • 关于如何防范缓冲区溢出攻击,可以从以下几个方面来考虑:
  • 对一些关键参数的输入作严格的检查,比如像上面实验中输入password的环节,对输入的password作出严格的字符数量限制,如果超过一定范围就会终止程序。
  • 使用一些安全性良好的函数,比如strcpy,应当被更换为strncpy,同样的还有fgets、sscanf等,用它们来替换有缺陷的函数。
  • 关注操作系统自身提供的一些保护机制,比如Linux系统的bash是自带栈溢出保护机制的,也保证了栈内地址随机化,同时没有特殊设置,栈内的代码也是不可执行的。像这样的保护机制应当合理使用。
  • 对于程序中的指针也需要检查,如果程序中有函数指针一类的指针,要对附近的缓冲区做出严格保护,因为函数指针内容可能被溢出内容覆盖,从而执行攻击者希望调用的函数,实现攻击。
posted @ 2022-10-31 22:29  油菜园12号  阅读(230)  评论(0编辑  收藏  举报