栈溢出(stack overflow)
一些术语/概念
- 类型安全(Type safety)
- In computer science, type safety and type soundness are the extent to which a programming language discourages or prevents type errors.
————From wikipidiea
- In computer science, type safety and type soundness are the extent to which a programming language discourages or prevents type errors.
前置知识:x86架构下函数调用中的栈变化
-
初始:
- 栈示意图:
-
压入参数(arg1, arg2)
- 指令:
1 2push arg2 push arg1 ;注意,逆序压入参数 - 栈示意图:
- 指令:
-
调用函数
- 指令
1 2 3 4call fuc ; 等价于 push eip+5 ;5为call指令的长度 jmp fuc - 栈示意图
- 指令
-
函数序言(Prologue)
- 指令:
1 2 3push ebp ;保存调用者的EBP mov ebp, esp ;设置当前函数的EBP sub esp, 8 ;为局部变量分配空间(示例中分配8字节)- 栈示意图
-
访问数据
- 指令:
1 2 3mov eax, [ebp+8] ;访问arg1 mov ebx, [ebp+12] ;访问arg2 mov ecx, [ebp-4] ;访问val1- 栈示意图
-
函数尾声
- 指令
1 2 3mov esp, ebp ;释放局部变量 pop ebp ;恢复调用者ebp ret ;返回到调用者- 栈示意图(Epilogue)
-
调用者清理参数
- 指令
1sub esp, 8- 栈示意图:
栈溢出
缓冲区溢出
-
当数据写入到分配给特定数据结构的内存边界范围之外时,就会发生缓冲区溢出
-
当缓冲区边界被忽略和未检查时会发生
- 示例:
1 2 3 4 5 6 7 8#include<stdio.h> int main() { char a[5]; gets(a); puts(a); printf("%c", a[5]); }直接编译
1gcc buffer_overflow1.c可以看到如下warning
1 2 3 4 5 6 7buffer_overflow1.c: In function ‘main’: buffer_overflow1.c:6:5: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration] 6 | gets(a); | ^~~~ | fgets /usr/bin/ld: /tmp/ccA63mQo.o: in function `main': buffer_overflow1.c:(.text+0x28): warning: the `gets' function is dangerous and should not be used.这是因为gets(puts)是一个不安全的函数,缺少缓冲区边界检查,即没有对读入字符个数的检查和限制。 现在编译后的可执行文件
1./a.out输入6个字母abcdef,可以看到如下输出
1 2 3 4abcdef abcdef *** stack smashing detected ***: terminated Aborted (core dumped)可以看到程序被终止,并且给出了*** stack smashing detected ***: terminated,这是由堆栈保护机制(Stack Smashing Protection, SSP)发出的警告信息。当编译器开启了 SSP 选项(例如 GCC 的 -fstack-protector 或 -fstack-protector-all)时,它会在函数栈帧中插入一个被称为“canary”(金丝雀)的随机值。如果缓冲区溢出发生,覆盖了返回地址,那么这个 canary 值也会被改变。在函数返回之前,程序会检查这个 canary 值是否被篡改。如果被篡改,就意味着发生了缓冲区溢出,程序会立即终止执行,并打印这个警告信息。 现在在关闭相关保护机制的情况下编译
1gcc -fno-stack-protector -no-pie buffer_overflow1.c其中的编译选项含义如下:
- -fno-stack-protector: 禁用堆栈保护(stack canary)。这会阻止编译器在缓冲区溢出发生时自动终止程序。
- -no-pie: 禁用位置独立可执行文件(Position Independent Executable),使得程序加载到固定的内存地址。这与另一种针对栈溢出的防御地址空间布局随机化(ASLR)有关。
运行后得到如下输出
1 2 3abcdef abcdef f可以看到输入的字符成功覆盖了字符串的后一个字节。即可以利用栈溢出篡改内存中的字节,这是栈溢出攻击的基本原理。
利用栈溢出的攻击方式————拿shell
即通过栈溢出注入shellcode来获取目标程序的shell,得到shell后就可以劫持数据流