本页目录
ASLR 与安全防护
覆盖: ASLR (stack/mmap/exec) → PIE → RELRO → stack canary → NX → CET (IBT/SHSTK) → CFI → sanitizers as defense 适用: Linux 用户态, GCC/Clang + glibc + kernel
概述
ASLR 是 Linux 用户态安全的第一道防线——通过随机化地址空间布局,让攻击者无法预测代码、数据和栈的位置。但 ASLR 单独不够——PIE、RELRO、stack canary、NX 各自解决一类攻击面,组合使用形成纵深防御。
ASLR 三层
# /proc/sys/kernel/randomize_va_space
# 0: 关闭
# 1: 随机化 mmap base + stack + vDSO (partial)
# 2: 全部随机化 + brk (full, 默认)
# 随机化哪些:
# - stack: 每次 exec 随机偏移 (可达 ~8MB 随机范围)
# - mmap base: 共享库 .so 和匿名 mmap 的起始地址
# - executable: 仅 PIE (Position-Independent Executable)
# - brk (heap): full mode 下随机
# 不受 ASLR 影响:
# - 非 PIE 可执行文件: 固定地址 (0x400000)
# - vsyscall: 固定地址 (0xffffffffff600000)
PIE: Position-Independent Executable
# 非 PIE (传统, GCC 默认 -no-pie, 已过时):
# .text 加载到固定地址 0x400000
# → 攻击者知道所有 gadget 的地址 → ROP 简单
# PIE (现代默认, GCC -pie):
# 整个 binary 像 .so 一样可以随机加载 → ASLR 生效
# 加载基址在每次 exec 时随机
# 查看加载基址:
|
# PIE 的性能代价: ~2-3% (GOT 的额外间接)
# 通过 -fno-plt 可以消除部分 (直接 GOT 访问, 跳过 PLT)
RELRO: 重定位只读
# 链接器选项:
# -Wl,-z,norelro: 无保护
# -Wl,-z,relro: 部分保护 (默认)
# -Wl,-z,now: 完全保护 (BIND_NOW + RELRO)
# 效果:
# Partial RELRO:
# .got 和 .dynamic 在 startup 后变为 read-only
# .got.plt 仍然可写 (lazy binding 需要)
#
# Full RELRO (BIND_NOW):
# 所有 GOT 在 startup 后变为 read-only
# 代价: 所有符号在 startup 时立即解析 → 启动稍慢
# 好处: GOT 不可写 → 无法覆盖 GOT pointer → 防止 GOT overwrite attack
# 检查:
readelf -l /bin/ls | grep GNU_RELRO # 有 → RELRO enabled
readelf -d /bin/ls | grep BIND_NOW # 有 → Full RELRO
Stack Canary
// GCC -fstack-protector (自动启用)
// 在每个函数的栈帧底部放一个 8-byte canary (随机值)
// 函数返回前检查 canary → 如果被改 → 栈溢出 → abort
// Canary 来源:
// /proc/sys/kernel/randomize_va_space → AT_RANDOM (16 bytes)
// 或: __stack_chk_guard (glibc TLS, 启动时从 AT_RANDOM 初始化)
// 检查:
// __stack_chk_fail() → __fortify_fail() → abort + 日志
// 编译器级别:
// -fstack-protector: 保护有 char buf[8+] 的函数
// -fstack-protector-strong: 保护有数组/地址取操作的函数 (推荐)
// -fstack-protector-all: 保护所有函数 (性能代价 ~5%)
NX (No-Execute)
硬件 (x86: XD bit / NX bit, ARM: XN):
ELF segment PF_X → 映射为 executable
栈和堆: 无 PF_X → 不可执行
→ 攻击者不能注入 shellcode 到堆/栈并跳转执行
→ 但 ROP 仍然有效 (复用已有的 .text 片段)
检查:
cat /proc/cpuinfo | grep nx # x86: 'nx' in flags
readelf -l /bin/ls | grep STACK # PT_GNU_STACK: RW (no X)
CET: Control-flow Enforcement (Intel 11th gen+)
IBT (Indirect Branch Tracking):
硬件: 间接跳转(jmp/call [reg])只能到 endbr32/endbr64 指令
→ ROP/JOP 跳到 gadget 中间 → CPU 检测 → #CP fault
SHSTK (Shadow Stack):
硬件: 维护独立的 shadow stack (不能通过常规 store 修改)
call: 硬件 push 返回地址到 shadow stack
ret: 硬件检查 shadow stack vs 普通栈 → 不匹配 → #CP fault
→ ROP 修改栈 → ret 时检测到 → 拒绝
编译器: GCC 8+ / Clang 7+ -fcf-protection=full
内核: CET 支持 (5.18+)
组合防御
攻击向量 缓解措施
────────────────────────────────────
栈溢出 → canary + NX + SHSTK
堆溢出 → FORTIFY_SOURCE, ASAN (调试)
GOT overwrite → Full RELRO
ROP/JOP → ASLR + PIE + IBT
信息泄露 → kptr_restrict, dmesg_restrict
UAF → ASAN (调试), SLAB_FREELIST_RANDOM
检查工具
# 检查 binary 的安全加固
# 或: hardening-check /bin/ls (Debian/Ubuntu devscripts)
# 输出:
# Position Independent Executable: yes
# Stack protected: yes
# Fortify Source functions: yes
# Read-only relocations: yes
# Immediate binding: yes
参考
- 内核文档:
Documentation/admin-guide/sysctl/kernel.rst(randomize_va_space) - LWN: "CET on x86", "Full RELRO and BIND_NOW"
- 工具:
checksec,hardening-check,pwntools
关键词: ASLR, PIE, RELRO, stack canary, NX, CET, IBT, SHSTK, FORTIFY_SOURCE, BIND_NOW