Sublarge

// 安全研究 · 2025-08-18

二进制程序动态链接风险与检测脚本实践

本文深入剖析了Linux动态链接机制的安全风险,从环境变量劫持(LD_PRELOAD/LD_LIBRARY_PATH)、系统配置文件(/etc/ld.so.preload)篡改、二进制文件路径配置(RPATH/RUNPATH)漏洞,到库文件权限问题和GOT/PLT运行时攻击面。针对这些风险,文章提出了一套Bash检测脚本方案,可自动检查库文件权限、路径配置和核心库安全性,帮助运维人员快速识别依赖库安全隐患。

在现代 Linux 系统中,动态链接机制是共享库复用的基石,但同时也是攻击者实施代码注入、权限提升和持久化的重要攻击面。本文从动态链接器的工作流程出发,系统梳理了从环境变量劫持到 GOT/PLT 覆写的多层风险,并配套提供了一套可落地的 Bash 检测脚本,帮助安全运维人员快速排查二进制程序的依赖安全隐患。


一、动态链接器的工作流程与攻击面

当用户在 Linux 上执行一个动态链接的 ELF 二进制文件时,内核首先加载程序本身,随后将控制权交给动态链接器 ld.so(或 ld-linux.so)。链接器按照特定顺序解析依赖库,其搜索优先级如下:

  1. LD_PRELOAD 环境变量指定的库(除非程序具有 setuid/setgid 位,此时会被 glibc 的 AT_SECURE 机制自动清除)
  2. /etc/ld.so.preload 系统级预加载配置文件(需要 root 权限修改,但一旦写入即可实现系统级持久化)
  3. RPATH 和 RUNPATH 二进制文件中硬编码的路径(RPATH 优先级高于 LD_LIBRARY_PATH,而 RUNPATH 反之)
  4. LD_LIBRARY_PATH 环境变量
  5. 系统标准路径(/lib、/usr/lib、/lib64、/usr/lib64 等,由 /etc/ld.so.conf 定义)
flowchart TD
    A[程序启动] --> B[内核加载 ELF]
    B --> C[调用 ld.so 动态链接器]
    C --> D{检查 /etc/ld.so.preload}
    D -->|存在| E[预加载恶意库]
    D -->|不存在| F{检查 LD_PRELOAD}
    F -->|存在| G[加载攻击者库]
    F -->|不存在| H{检查 RPATH/RUNPATH}
    H -->|包含可写目录| I[加载恶意 .so]
    H -->|安全| J{检查 LD_LIBRARY_PATH}
    J -->|被篡改| K[加载伪造库]
    J -->|正常| L[按标准路径加载]
    L --> M[解析符号 → GOT/PLT]
    M --> N[程序执行]
    E --> N
    G --> N
    I --> N
    K --> N
    style E fill:#f96,stroke:#333
    style G fill:#f96,stroke:#333
    style I fill:#f96,stroke:#333
    style K fill:#f96,stroke:#333

每一个解析节点都可能成为攻击者的突破口。以下分层次展开具体风险。


二、风险分层详解

2.1 环境变量层:LD_PRELOAD 与 LD_LIBRARY_PATH

LD_PRELOAD 允许用户在程序启动前强制加载指定的共享对象,其加载优先级甚至高于程序自身依赖的库。攻击者可以编写一个包含恶意代码的 .so 文件,通过劫持标准库函数(如 connect()、read()、getuid() 等)实现数据窃取、权限提升或后门植入。

一个典型的提权场景是:当 sudo -l 显示某用户可以以 root 身份运行特定命令,且该命令未被配置为清除 LD_PRELOAD 时,攻击者即可注入恶意库获取 root shell。

LD_LIBRARY_PATH 则通过篡改库搜索路径实现劫持。例如,攻击者将伪造的 libssl.so.3 放入 /tmp/evil/,设置 LD_LIBRARY_PATH=/tmp/evil,即可拦截目标程序的 TLS 会话密钥。

缓解措施:

  • 对 setuid/setgid 程序,glibc 会自动忽略 LD_PRELOAD(AT_SECURE 机制)。
  • 通过 SELinux/AppArmor 策略限制不可信库的 mmap(PROT_EXEC)。
  • 在容器中使用 readOnlyRootFilesystem: true 防止 /etc/ld.so.preload 被篡改。

2.2 系统配置层:/etc/ld.so.preload

与 LD_PRELOAD 不同,/etc/ld.so.preload 是一个系统级配置文件,写入其中的库路径会被 所有 动态链接进程加载。攻击者在获取 root 权限后,将恶意库路径写入该文件,即可实现跨重启的持久化后门。

由于该文件需要 root 权限才能修改,其检测重点在于:

  • 监控文件内容的非预期变更(通过 auditd 或 AIDE)。
  • 使用 chattr +i 将其设为不可变。
  • 定期比对文件哈希或内容签名。

2.3 二进制层:RPATH 与 RUNPATH

RPATH 和 RUNPATH 是在编译链接时写入 ELF 文件的动态段(Dynamic Section)的路径信息,用于指定程序查找依赖库的私有路径。两者的关键区别在于优先级:

  • RPATH:优先级最高,无法 被 LD_LIBRARY_PATH 覆盖。如果 RPATH 指向的目录是用户可写的(如 /tmp、$ORIGIN/../lib),攻击者只需在该目录放置同名恶意库即可实现劫持。
  • RUNPATH:优先级低于 LD_LIBRARY_PATH,管理员可以通过设置环境变量来临时覆盖危险路径,但攻击者同样可以利用这一点注入恶意库。

此外,$ORIGIN 相对路径和空路径(::)会导致链接器搜索当前工作目录,进一步扩大了攻击面。

2.4 文件系统层:库文件权限与目录可写性

即使链接器按正确路径找到了库文件,如果该库文件本身权限过于宽松(如 chmod 777),或者所在目录对非特权用户可写,攻击者仍然可以:

  • 直接替换库文件内容(如将 /usr/lib/libcustom.so 替换为植入后门的版本)。
  • 利用目录写权限创建同名库进行抢占。

对于 root 所有的系统库(如 libc.so.6),如果存在其他用户写权限(o+w),则任何本地用户都可以直接篡改核心系统库,造成全局性的权限提升风险。

2.5 运行时层:GOT/PLT 覆写

动态链接通过 全局偏移表(GOT) 和 过程链接表(PLT) 实现延迟绑定(Lazy Binding)。程序首次调用外部函数时,PLT 跳转到 GOT,由动态链接器解析真实地址并回填 GOT;后续调用直接通过 GOT 跳转。

由于 GOT 在默认情况下是可写的,攻击者若能通过缓冲区溢出、格式化字符串漏洞或任意地址写等手段修改 GOT 中的函数指针,即可将 printf() 的地址替换为 system() 的地址,实现非预期的代码执行。这种攻击被称为 GOT Poisoning。

缓解措施:

  • Full RELRO:编译时添加 -Wl,-z,relro,-z,now,强制链接器在程序启动时解析所有符号并将整个 GOT 标记为只读。
  • PIE + ASLR:配合地址空间布局随机化,增加攻击者定位 GOT 的难度。

三、检测脚本设计与实现

基于上述风险模型,我们设计了一套 Bash 检测脚本 check_lib.sh,覆盖文件系统层和二进制层的关键检查点。脚本核心逻辑如下:

flowchart LR
    subgraph "攻击面层级"
        A1[环境变量层<br/>LD_PRELOAD / LD_LIBRARY_PATH]
        A2[系统配置层<br/>/etc/ld.so.preload]
        A3[二进制层<br/>RPATH / RUNPATH]
        A4[文件系统层<br/>库文件权限 / 目录可写性]
        A5[运行时层<br/>GOT/PLT 覆写]
    end
    
    subgraph "检测脚本覆盖"
        D1[✓ 检查库文件权限]
        D2[✓ 检查 RPATH/RUNPATH]
        D3[✓ 检查 libc 路径]
        D4[✗ 环境变量审计]
        D5[✗ GOT/PLT 完整性]
    end
    
    A1 -.-> D4
    A2 -.-> D4
    A3 -.-> D2
    A4 -.-> D1
    A5 -.-> D5

3.1 脚本功能模块

模块一:库文件权限审计

脚本通过 ldd 提取目标程序的所有依赖库路径,逐一检查:

  • 权限位是否包含组写(g+w)或其他用户写(o+w)。
  • 对于 root 所有的库,若存在 o+w,则标记为 高危(存在本地提权风险)。
  • 库文件是否真实存在(防止 broken dependency 被利用)。

模块二:RPATH / RUNPATH 配置检查

使用 readelf -d 提取 RPATH 和 RUNPATH:

  • 检测是否包含相对路径(./)或空路径(::),这些会导致链接器搜索当前目录。
  • 检测路径是否指向用户可写目录(通过 [ -w "$path" ] 判断),可写目录意味着攻击者可以放置同名库进行劫持。

模块三:libc 路径安全性验证

libc.so.6 是所有动态链接程序的核心依赖。脚本单独检查:

  • 其路径是否位于系统标准目录(/lib、/lib64、/usr/lib、/usr/lib64)。
  • 若 libc 位于非标准路径(如 /opt/、/home/),则警告可能被替换。
  • 检查 libc 自身的权限位,防止核心库被篡改。

3.2 完整脚本代码

3.3 使用示例

# 赋予执行权限
chmod +x check_lib.sh

# 检测 /usr/bin/ssh 的依赖安全
./check_lib.sh /usr/bin/ssh

# 检测自定义程序
./check_lib.sh /opt/myapp/bin/server
bash8 行

四、检测能力边界与扩展建议

当前脚本主要覆盖 文件系统层 和 二进制层 的静态检查,对于以下攻击面仍需配合其他工具或手段进行补充:

攻击面 当前脚本覆盖 补充检测手段
LD_PRELOAD / LD_LIBRARY_PATH ✗ 审计进程环境变量(ps eww <pid> / /proc/<pid>/environ)
/etc/ld.so.preload ✗ 文件完整性监控(AIDE、Tripwire)、auditd 规则
RPATH / RUNPATH 可写目录 ✓ 脚本已覆盖
库文件权限异常 ✓ 脚本已覆盖
libc.so.6 非标准路径 ✓ 脚本已覆盖
GOT/PLT 运行时覆写 ✗ 内存取证(Volatility linux_plthook)、Full RELRO 编译检查
库文件内容篡改 ✗ IMA/EVM 完整性度量、dm-verity、RPM/dpkg 校验

扩展建议:

  1. 环境变量审计:在关键服务启动脚本中显式 unset LD_PRELOAD LD_LIBRARY_PATH,或使用 env -i 清理环境。
  2. 编译时加固:对所有安全关键型二进制文件启用 -Wl,-z,relro,-z,now(Full RELRO)和 -fPIE -pie(PIE),消除 GOT 覆写和地址预测风险。
  3. 静态链接关键工具:对于必须在受感染环境中运行的取证或审计工具(如 aide、chkrootkit),建议静态编译以彻底消除动态链接攻击面。
  4. 容器加固:在 Kubernetes 中启用 readOnlyRootFilesystem: true 和 allowPrivilegeEscalation: false,防止容器内篡改链接器配置。

五、总结

动态链接机制在提升系统资源利用率和软件可维护性的同时,也引入了从环境变量、系统配置、二进制元数据到运行时内存的多层攻击面。理解 ld.so 的库解析优先级(LD_PRELOAD → /etc/ld.so.preload → RPATH → LD_LIBRARY_PATH → 系统路径)是构建防御体系的前提。

本文配套的 check_lib.sh 脚本聚焦于 静态层面的快速排查,能够在日常安全巡检中高效发现库权限异常、RPATH/RUNPATH 配置缺陷和 libc 路径异常等问题。对于更深层的运行时攻击(如 GOT 覆写)和持久化手段(如 /etc/ld.so.preload),则需要结合编译时加固(Full RELRO、PIE)、文件完整性监控和内存取证工具,形成纵深防御体系。


参考来源

  • SystemsHardening: Linux Shared Library Security — LD_PRELOAD Attacks, Library Hijacking, and Hardened Linking
  • CSDN: ELF 文件中的 RPATH 和 RUNPATH 安全风险分析
  • CoderMusings: Dynamic Linking & Relocations — How GOT and PLT Work
  • USENIX Security Symposium: PLT/GOT Randomization
  • Vickie Li: Attacking Dynamic Linking — GOT Overwrite and RELRO
  • GitHub: Library Injection via /etc/ld.so.preload
  • dpkg-buildflags(1) — RELRO, BIND_NOW, PIE Hardening

原文 https://blog.csdn.net/2301_79518550/article/details/150486366