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

20242417 2026-2027-1 《网络与系统攻防技术》实验一

实验报告

1.基础知识

(1)ELF可执行文件基础

Linux下ELF是可执行程序格式,程序代码段保存CPU执行的机器指令。代码段中的每一条汇编指令都对应固定的机器码(十六进制),我们可以通过十六进制编辑器直接修改机器码,静态篡改程序执行流,改变跳转逻辑。本次涉及的x86汇编指令与机器码:

NOP:空操作,机器码0x90,CPU 不做任何运算,消耗一个指令周期;

CMP:比较指令,实质做减法,只修改标志位,不保存计算结果;

JE:相等则跳转(ZF=1时跳转);

JNE:不相等则跳转(ZF=0时跳转);

JMP:无条件跳转,直接修改EIP指令指针,跳转到目标地址执行代码。

(2)缓冲区溢出(BOF)原理

缓冲区是内存中一块用来临时存放输入、读写数据的连续区域,一般在进程的栈、堆上开辟。程序接收外部输入(字符串、网络数据包)时,会把数据先放到缓冲区,再做后续处理。

当外部输入的数据长度超过缓冲区预先分配的内存空间时,多余的数据就会越界,覆盖缓冲区后面相邻的内存数据。攻击者通过构造超长输入,覆盖栈上的关键数据(函数返回地址),劫持程序执行流,让程序跳转到攻击者注入的恶意代码执行,从而拿到权限、窃取数据、破坏系统,这就是缓冲区溢出攻击。

缓冲区溢出攻击发生的直接原因是程序不判断输入长度是否超过缓冲区容量,直接拷贝数据,导致超长数据溢出缓冲区,覆盖相邻内存;根本原因在于程序和数据地位相同,均以二进制形式存放在内存中。

(3)反汇编基础

反汇编:将二进制机器码翻译为汇编指令,静态/动态分析ELF程序,定位漏洞。

静态反汇编(objdump):不运行程序,读取文件机器码

objdump -d -M intel 2417_pwn

动态反汇编(GDB):调试时查看内存指令

gdb ./2417_pwn

disass main

核心汇编指令

call:保存返回地址并跳转函数

ret:读取栈上返回地址跳转,栈溢出攻击目标

push/pop:栈压入、弹出数据

(4)防护机制

保护机制

功能简述

对栈溢出影响

Canary

栈插入随机校验值

开启→普通栈溢出直接崩溃

NX

栈内存不可执行

开启→栈无法直接执行 shellcode

PIE

程序基址随机化

开启→函数地址不固定,不能硬编码

RELRO

GOT 表保护

Full RELRO→无法 GOT 劫持

ASLR

系统级 libc / 栈地址随机

开启→libc 地址每次运行变化

2.实验内容

本次实验目标对象为Linux可执行文件pwn1。程序正常流程:main函数调用foo函数,foo函数简单回显用户输入字符串。程序内存在getShell代码片段,正常运行不会被触发。本次实验一共完成三项实践任务:

(1)手工修改可执行文件:使用十六进制编辑器修改程序机器指令,直接篡改程序跳转逻辑,改变执行流程,让程序运行getShell函数。

(2)缓冲区溢出漏洞利用:构造攻击payload字符串,覆盖栈上函数返回地址,劫持程序执行流,触发执行getShell函数,拿到shell。

(3)Shellcode注入攻击:自行编写shellcode,通过缓冲区溢出将shellcode注入栈内存,劫持EIP,让CPU执行注入的shellcode,获取交互式shell。

3.实验过程

3.1手工修改可执行文件,跳转执行getShell

(1)反汇编查看函数地址

objdump -d 2417_pwn

得到getShell函数入口地址:0x08049196 foo函数入口地址:0x080491c1

偏移 =

的32位补码:0xffffffd0 小端存储顺序:d0 ff ff ff

所以foo开头机器码替换成: E9 FB FF FF FF(我们需要修改foo函数入口,让程序进入foo后直接jmp到getShell,不再执行原有foo代码)

(2)计算相对跳转偏移(jmp相对寻址)

得到虚拟地址VMA:08049080 文件Offset:001080

根据公式:文件偏移 = 虚拟地址 − .text的VMA + .text的文件Offset

计算得出要跳转到的文件偏移地址为0x11c1

(3)在hexedit界面定位并修改foo函数开头的机器码

进入hexedit界面后,按“ctrl + G”输入0x11c1,回车,定位到foo函数的开头

将原来的5字节机器码覆盖成 E9 D0 FF FF FF

按Ctrl+X,然后输入Y确认保存修改

(4)验证修改结果

程序不再走原有正常分支,直接跳转到getShell,成功获取shell。

3.2构造payload覆盖返回地址触发getShell

注:通过任务一已经得出偏移为136字节,这里不再生成特征字符串定位溢出点

(1)获取getShell函数地址

(2)构造Payload结构

132字节A字符:填充缓冲区 + 旧EBP,占满返回地址之前的栈空间

p32():将32位地址转为小端序 4 字节,覆盖函数返回地址。

(3)执行攻击脚本,验证Shell

程序执行流被劫持,foo函数ret时跳转到getShell,getShell内部调用system("/bin/sh"),成功弹出交互式shell,终端出现$提示符。

可以执行ls、id等系统命令,攻击成功。

3.3注入并运行自定义Shellcode

(1)关闭防护

关闭ASLR地址随机,给程序开启栈可执行(关闭NX)

(2)GDB拿到栈地址

gdb ./2417_pwn

#在foo函数下断点

b

foo r

info registers esp

(3)编写exp脚本

(4)注入攻击

4.问题及解决方案

问题1:编译程序时报错

报错原因:gets()在C11标准已经被彻底移除,新版GCC头文件<stdio.h>不再提供gets函数声明

解决方案:追加参数-Wno-implicit-function-declaration,关闭了“隐式函数声明”的错误检查

问题2:任务二成功拿到shell,但是控制台打印/bin/sh: AAAAAA... not found,以为攻击失败

解决方案:该报错不是漏洞利用失败。getShell中调用system("/bin/sh"),system拉起/bin/sh程序,shell会继续读取栈上payload中A字符串,并尝试将其解析为待执行命令,输出命令未找到。shell本身已经成功启动,可以正常输入系统命令。

5.学习感悟、思考

本次缓冲区溢出实验,是我第一次动手实践二进制漏洞利用,整个过程充满试错与收获。刚开始操作时,我对反汇编命令、hexedit界面、pwntools脚本都很陌生,多次遇到程序无响应、段错误、地址无效等问题。反复排查后才明白,虚拟地址和文件偏移不能混淆,防护机制会直接影响漏洞利用的可行性。

调试任务二时,偏移计算、EXP脚本编写接连报错,我反复修改参数、在 GDB中调试观察栈的变化,才理解返回地址被覆盖的底层原理。曾经以为漏洞利用只是简单复制代码,实际操作后才懂得每一个地址、每一段输入都至关重要。

这次实验让我认识到二进制安全需要严谨细致,微小失误就会导致利用失败。同时我也意识到软件防护机制的价值,懂得缓冲区漏洞的危害。作为安全方向的初学者,我还需要继续夯实汇编与内存基础,保持耐心,在不断调试中积累经验。

posted @ 2026-10-12 00:30  neverorever  阅读(6)  评论(0)    收藏  举报