网络安全复习|第七章 缓冲区溢出攻击与防御

必考,大题,考核方式:代码,c++,为什么溢出,溢出的后果,溢出堆栈长什么样,overflow1,overflow2看懂复现,掌握到代码级别,格式化,串溢出、整数溢出,了解即可,堆溢出不涉及,看看缓冲区溢出如何防御,综合题,怎么攻击?输入什么?如何防御缓冲区溢出?有哪几种防御措施?

基础知识

进程的虚拟内存空间

栈帧空间

❤每个函数独自占用自己的栈帧空间,当前运行的函数的栈帧总在栈顶。

❤寄存器——标识当时运行函数的栈帧 

                                                               ESP(栈指针寄存器):系统中正在运行函数的栈顶

                                                               EBP(基指针寄存器):系统中正在运行函数的栈底

❤当栈帧1对应的函数返回1时,栈帧1的空间被收回。

❤栈帧2变为当前运行函数的栈帧。

❤函数栈中,包含函数运行和函数返回的相关数据

                                                                                ·局部变量。

                                                                                ·栈帧状态信息:保存前一段函数栈帧的栈信                                                                             息。

                                                                                ·函数返回地址:函数返回主程序继续执行的指                                                                       令地址。

缓冲区溢出原理

通过往程序的缓冲区,写入超出其长度的内容,造成缓冲区的溢出,从而破坏程序的“堆栈”,使程序转而执行其他指令,以达到攻击的目的。

栈溢出

  • 栈帧信息由EBP和ESP指针控制,但系统只有一组EBP和ESP指针,只能由正在执行函数的栈帧使用。
  • 当被调函数执行完毕返回时,主调函数的ESP自然显露出来,因此不需要保存。
  • 所以,编译器将主调函数的EBP存放在栈上。

c语言比较规则

C语言中字符串的比较通常使用 strcmp 函数,其比较规则基于字符串中各个字符的ASCII值。下面是 strcmp 函数的基本工作原理:

  1. 逐字符比较: strcmp 会逐个字符地比较两个字符串,从每个字符串的第一个字符开始。

  2. ASCII值比较: 比较是基于字符的ASCII值。在ASCII编码表中,每个字符都有一个对应的数值。

  3. 返回值:

    • 如果第一个不匹配的字符在第一个字符串中的ASCII值小于第二个字符串中对应位置的字符的ASCII值,strcmp 返回负值。
    • 如果两个字符串的所有字符都匹配(直到字符串结束符 '\0'),则返回0,表示字符串相等。
    • 如果第一个不匹配的字符在第一个字符串中的ASCII值大于第二个字符串中对应位置的字符的ASCII值,strcmp 返回正值。
  4. 字符串结束符 '\0' 的作用: 字符串在C语言中以 '\0' 结束。如果一个字符串先达到 '\0',而另一个字符串在该位置的字符不是 '\0',则到达 '\0' 的字符串被认为是较小的。

overflow 

#include <stdio.h>
#define PASSWORD "1234567"

int verify_password(char *password)//验证密码函数
{
	int authenticated;//定义认证变量
    char buffer[8];//定义一个8字节缓冲区
	authenticated=strcmp(password,PASSWORD);//比较输入密码与设定密码	
	strcpy(buffer,password);//将输入密码复制到缓冲区,可能导致溢出
	return authenticated;//返回认证结果	
}
main(){
	int valid_flag=0;//定义验证标志 	
	char password[1024];//定义一个1024字节的密码输入缓冲区
	while(1)
	{	printf("please input password:      ");//提示输入密码
		scanf("%s",password);//读取输入密码
		valid_flag=verify_password(password);//验证密码
		if(valid_flag)//如果验证失败
		 {    printf("incorrect password!\n\n");//打印密码错误信息
		 } else//如果验证成功
	{printf("Congratulation! You have passed the verification!\n");//打印验证通过信息
			break;//跳出循环
		}
    }
}

为什么会溢出: 

  • 'verify_password'中的'buffer'为8字节,'strcpy'在复制的时候没有检查passw的长度,如果超过7字节(加上空字符)会导致buffer溢出到authenticated的位置

输入什么会导致溢出 :

输入大于“1234567”的数会导致溢出,验证通过
输入小于“1234567”的数会导致栈溢出,验证不通过

溢出栈图: 

详细解释

在下面这段代码中,password的长度是8,字符串会以“\0”结尾。

  1. 输入“1234567”时:

    • 输入的字符串正好填满缓冲区(7个字符加上字符串结束符 '\0')。
    • 因为输入与预设密码匹配,strcmp 返回 0,表示认证通过。
  2. 输入“12345678”时:

    • 这个字符串长度超出了缓冲区的大小(8个字符加上 '\0' 结尾)。
    • 根据 C 语言的字符串比较规则,"12345678" 确实大于 "1234567",因此 strcmp 返回一个正数(1)。
    • 但由于缓冲区溢出,字符串结束符 '\0' 被写入到了 authenticated 变量的内存空间,将它的最后一个字节修改为 0。这导致 authenticated 的值变成了 0,错误地表示认证通过。
  3. 输入“11111111”时:

    • 同样超出缓冲区大小,但由于 "11111111" 小于 "1234567",strcmp 返回一个负数。
    • 在 C 语言中,负数以补码形式表示,因此 authenticated 的值变为 0xFFFFFFFF。
    • 当缓冲区溢出发生,authenticated 的最后一个字节变为 0,但它的值变为 0xFFFFFF00。由于这个值不为 0,表示认证失败。

字符串 "1234567" 和 "11111111" 的比较过程

  1. ASCII值对比:

    • 字符串 "1234567" 中的字符依次为 '1', '2', '3', '4', '5', '6', '7',它们对应的 ASCII 值分别是 49, 50, 51, 52, 53, 54, 55。
    • 字符串 "11111111" 中的字符全都是 '1',对应的 ASCII 值是 49。
  2. 逐字符比较:

    • 首先比较两个字符串的第一个字符。在这个例子中,两个字符串的第一个字符都是 '1'(ASCII值49),所以它们是相等的,比较继续。
    • 接着比较第二个字符。"1234567" 的第二个字符是 '2'(ASCII值50),而 "11111111" 的第二个字符仍然是 '1'(ASCII值49)。因为 49 小于 50,所以 strcmp 会返回一个负值。

结果解释

由于 "11111111" 中的第二个字符 '1' 的 ASCII 值小于 "1234567" 中的第二个字符 '2' 的 ASCII 值,strcmp("1234567", "11111111") 将返回一个负值。这个负值表明第一个字符串在字典顺序上是大于第二个字符串的。

在缓冲区溢出的上下文中,这个负值(通常是 -1,但具体值取决于 strcmp 的实现)会以补码形式存储在整数变量中。对于一个32位整数,-1的补码是 0xFFFFFFFF。如果后续发生缓冲区溢出,这个值可能会被部分覆盖,但只要最高有效位保持为1(即值为负),验证就会失败,因为 C 语言中任何非零值都被视为真(true)。

overflow2

#include <stdio.h>
#define PASSWORD "1234567"
int verify_password(char *password)
{
    char authenticated;
    char buffer[8];
    authenticated = strcmp(password,PASSWORD);
    strcpy(buffer,password);//会导致缓冲区溢出,没有检查password的长度
    return authenticated;
}
main(){
    int valid_flag=0;
    char password[1024];
    FILE *fp;//文件指针
    if(!fp=fopen("password.txt","rw+")){
        exit(0);
    }//以读写模式打开一个名为"password.txt"的文件。如果文件不存在或无法打开,程序会调用'exit'函数立即退出。
    fscanf(fp,"%s",password);//从文件中读取一个字符串到"password"缓冲区中,会导致溢出,没有检查文件中字符串的长度。
    valid_flag = verify_password(password);
    if(valid_flag){
        printf("incorrect password!\n\n");
    }else{
        printf("Congratulation!You have passed the verfication!\n")
    fclose(fp);//关闭文件夹
}
    

 要攻击这段代码,攻击者可以在password.txt中输入一个超过buffer数组长度的字符串。这个字符串需要精心构造,以便在内存中覆盖authenticated变量的存储空间,造成认证绕过。

overflow3

#include <stdio.h>
#include <windows.h>
#define PASSWORD "1234567"
int verify_password(char *password)
{
    int authenticated;
    char buffer[44];
    authenticated = strcmp(password,PASSWORD);
    strcpy(buffer,password);//缓冲区溢出
    return authenticated;
}
main(){
    int valid_flag=0;
    char password[1024];
    FILE *fp;//文件指针;
    LoadLibrary("user32.dll")//调用'LoadLibrary'函数加载Windows系统库'user.dll'这常用于动态链接库的加载,但在上下文没有明显实验目的。
    if(!(fp=fopen("password.txt","rw+"))){
        exit(0);
    }//尝试以读写模式打开名为"password.txt"的文件。如果文件打开失败,程序将退出。
    fscanf(fp,"%s",password);//从文件中读取一个字符串到 password 缓冲区。这里存在潜在的安全风险,因为 fscanf 使用 %s 格式说明符时不会检查目的缓冲区大小,可能导致缓冲区溢出。
    valid_flag = verify_password(password);
    if(valid_flag){
        printf("incorrect password!\n\n");
    }else{
        printf("Congratulation!You have passed the verification!\n");
    flose(fp);
}


攻击这段代码的方法是在password.txt中放入超过buffer的大小(44字节)的字符串。这个字符串必须足够长,以覆盖authenticated变量,并可能还要覆盖返回地址或其他重要的栈结构。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值