这周强基计划有一道rctf的题目。感觉很有意思然后正好也通过这个题目扩展了一写东西。题目是rctf的verysafe。原理这里就不做过多的描述了,这里和大家分享一下关于pearcmd的一些利用技巧。
pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear
才会安装。
不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php
。
原本pear/pcel是一个命令行工具,并不在Web目录下,即使存在一些安全隐患也无需担心。但我们遇到的场景比较特殊,是一个文件包含的场景,那么我们就可以包含到pear中的文件,进而利用其中的特性来搞事。
这里我在验证过程中是使用的MAMP,他的安装目录为/Applications/MAMP/bin/php/php7.4.21/lib/php/pearcmd.php
.pearcmd的利用思路就是当php开启register_argc_argv
这个配置,用户的输入将会被赋予给$argc
、$argv
、$_SERVER['argv']
几个变量。
这里PHP读取命令行参数的逻辑可以在Getopt.php->readPHPArgv()中看到:
/**
* Safely read the $argv PHP array across different PHP configurations.
* Will take care on register_globals and register_argc_argv ini directives
*
* @return mixed the $argv PHP array or PEAR error if not registered
*/
public static function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
$msg = "Could not read cmd args (register_argc_argv=Off?)";
return PEAR::raiseError("Console_Getopt: " . $msg);
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
}
这里PHP会依此验证argv,$_SERVER[‘argv’]是否有值。后者可通过query-string控制。也就是说,可以通过Web访问了pear命令行的功能,且能够控制命令行的参数。
这里可以先看看pearcmd可以使用的命令有哪些:
这里重点可以利用的有config-create,install和download。这里关于这三个命令的具体利用方式,这里不做过多赘述。接来下是一些其他关于query.string的利用。看了p神的这文章以后,我比较感兴趣的还有p神的另外两篇文章分别是:《PHP-CGI远程代码执行漏洞(CVE-2012-1823)分析》和《[Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写》。通过这两篇文章,我大概学到的一些东西有:php-fpm,fast-cgi和php-cgi等一些基础的知识。
首先,php-fpm就是一个fastcgi的解释器,web中间件将用户请求按照fastcgi规则打包以后就会发送给fpm,然后fpm会按照fastcgi的规则将TCP流解析成真正的数据。完整的fastcgi的参数如下:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
举个例子,用户访问http://127.0.0.1/index.php?a=1&b=2
,如果web目录是/var/www/html
,那么Nginx会将这个请求变成如下key-value对:
{
'GATEWAY_INTERFACE': 'FastCGI/1.0',
'REQUEST_METHOD': 'GET',
'SCRIPT_FILENAME': '/var/www/html/index.php',
'SCRIPT_NAME': '/index.php',
'QUERY_STRING': '?a=1&b=2',
'REQUEST_URI': '/index.php?a=1&b=2',
'DOCUMENT_ROOT': '/var/www/html',
'SERVER_SOFTWARE': 'php/fcgiclient',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '12345',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': "localhost",
'SERVER_PROTOCOL': 'HTTP/1.1'
}
这里引起我兴趣的是p神提到的CVE-2012-1823,因为这里利用到的也是通过qurey.string来传入命令行参数。还有一个是p神提到的关于fpm和fastcgi的任意代码执行漏洞。这里漏洞的利用方式主要是设置auto_prepend_file = php://input
且allow_url_include = On
,然后将需要执行的代码放在Body中,即可执行任意代码。当然,这个漏洞的利用条件还比较苛刻。目前正在复现中。
参考文章:
《Fastcgi协议分析 && PHP-FPM未授权访问漏洞 && Exp编写》