看的有点吃力,太菜了….

流程&重要文件记录

记录一下View.php的功能 他是路由中比较中要的文件 先看一个数据包:
Alt text
先关注一下 标记的点 他们确定了文件的调用 部分代码:

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
if($rock->contain($m, '|')){
$_mas = explode('|', $m);
$m = $_mas[0];
$_m = $_mas[1];
}
include_once($rock->strformat('?0/?1/?1Action.php',ROOT_PATH, $p));
$rand = date('YmdHis').rand(1000,9999);
if(substr($d,-1)!='/' && $d!='')$d.='/';
$errormsg = '';
$methodbool = true;
$actpath = $rock->strformat('?0/?1/?2?3',ROOT_PATH, $p, $d, $_m);
define('ACTPATH', $actpath);
$actfile = $rock->strformat('?0/?1Action.php',$actpath, $m);
$actfile1 = $rock->strformat('?0/?1Action.php',$actpath, $_m);
$actbstr = null;
if(file_exists($actfile1))include_once($actfile1);
if(file_exists($actfile)){
include_once($actfile);
$clsname = ''.$m.'ClassAction';
$xhrock = new $clsname();
$actname = ''.$a.'Action';
if($ajaxbool == 'true')$actname = ''.$a.'Ajax';
if(method_exists($xhrock, $actname)){
$xhrock->beforeAction();
$actbstr = $xhrock->$actname();
if(is_string($actbstr)){echo $actbstr;$xhrock->display=false;}
if(is_array($actbstr)){echo json_encode($actbstr);$xhrock->display=false;}
}else{
$methodbool = false;
if($ajaxbool == 'false')echo ''.$actname.' not found';
}
$xhrock->afterAction();
}else{
echo ''.$actfile.' not found';
$xhrock = new Action();

其中将/?1相应部分 文件地址 类文件 这样来实例化 $_GET['a']为调用对应方法 如果$ajaxbool为True会调用$a.'Ajax'方法 不然就调用$a.'Action'
方法
其中对于控制器以flow/input文件为主 一般类以mode_开头 父类为目录下的inputAction.php 其中他的父类为webmain\webmainAction.php中的Action类 他的父类为include\Action.php中的mainAction 比较绕 关注他的初始化 其中mainAction中给出:

1
2
3
4
5
6
7
8
9
10
11
12
13
$this->rock = $GLOBALS['rock'];
$this->smarty = $GLOBALS['smarty'];
$this->jm = c('jm', true);
$this->now = $this->rock->now();
$this->date = $this->rock->date;
$this->ip = $this->rock->ip;
$this->web = $this->rock->web;
$this->perfix = PREFIX;
$this->display = true;
$this->initMysql();
$this->initConstruct();
$this->initProject();
$this->initAction();

会首先调用几个初始化函数 关注后两个函数 其中initProjectwebmainaction.php给出:

1
2
3
4
5
6
7
8
public function initProject()
{
$this->getlogin();
if($this->rock->get('ajaxbool')=='true')return;
$this->smartydata['adminid'] = $this->adminid;
$this->smartydata['adminuser'] = $this->adminuser;
$this->smartydata['adminname'] = $this->adminname;
}

调用了getlogin()来获取登陆session信息:

1
2
3
4
5
6
7
8
9
10
11
12
public function getlogin($lx=0)
{
$this->ajaxbool = $this->rock->jm->gettoken('ajaxbool', 'false');
$this->adminid = (int)$this->getsession('adminid',0);
$this->adminuser = $this->getsession('adminuser');
$this->adminname = $this->getsession('adminname');
$this->admintoken = $this->getsession('admintoken');
$this->setNowUser($this->adminid, $this->adminname, $this->adminuser);
$agid = $this->rock->get('agentid');
if($agid!='')$this->rock->savesession(array('wxqyagentid' => $agid));
if($lx==0)$this->logincheck();
}

再记录一下他的模块调用(模块太多了………)
主要是使用m函数 一种使用m("xxx")就调用model/xxxxModel.php 如果xxxModel不存在 即实例化Model模块 一种是m('xx:'.'pp')就调用model/xx/xx.php 如果文件存在 实例化 传入pp参数 一般第二种方式 是先调用flowModel.php中的initflow.php方法去完成:

1
2
3
4
5
6
7
public function initflow($num,$mid=null, $isqx=true)
{
$this->flow = m('flow:'.$num.'');
$this->flow->initdata($num);
if($mid != null)$this->flow->loaddata($mid, $isqx);
return $this->flow;
}

还有一点是 对于模块的初始化 如第一种情况 实例化了Model 则将接收的参数将为 所对应的表名:

1
2
3
4
5
6
7
8
9
public function __construct($table='')
{
$this->rock = $GLOBALS['rock'];
$this->db = $GLOBALS['db'];
$this->adminid = $this->rock->adminid;
$this->adminname = $this->rock->adminname;
$this->settable($table);
$this->initModel();
}

对于第二种 如果是m('flow:'.num) 则会有如下流程

1
->flowModel.php->initflow()->flow.php->initdata()->Model.php('flow_set') 即查询flow_set表 初始化相应数据

获取参数

大多使用的post方式来获取参数 他对post传参的处理是调用jmuncode函数 :

1
2
3
4
5
6
7
$s=str_replace("'", '&#39', $s);
if($lx==2)$s=str_replace(array('{','}'), array('[H1]','[H2]'), $s);
$str = strtolower($s);
foreach($this->lvlaras as $v1)if($this->contain($str, $v1)){
$this->debug(''.$na.'《'.$s.'》error:包含非法字符《'.$v1.'》','params_err');
$s = str_replace($v1,'', $str);
}

会检测关键字 做记录 最后的处理是将关键字替换为空 所以这里导致可以绕过关键字 但是会将单引号替换为字符实体 根据具体情况来看

数据库操作

关注几个关键函数
toaddval函数:

1
2
3
4
5
$adstr="'$str'";
if($this->isempt($str)){
$adstr='null';
}
return $adstr;

简单的加上单引号操作 但是前面提到 post传参做了对单引号的操作 所以对于psot传参调用了toaddval函数 基本避免了注入

多出储存型XSS

cms在保存数据时 会调用saveAjax函数 比如发送邮件
用户发送邮件给其他人 其中在文件标题处存在储存型XSS
抓个包分析:
Alt text
可以看到调用webmain\flow\input\mode_emailmAction.php 如最上面分析 $_GET['a']=save 所以会调用inputAction.phpsaveAjax方法 其中有一个数据处理函数 xxsstolt函数:

1
2
3
4
5
6
7
foreach($uaarr as $k=>$v){
$vss = strtolower($v);
if(contain($vss, '<script')){
$uaarr[$k] = str_replace(array('<','>'),array('&lt;','&gt;'), $v);
}
}
return $uaarr;

我觉得挺复杂的一个cms 这过滤也是挺水的…..
所以可以比较容易的绕过 不适用<script和单引号即可
所以效果:
Alt text
Alt text
管理员点击收信箱 就中招了….
还真有点不明白 他们本身是有做处理 但是这里偏偏又没做处理
Alt text
用户资料储存型xss:
Alt text

最后去看这个cms漏洞的发现 发现这个漏洞好像已经有一段时间了:
Alt text
应该是一直没修

最后

这个cms代码挺多 比较绕 主要都是公司内部使用
没有信心在看下去了 先把这个cms放一下了 来日再战……太菜了