20171121王朋伟《逆向及Bof基础实践》实验报告

一、实验名称

  逆向及Bof基础实践

二、实验目的

  1.掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码

  2.掌握反汇编与十六进制编程器

  3.能正确修改机器指令改变程序执行流程

  4.能正确构造payload进行bof攻击

三、实验内容

       本次实验的对象是一个名为pwn1的linux可执行文件。

  该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。该程序同时包含另一个代码片段getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。

  本次实验的三个实践内容如下:

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

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

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

  这几种思路,基本代表现实情况中的攻击目标:

  (1)运行原本不可访问的代码片段

  (2)强行修改程序执行流

  (3)注入运行任意代码。

四、基础知识与实验准备

       1.部分汇编指令极其对应的机器码

       (1)NOP :空操作,机器码0x90

  (2)JNE :条件转移指令,不相等则跳转,机器码0x75

  (3)JE  :条件转移指令,相等则跳转,机器码0x74

  (4)JMP :无条件跳转

        段内直接短转Jmp short,机器码0xeb;

        段内直接近转移Jmp near,机器码0xe9;

        段内间接转移Jmp word,机器码0xff;

        段间直接(远)转移Jmp far,机器码0xea

  (5)CMP :比较指令,相当于减法,可以改变标志位,不保存结果

  2.安装所需的软件包

    sudo apt-get update

    sudo apt-get install execstack  //安装execstack

    sudo apt-get install gdb       //安装gdb

       3.Linux下有两种基本构造攻击buf的方法:

    (1)retaddr+nop+shellcode

    (2)nop+shellcode+retaddr

    多选用第一种方法(RNS)。

五、实验步骤

       5.1直接修改程序机器指令,改变程序执行流程

       (1)将pwn1复制到虚拟机的/home/wpw20171121目录下,并备份一个pwn2。

 

       (2)使用objdump -d pwn1 | more对pwn1反汇编,其中“-d”为disassemble反汇编之意,“|”为管道服务,意为把pwn1反汇编的结果作为more的输入。

 

       (3)输入“/getShell”,直接找到getShel函数、foo函数和main函数的反汇编结果。

 

  当程序正常执行到“call 8048491 <foo>”时,意为这条指令将调用foo函数,而foo函数的地址在上图我们可以看到时8048491,与指令相符。对应的机器指令中的e8即跳转之意,我们要做的就是把这条指令的跳转地址从8048491的foo函数改为804847d的getShell函数,从而实现通过直接修改程序机器指令,改变程序执行流程的目的。此时EIP寄存器中的值应该是下条指令的地址,即80484ba,当执行call指令的时候,CPU会执行“EIP+call后面的地址(此处为d7ffffff)”所在位置的指令(其中d7ffffff是补码),所以要想执行getShell函数,只要call X满足“EIP+X=804847d”即可。用计算器计算得到X为c3ffffff。

 

       (4)使用vi文本编辑器,vi 打开文件后就直接进入一般模式,在一般模式中可以进行删除、复制、粘贴等动作,但是却无法编辑文件内容,按“r”会进入编辑模式,可以修改文件内容,编辑模式下按Esc键可退回一般模式。刚开始打开pwn1会显示乱码,输入“:%!xxd” 将显示模式切换为16进制模式。输入“/d7”找到要修改的地方,确认位置正确后,按“r”将d7改为c3,输入“:%!xxd -r” 将显示转换回原格式,最后输入“:wq”存盘退出。

 

 

 

       (5)再次反汇编,观察到call指令调用了getShell函数。

 

 

       5.2通过构造输入参数,造成BOF攻击,改变程序执行流

  (1)输入“cp pwn2 pwn1”恢复原pwn1文件

       (2)对pwn1反汇编,按程序正常执行顺序,main函数会调用foo函数,foo函数会简单回显任何用户输入的字符串,因为foo函数调用了gets函数,不会检查用户输入字符串的长度,所以有缓冲区溢出攻击漏洞,我们可以利用输入的字符串,覆盖foo函数的返回地址(即main栈帧中的EIP),使程序执行getShell函数。观察到foo函数反汇编的结果,由“lea -0x1c(%ebp),%eax”发现foo函数的缓冲区长度是0x1c(即十进制28个字节),加上堆栈中存放main函数的ebp地址所占的4个字节,共32个字节。

 

 

 

  使用gdb工具调试pwn1,根据上图中eip寄存器的值0x34333231确定输入字符串的第33、34、35、36个字符会覆盖到堆栈上的返回地址。

       (3)由以上的分析,只要我们在输入字符串的第33-36字节设为getShell函数的地址0x0804847d,即可在程序返回时跳转到getShell函数。对比上一次输入1234得到的返回地址为0x34333231,所以我们0x0804847d的输入顺序应为\x7d\x84\x04\x08。

  构造的字符串为11111111222222223333333344444444\x7d\x84\x04\x08。由于键盘无法输入16进制值,所以我们先用perl指令生成包括这样字符串的一个文件。输入“perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input”,使用输出重定向“>”将perl生成的字符串存储到文件input中,并使用16进制查看指令xxd查看input文件的内容。

       (4)输入“(cat input; cat) | ./pwn1”,即通过管道服务,读出文件input的内容并将其作为pwn1的输入,结果如下图所示,程序执行到getShell函数,得到一个可用Shell。

 

 

  5.3注入Shellcode并执行

  (1)做好准备工作:设置堆栈可执行,关闭地址随机化,使攻击环境比较理想。

 

       (2)Linux下有两种基本构造攻击buf的方法:

    ·retaddr+nop+shellcode

    ·nop+shellcode+retaddr。

              这两种方法我都进行了尝试,但是通过尝试,发现nop+shellcode+retaddr的构造无法成功实现执行shellcode,我觉得可能是因为执行完汇编语言的leave指令(leave为mov %ebp, %esp + pop %ebp)后,esp寄存器的值为ebp寄存器的值,即系统收回该ebp和原esp之间的地址空间,而nop+shellcode+retaddr的构造就是将shellcode的代码部分放在被回收的那段地址空间中,导致无法执行shellcode。但是我在单步调试的过程中却发现,即使执行完leave指令后,shellcode的代码也暂时不会消失,直到运行至0xffffd352时才会出错,这也是我不太理解的地方。

 

·此处结构为nop+shellcode+retaddr

 

   输入“(cat input_shellcode;cat) | ./pwn1”后在另一个终端打开调试界面,找到foo栈帧中的返回地址。输入“ps -ef | grep pwn1”找到pwn1的进程号为1199,再使用gdb工具调试pwn1,输入“disassemble foo”对foo函数反汇编,将断点放在ret(即pop %eip)之前。回原终端按下回车(即字符串输入结束),再回来输入c继续执行。使用“info r esp”查看esp寄存器的地址为0xffffd35c,再往前翻一翻,找到若干0x90(nop)即可。确定空操作的地址后,将地址换至input_shellcode文件中。

 

  调用出错后重复上述步骤进行单步调试,尝试找出错误的原因(已在上文中提及)。

 

  一开始shellcode的代码段是完整的,但执行到0xffffd352后,shellcode的代码就丢失了一部分,随后程序出错结束。

 

 

·此处结构为retaddr+nop+shellcode

       坑跳完之后,选用retaddr+nop+shellcode构造重新开始。因为设置了关闭地址随机化,所以返回地址没有改变,仍为0xffffd35c,所以它的下一个地址(0xffffd360)会存放nop指令。输入“perl -e 'print "A" x 32;print "\x60\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\x90\x00\xd3\xff\xff\x00"' > input_shellcode”,设计好字符串并将其放入input_shellcode文件中。使用xxd查看input_shellcode文件中的内容是否如预期所示。

       (3)最后输入“(cat input_shellcode;cat) | ./pwn1”,将input_shellcode文件的内容作为pwn1的输入,成功实现了调用注入的shellcode。

 

六、问答题

       什么是漏洞?漏洞有什么危害?

       答:漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。

  漏洞的存在,很容易导致黑客的侵入及病毒的驻留,会导致数据丢失和篡改、隐私泄露乃至金钱上的损失,如:网站因漏洞被入侵,网站用户数据将会泄露或被篡改、网站功能可能遭到破坏而中止乃至服务器本身被入侵者控制。

七、实验收获与感想

       通过本次网络对抗技术实验,我实现了手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数,利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数以及注入一个自己制作的shellcode并运行这段shellcode。因为是首次做网络对抗实验,所以本次实验我主要是观看老师的教学视频,跟着老师的步骤,一步一步完成的,通过理解老师所讲的汇编语言、机器指令和栈帧的相关知识,明白缓冲区溢出攻击的原理,最后尝试抛开指导,用所学到的知识独立完成实验。

  本次实验让我受益匪浅,期待下次实验能让我对这门课程有更深的理解和感悟。

posted on 2020-03-11 22:19  20171121王朋伟  阅读(134)  评论(0编辑  收藏