32位程序的ROP攻击示例
给定的程序
readelf32
存在一个漏洞(想多了,这当然不是Linux里的readelf
),它读取文件内容写入到一个局部变量,但没有正确地检查文件大小。
实验中假设 ASLR 已经关闭: 1
echo 0 >/proc/sys/kernel/randomize_va_space
用 IDA Pro 反编译得到如下伪代码,其中数组的名字和大小是我自己修改的:
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
嗯,一个明显的不能再明显的越界。我们要做的是,利用越界修改栈,使得程序执行我们想要的代码。本题中,我们想删除一个文件“data”,所以要做的事情有:
- 系统调用:
unlink("data")
- 系统调用:
exit(0)
Linux系统调用的方式如下:
- 设置
%eax
寄存器为系统调用号; - 寄存器
%ebx
、%ecx
、%edx
、%esi
和%edi
依次存放需要的参数。本次实验两个系统调用均只需一个参数。即设置%ebx
为需要的参数; - 执行
int $0x80
.
unlink
系统调用号为 0xa
,exit系统调用号为
0x1
。
对于 unlink("data")
可以拆分成如下三条 Gadgets,均能在
libc 里轻松找到:
1 | pop eax ; ret |
栈中的内容依次是:
1 | libc_base_addr + 0x2470F, # pop eax ; ret |
注意,这里的 "data"
字符串恰好在libc的内存中可以找到(包含结尾的\0
),那我们就不客气了,直接拿来用。如果没有怎么办?最简便的做法是,在栈里存储字符串,并用一些
MOV 和 POP 指令把这些字符写入 .data
段。参见 Ref.1
同理,可以得到 exit
系统调用应该准备的栈内容。
写了一个 Python 脚本来产生最终的 payload 文件:
1 | import struct |
之后运行程序看看效果:
可以看到,data
文件被删掉了。
References:
Helpful Links: