CTF | PWN 一些基础 ROP


本篇为个人对 CTFWiki 的学习笔记。

0x00 What’s ROP

随着 NX (Non-eXecutable) 保护的开启,传统的直接向栈或者堆上直接注入代码的方式难以继续发挥效果,由此攻击者们也提出来相应的方法来绕过保护。

目前被广泛使用的攻击手法是 返回导向编程 (Return Oriented Programming),其主要思想是在 栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

gadgets 通常是以 ret 结尾的指令序列,通过这样的指令序列,我们可以多次劫持程序控制流,从而运行特定的指令序列,以完成攻击的目的。

返回导向编程这一名称的由来是因为其核心在于利用了指令集中的 ret 指令,从而改变了指令流的执行顺序,并通过数条 gadget “执行” 了一个新的程序。

使用 ROP 攻击一般得满足如下条件:

  • 程序漏洞允许我们劫持控制流,并控制后续的返回地址。
  • 可以找到满足条件的 gadgets 以及相应 gadgets 的地址。

作为一项基本的攻击手段,ROP 攻击并不局限于栈溢出漏洞,也被广泛应用在堆溢出等各类漏洞的利用当中。

0x01 ret2text

ret2text 是指利用程序已有的 .text 段代码进行攻击的 ROP 类型。

简单的原理如上图,即覆盖栈内的 Return Address 达到劫持程序流的作用。

这里用 CTFWiki 上的 ret2text 作示例,首先用 checksec 命令查看安全信息。

可以看到打开了 NX (Non-Excutable) 保护,作用是使数据,堆栈和堆段不可执行,而代码段不可写

使用 Binja 反编译发现使用了可能导致栈溢出的函数 gets,同时发现 secure 函数中有着能够获取 Shell 的代码:

这时我们的主要目标就明朗了:覆盖 main 函数栈的 retaddr0x804863A 拿到 Shell,开始构造 Payload。

我们会遇到一个问题:如何确定栈的深度?即如何确认我们需要在栈内覆盖多少大小的数据?

首先我们给出简单的方法:覆盖栈直到程序产生栈溢出错误。方法可以参考这篇文章:gdb调试之超长字符生成与定位_gdb cyclic-CSDN博客

我们用 pwngdb 来调试,首先用命令 cyclic 200 生成长度为 200 的字符串,输入 r 运行程序,粘贴上面生成的字符串回车,可以看到程序产生了栈溢出错误。

此时 EIP 的值为 daab 。我们再用 cyclic -l daab 命令获取偏移量。

这说明我们需要填充 112 个字符,所以我们可以得到以下的 Exploit:

from pwn import *
sh = process('./ret2text')
addr = 0x0804863a
payload = b'a' * 112 + p32(addr)
sh.sendline(payload)
sh.interactive()

成功得到 Shell,但是仍然存在着一些问题需要深究:

  1. 为什么用的是 EIP 的值去计算偏移值?
  2. 能否不用这种方法,直接计算偏移值?

首先来看第一个问题,我们首先要知道 EIP 寄存器的作用:存储即将执行的程序命令的地址

0x0 References

  1. 知乎 – 【PWN】学习笔记(二)【栈溢出基础】

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注