Featured image of post CTF PWN 常见二进制保护机制详解

CTF PWN 常见二进制保护机制详解

pwn中常见的五种保护机制

CTF PWN 常见二进制保护机制详解

在 CTF PWN 题中,拿到题目的第一步通常是使用 checksec 命令查看程序的保护开启情况。以下是五大核心保护机制的原理、作用及绕过思路。

1. Canary (栈溢出保护)

全称:Stack Canary / Stack Smashing Protector (SSP) 原理: 类似于煤矿中的金丝雀(Canary),系统会在栈底(EBP)之上、局部变量之下插入一个随机生成的数值(通常是 4 字节或 8 字节)。 函数在返回(ret)之前,会检查这个值是否被修改。如果发生了栈溢出,这个值通常会被覆盖,程序就会检测到并抛出 stack smashing detected 错误,从而终止运行。

内存布局

[ buffer (局部变量) ]
[ Canary (随机值)   ]  <--- 溢出必须经过这里
[ Old EBP           ]
[ Return Address    ]

绕过思路

  • 泄露 (Leak):利用格式化字符串漏洞或数组越界读取,先读出 Canary 的值,在构造 Payload 时填回去。
  • 爆破 (Brute Force):仅限于 fork 出的子进程(如网络服务),因为子进程 Canary 不变,可以逐字节爆破。
  • 劫持 __stack_chk_fail:如果能改写 GOT 表,劫持报错函数的地址,使其跳转到后门。

2. NX (不可执行内存)

全称:No-Execute (Linux) / DEP (Windows)

原理:

将数据所在的内存页(如 Stack 和 Heap)标识为不可执行(Not Executable)。只有代码段(Code Segment)拥有执行权限。

这意味着即使你把 Shellcode 写入了栈中,CPU 也不会执行它,而是直接报错。

绕过思路

  • ROP (Return Oriented Programming):既然不能执行栈上的代码,就利用程序现有的代码片段(Gadgets)。通过构造栈帧,让程序在现有的 ret 指令之间跳跃,拼凑出攻击逻辑(如执行 system("/bin/sh"))。
  • ret2libc:跳转到 libc 库中的函数去执行。

3. PIE (代码段地址随机化)

全称:Position Independent Executable

原理:

开启 PIE 后,程序每次加载到内存时,其代码段(Code Segment)的基地址是随机的。

这意味着你在 IDA 里看到的固定地址(如 0x400520)在实际运行时会变动,你无法直接硬编码函数的地址。

区别

  • No PIEmain 函数地址通常固定(如 0x400xxx)。
  • PIE Enabledmain 函数地址随机,但偏移量(Offset)固定。

绕过思路

  • 泄露基址:利用漏洞泄露栈上保存的某个代码段地址(如返回地址),减去其在 IDA 中的偏移,算出程序的 Base Address
    • 公式:真实地址 = Base Address + 偏移量
  • Partial Overwrite:利用低 12 位(页内偏移)通常不变的特性,仅覆盖返回地址的低几位(有一定的爆破概率)。

4. ASLR (系统级地址随机化)

全称:Address Space Layout Randomization

原理:

这是操作系统的功能(Linux内核配置),不是编译选项。它负责随机化**堆(Heap)、栈(Stack)和共享库(libc)**的加载位置。

查看方式:

cat /proc/sys/kernel/randomize_va_space

分为三个等级:

0: 关闭 (Disabled)

  • 特征:没有任何随机化。
  • 表现:栈 (Stack)、堆 (Heap)、动态库 (Libc) 的加载基址全部是固定的。
  • 利用:攻击者可以在本地调试好地址,直接硬编码攻击远程(前提是远程也是0)。

1: 保守随机化 (Conservative Randomization)

  • 特征“栈乱,堆不乱”
  • 随机区域:栈 (Stack)、动态库 (Libc/mmap)、vDSO。(注意:Libc 在这里通常是随机的)
  • 固定区域堆 (Heap)。此时堆的基地址紧跟在程序数据段(Data Segment)之后,如果程序本身没有开启 PIE,那么堆的地址就是固定的。
  • 利用:常用于攻击堆溢出相关的题目,因为堆块位置可预测。

2: 完全随机化 (Full Randomization) —— [标准环境]

  • 特征“全乱”
  • 表现:在等级 1 的基础上,堆 (Heap) 的基地址也是随机的。
  • 现状:这是现代 Linux 系统(以及大多数 CTF 远程服务器)的默认配置。

PIE vs ASLR

  • PIE 决定了程序本身(Main Binary) 是否随机。
  • ASLR 决定了依赖库(libc)、栈和堆 是否随机。
  • 注意:CTF 题目远程服务器通常默认开启 ASLR=2。

绕过思路

  • Leak Libc:利用 putswrite 等输出函数,打印 GOT 表中已解析的函数地址,算出 libc 的基址(Base),再计算 system/bin/sh 的真实地址。

5. RELRO (重定位只读)

全称:Relocation Read-Only

原理:

用于保护 GOT 表(Global Offset Table)不被恶意修改。

分类

  • No RELRO:GOT 表可读可写,非常危险。
  • Partial RELRO (GCC 默认):
    • 非 PLT 部分只读。
    • GOT 表仍然可写
    • 意味着我们可以修改 GOT 表中的函数地址(如把 printf 改成 system)。
  • Full RELRO
    • 程序加载时一次性解析所有函数。
    • 整个 GOT 表只读
    • 攻击者无法修改 GOT 表,必须通过其他方式(如 Hook 劫持、写 Stack)攻击。

总结:Checksec 对照表

当你输入 checksec ./pwn 时:

保护项 显示结果 含义 攻击影响
Arch amd64-64-little 64位程序 寄存器传参,地址 8 字节
RELRO Partial RELRO GOT 表可写 可以使用 GOT Overwrite 攻击
Stack Canary found 开启 Canary 需泄露 Canary 才能溢出
NX NX enabled 栈不可执行 必须使用 ROP,不能用 Shellcode
PIE PIE enabled 开启 PIE 需泄露程序基址才能用 Gadget
This blog has been running for
Published 11 posts · Total 48.46k words