跳到主要内容

LD_PRELOAD 与 ld.so.preload 劫持

动态链接程序启动时,loader 会把 preload 列表中的共享库提前载入进程地址空间。利用分两条路线:环境变量 LD_PRELOAD 只影响一次调用或子进程,适合劫持由高权限任务派生的脚本;/etc/ld.so.preload 文件则对系统上每个动态链接程序生效,一旦能以 root 身份写入就是稳定的提权点。

仅限授权环境

写坏 /etc/ld.so.preload 或污染被 source 的配置文件,可能影响目标系统上所有动态链接程序。仅在授权环境操作,保留原始文件副本,验证后立即清理。


路线 A:LD_PRELOAD 环境注入

root 定时任务脚本常常 source 一个配置文件来读取变量。如果该配置文件对当前用户可写,就在里面追加一行:

export LD_PRELOAD=/var/tmp/evil.so

cron 以 root 身份执行脚本时,恶意 so 会随之加载进进程。

编译恶意 so

__attribute__((constructor)) void init(void) {
/* 库被加载时执行的目标动作 */
}
gcc -shared -fPIC -o /var/tmp/evil.so evil.c

__attribute__((constructor)) 标记的函数在库被加载时自动执行,不需要导出符号替换。

受限环境

fork 被禁、system 不可用时,constructor 内只用裸 syscall(如 writechmod)实现目标动作,避免依赖会触发 fork/exec 的 libc 封装。


路线 B:/etc/ld.so.preload 提权

/etc/ld.so.preload 列出的 so 会随每个动态链接程序 preload。若能以 root 身份写这个文件——例如利用可写组继承的 SUID touch 创建文件,或把输出路径 symlink 到可写输出原语——写入恶意 so 后,任意 root 动态程序启动都会触发。

常见 payload 是在 constructor 里生成 SUID shell:

__attribute__((constructor)) void init(void) {
chmod("/bin/bash", 04755);
}

挂载与清理注意事项

事项处理
/tmp 常有 sticky 位sticky 位提供 symlink 保护,软链接利用需换路径
/tmp 常带 nosuidSUID bash 副本放 /var/tmp 或 home 目录
攻击后清理还原 ld.so.preload,删除恶意 so 与 SUID 副本

审计清单

  1. /etc/ld.so.preload 是否存在、属主与内容是否异常。
  2. cron 引用的配置文件是否允许低权限用户写入。
  3. 系统上是否存在可写组继承的 SUID 工具、可被 symlink 的输出原语。
  4. find / -perm -4000 中是否有新增或非常见的 SUID 文件。

防御建议

  1. /etc/ld.so.preload 默认不存在,监控该文件的创建事件。
  2. cron 引用的配置文件属主 root 且权限 644。
  3. 定期审计异常 SUID 文件:find / -perm -4000
  4. 收紧 sudo 白名单中的任意写原语,避免输出路径被 symlink 替换。