20232305 2025-2026-1 《网络与系统攻防技术》实验一实验报告
一、实验内容
- 实践目标
本次实践的对象是一个名为pwn1的linux可执行文件。该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们的第一个实践目标就是想办法运行这个代码片段。
在本次实验中,我们使用两种方法运行这个代码片段,一个是手动修改可执行文件,通过反编译等手段,改变程序的执行流程,使其调用的函数从foo变为getshell;另一个是利用foo函数中的BOF漏洞,通过输入特定的字符串来覆盖正常的函数返回地址,从而使其能够运行getshell函数
本次实验的第二个实践目标是注入自己的shellcode并运行这段shellcode。 - 学习内容
(1)掌握反汇编与十六进制编辑器,初步了解ELF文件格式和一些简单的汇编语言和机器语言的对应关系;
(2)学习函数调用和程序运行过程中栈结构变化以及对应的地址存放的信息。 - 基础知识
(一)NOP,JNE,JE,JMP,CMP汇编指令的机器码
(1)NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
(2)JNE:条件转移指令,如果不相等则跳转。(机器码:75)
(3)JE:条件转移指令,如果相等则跳转。(机器码:74)
(4)JMP:无条件转移指令。段内直接短转Jmp short(机器码:EB)段内直接近转移Jmp near(机器码:E9)段内间接转移Jmp word(机器码:FF)段间直接(远)转移Jmp far(机器码:EA)
(5)CMP:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
(二)反汇编
(1)由已生成的机器语言(二进制语言)转化为汇编语言的过程,也可以说是汇编的逆向过程
(2)在本次实验中,我们在Linux环境下使用objdump反汇编工具对pwn1文件进行反汇编
(3)反汇编指令objdump -d <文件名>
(三)十六进制编辑器
(1)十六进制编辑器是用于编辑单个字节数据的软件应用程序,主要由程序员或系统管理员使用。Linux系统中可以使用多种十六进制编辑器,在本次实验中我主要使用xxd,xxd 是一个命令行十六进制编辑器,可以创建二进制文件的十六进制转储。
(2)%!xxd 进入十六进制编辑模式
(3)%!xxd -r 切换回原模式
二、实验过程
(一)直接修改程序执行流程,使代码能够执行getshell
首先配置环境,并修改终端名字和实验要用到的文件名字,为了避免实验出错后重复从本地传pwn1,所以我复制了一份并将文件名改为pwn20232305作为实验用的文件。
然后通过objdump -d pwn20232305 | more指令反汇编pwn20232305,找到main,foo和getshell函数
可见,getshell的地址是80487d,foo函数的地址是8048391,main函数中调用foo函数的指令为e8 d7 ff ff ff。其中,e8是call指令,d7 ff ff ff是偏移量(补码形式),根据 call 指令的执行逻辑,CPU 会将当前指令下一条地址(0x080484ba)与该偏移量相加,得到目标函数地址(0x080484ba - 0x29 = 0x08048491),即 foo 函数地址。
因此,我们需要根据getshell函数的地址来重新计算偏移量,使其能够调用getshell。通过计算,0x0804847d - 0x080484ba = -0x3d,其补码为 “c3 ff ff ff”。即偏移量需要修改为c3 ff ff ff。
然后就要在文件中修改偏移量,通过vi打开文件,再输入:%!xxd切换为十六进制视图。在文件中找到e8 d7部分并将d7修改为c3。下面是修改后的图片
再输入:%!xxd -r退出十六进制视图,输入:wq保存并退出。
通过反汇编再验证一下是否修改成功。
最后给予文件可运行权限,并运行文件。
可以进入shell,说明修改程序执行流程成功。
(二)通过输入特定的字符串,利用get函数无边界检查的缺陷,覆盖返回地址,运行getshell
首先反汇编了解程序的功能和漏洞。通过反汇编可以看到对应指令0804849d: call 08048330 gets@plt,而gets函数最大的缺陷就是没有输入长度检查,只要有输入就会接收,完全不判断输入是否超过目标缓冲区的容量,这就为后续的缓冲区溢出(BOF)漏洞埋下了隐患。结合反汇编结果,能更清晰地理解foo函数的栈空间分配与缓冲区布局:foo函数执行时,会通过sub $0x38, %esp指令在栈上分配一块 56 字节(即十六进制0x38)的内存空间,用于存储缓冲区、函数调用的临时数据以及关键寄存器值;随后通过lea -0x1c(%ebp), %eax指令,将缓冲区的起始地址传递给gets函数,这里的0x1c转换为十进制是 28,意味着缓冲区的实际容量只有 28 字节,也就是说gets函数最多只能安全接收 28 字节的输入,一旦超过这个长度,多余的输入数据就会引发溢出。
栈空间的存储有固定顺序,从缓冲区开始,后续依次存放着ebp寄存器值(函数调用时的栈基址,占 4 字节)和函数返回地址(占 4 字节),所以当输入字符串长度超过 28 字节时,第 1 到 28 字节会正常填充缓冲区,不会出现异常;第 29 到 32 字节的多余数据会覆盖栈中的ebp寄存器值(原本用于函数执行完毕后恢复栈结构);而第 33 到 36 字节的溢出数据,则会直接覆盖函数返回地址,这也是我们利用漏洞的核心突破口。
另外,在main函数调用foo函数的过程中,会先将一个 “返回地址” 压入栈中,通过反汇编确认这个地址是0x080484ba,它的作用是:当foo函数正常执行结束后,程序会根据这个地址跳回main函数,继续执行后续流程。我们的攻击逻辑就是利用前面提到的缓冲区溢出漏洞,构造一段超长输入,让输入中第 33 到 36 字节的数据恰好覆盖掉这个默认的返回地址0x080484ba,并将其替换成我们指定的地址 —— 比如getShell函数的起始地址,或者后续注入的 Shellcode 地址。这样一来,当foo函数执行完准备 “返回” 时,就会跳转到我们指定的地址,进而执行getShell代码或自定义的 Shellcode,最终达成实验目标。
所以我通过gdb调试程序,输入测试字符串 1111111122222222333333334444444412345678,然后执行 info r查看寄存器状态,发现 EIP(存储下一条指令地址的寄存器)被覆盖为 0x34333231(即字符串 "1234" 的 ASCII 码)
通过反汇编已知 getShell 函数的入口地址为 0x0804847d。因此,只需将字符串末尾的 "1234" 替换为该地址的字节表示,即可让程序在返回时跳转至 getShell。为了利用缓冲区溢出漏洞,我们需要将返回地址覆盖为getShell函数的地址0x0804847d。
也就是11111111222222223333333344444444\x7d\x84\x04\x08![53d2a96b8b08be67b80987dbea0b7e6d]
输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
成功。
(三)注入shellcode并执行
首先需要做一些准备工作:
设置堆栈可执行:
关闭地址随机化:
以nops+shellcode+retaddr为结构构造shellcode,并用perl生成基础Payload,保存到input_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
打开一个终端,注入这个buf,并另开一个终端,找到这个进程。
然后在另开的终端中用gdb调试,通过设置断点,查找buf的内存地址
找到01020304,shellcode就挨着,因此地址是ffffd310。修改shellcode并注入。
成功。
三、问题及解决方案
- 问题1:在设置堆栈可执行时,execstack命令总是使用不了
- 问题1解决方案:首先我想要下载execstack命令的安装包,但是显示apt-get install execstack无法定位软件包,之后询问ai,给出的解决方案是安装prelink安装包,但是显示无法定位prelink安装包。再次询问ai后给出使用kali自带的工具patchelf,并使用patchelf --set-execstack yes ./pwn1命令,并使用patchelf --print-execstack ./your_binary验证,验证成功解决该问题。
4.学习感悟、思考等
在做本次实验时,首先感受到的就是这个实验的前置知识非常多,首先要有一定的命令行,gdb调试的基础,还需要理解程序执行时堆栈存放地址的变化,否则就变成了简单的根据实验指导而简单的复现。同时也让我感受到网络攻防这门注重实践的课程的乐趣,这不像计网那种偏向理论的课那样枯燥,给我做中学的感受。同时在成功实现缓冲区溢出攻击后更让我感受到这门课的乐趣,感觉自己已经成为了一入门“黑客”,虽然这是建立在很多前置条件下的攻击。不过还是让我对这门课有了很大的兴趣。
参考资料
- https://blog.csdn.net/m0_74030222/article/details/143866270?ops_request_misc=elastic_search_misc&request_id=481d3fc5e3f829edbb99248803e4de21&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-143866270-null-null.142v102pc_search_result_base3&utm_term=kali%20linux%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B&spm=1018.2226.3001.4187