上一篇文章简单的介绍了栈溢出攻击的一种方式 –hijack GOT,即覆写GOT表而间接控制程序,这篇文章会介绍另一种攻击方式 –ROP,即返回导向编程。
ROP简介
返回导向编程这个名字听起来可能有些奇怪,但是仔细思考一下就会发现,它的含义已经完全在字面上显示出来了。
那就是通过编程的方式,劫持程序的返回地址,将其重新导向至目标位置(shellcode或其他代码)。
1 | 返回导向编程(英语:Return-Oriented Programming, ROP)是计算机安全漏洞利用技术,该技术允许攻击者在安全防御的情况下执行代码,如不可执行的内存和代码签名。攻击者控制堆栈调用以劫持程序控制流并执行针对性的机器语言指令序列(称为Gadgets)。 每一段 gadget 通常结束于 return 指令,并位于共享库代码中的子程序。系列调用这些代码,攻击者可以在拥有更简单攻击防范的程序内执行任意操作。 ---互动百科 |
我们可以看到,ROP的核心也是操作返回地址,从而引导程序执行本不应执行到的语句。
ROP的几个术语和概念
- gadget
字面意思为“小工具”,如果把某个程序比喻成汽车,那么gadget就相当于拆车的工具,依靠它,我们就可以“突破”程序的限制,执行我们希望执行的代码。 - 函数的调用–参数的传递
大多数的程序都是由一个一个函数组成的,当函数发生调用的时候,通常需要传递参数,在x86框架下,函数的参数全部是由栈来传递的,而在x64框架下,函数的前6个参数依次通过 rdi,rsi,rdx,rcx,r8 和 r9这六个寄存器来传递,剩下的会从栈中传递。 - libc
即linux下的函数库。1
libc是Linux下的ANSI C的函数库。ANSI C是基本的C语言函数库,包含了C语言最基本的库函数。这个库可以根据 头文件划分为 15 个部分,其中包括:字符类型 ()、错误码 ()、 浮点常数 ()、数学常数 ()、标准定义 ()、 标准 I/O ()、工具函数 ()、字符串操作 ()、 时间和日期 ()、可变参数表 ()、信号 ()、 非局部跳转 ()、本地信息 ()、程序断言 () 等等。这在其他的C语言的IDE中都是有的。
正片开始
在前置背景知识有了大概的了解之后,我们以实验吧的 ropbaby 为例来实践一下ROP。
首先打开题目页面:
nc,即netcat,是一种网络连接工具,我们暂且把它视为类似于ssh的命令。
题目还给出了一个链接,通过链接可以下载得到两个文件:
使用file命令来查看一下这两个文件的属性:
其中一个是编译出的程序, .so文件是unix的动态链接文件(即所谓的函数库)。
先使用Ida打开ropbaby这个文件:
分析程序流程可以发现
选择1 程序给出libc的基地址
选择2 可以获取各个函数的地址
选择3 可以输入最大长度为1024的字符串
选择4 会退出程序
很明显 可以溢出的位置就在要求我们输入字符串的地方了:
分析此处代码 可以发现 无论输入的字符串有多长 都会被拷贝到一个 int64 型的变量中,那么 一个int64型变量有多大呢?–8个字节
于是 如果输入大于8个字节,就会造成溢出。
既然确定了溢出点 下面来看一下程序的保护方式:
开启了 NX 写SHELLCODE的方法就不行了 但可以使用ROP进行攻击。
首先 程序是64位的 函数调用时 前六个参数是通过rdi,rsi,rdx,rcx,r8 和 r9进行传递的 而我们的目的是构造payload 使程序执行 system(‘/bin/sh’) 这个命令
溢出的思路就是:
- 构造一个gadget 包含 pop rdi | retn :将’/bin/sh’ 送入rdi寄存器
- 找到system函数地址 使程序跳转执行system函数
思路很简单,下面就开始动手操作:
首先需要在函数库中搜索ROP链(因为程序直接给出了libc的基地址和libc文件 所以不需要去主程序中寻找了)找到可用的gadget 可以使用ROPgadget这个开源工具完成,在linux终端输入一下命令:
ROPgadget –binary libc-2.23.so –only “pop|ret”
找到了gadget:
记录这个地址 一会有用
在libc中找到 ‘/bin/sh’ 字符串的地址
在终端中输入命令
strings -tx libc-2.23.so | grep “/bin/sh”
也记下这个地址
在libc中找到system函数的地址
在终端输入命令
objdump -T libc-2.23.so | grep “system”
同样的 记下这个地址
现在 我们需要的所有元素都已经具备了 可以开始构造payload
那么 payload = ‘a’ * 8 + gadget_address + bin_address + system_address
下面是payload
1 | from pwn import * |
即可得到flag。
稍微总结一下
1 | 通过实践可以看到 我们依然是通过控制程序的返回地址来操作程序的,其中的核心就是能够找到那么一个点,使操作返回地址成为可能。 |
- 本文作者: CataLpa
- 本文链接: https://wzt.ac.cn/2018/04/02/ROP/
-
版权声明:
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。