基于64位ubuntu 16.04操作系统版本以及gcc,gdb版本如下:
Linux ubuntu 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
###准备工作
为了更简单的方式实现栈溢出,需要关闭一些保护措施。
- 关闭
ASLR
(地址空间布局随机化):
echo 0 |sudo tee /proc/sys/kernel/randomize_va_space
Cannary
:开启Cannary
之后,函数开始时在rbp和临时变量之间插入一个随机值,函数结束时验证这个值。如果不相等(也就是这个值被其他值覆盖了),就会调用__stackchk_fail
函数,终止进程。对应GCC编译选项-fno-stack-protector
解除该保护。NX
:开启NX保护后,程序的堆栈将会不可执行,对应GCC编译选项-z execstack
解除该保护。
C语言栈帧结构如下:
漏洞程序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
FILE* g_fp;
void read_file(){
char buf[100];
int v,length=0;
g_fp=fopen("../input/buffer_overflow_code_injection_write_file.txt","r");
if(g_fp==NULL)
{
printf("open file failed!\n");
exit(1);
}
while (fscanf(g_fp, "\\x%02x", &v) == 1)
{
buf[length++] = v;
}
fclose(g_fp);
}
int main(int argc,char *argv[]){
read_file();
return 0;
}
分析
- 分析漏洞代码, while循环出现栈溢出,读入文件中覆盖了rbp与返回地址,且修改其指向shellcode起始指令;
- 分析读入文件,即将返回地址处修改为shellcode首地址
- 编译:
gcc -g -z execstack -fno-stack-protector buffer_overflow_code_injection_write_file.c -o buffer_overflow
运行
- 使用gdb调试运行:
gdb buffer_overflow
由调试可知栈帧结构如图所示,因此栈溢出时会覆盖length
值,即100后有覆盖为97,最终读入0x6d即109,刚好从rbp指向地址开始填充之后的8个0x61,之后填充返回地址,由图中分析可知该返回地址应改为0x00007fffffffdab0
,小端模式,读入文件中\x20\x18改为\xb0\xda。重新调试:
成功跳入shellcode,继续运行:
正常退出,此时output文件夹已生成新文件:
在gdb调试环境下运行成功,但实际运行却出现段错误:
改进
经搜索查阅资料得知是gdb有自己的变量环境,变量的存放地址和程序实际运行会不一致,因此只需要把返回地址改为shellcode实际存放的地址即可,填充长度无须改变,因为相对偏移不变。
要获取shellcode首条指令的地址,可以在程序中打印出length的地址加上4(length)+8(rbp)+8(返回地址)=20,也可以利用内核转储获取真实地址。
首先启用内核转储:ulimit -c unlimited,缺省情况下,内核在coredump时所产生的core文件放在与该程序相同的目录中,并且文件名固定为core。
由图中可知应把读入文件返回地址处改为:\x40\xdb\xff\xff\xff\x7f\x00\x00
,再次运行:
正常环境下运行成功。
总结
复现关键点:
- 关闭保护措施
- 找准rbp后保存返回地址的8个字节
- 确定shellcode的入口地址