0x01
最近审计了一波seacms,官网:https://www.seacms.net 。版本是最新版本的(审计时),和那些单入口框架不同,这个cms审计起来很圆润,但是过滤的就很干干巴巴,印象中好像看到了3个过滤拦截页面,360webscan(cmseasy也有)一个,好像自带还有两个,丧心病狂。但还是盘出来一个可以绕过的注入。
0x02
首先了解了下参数传递方式。核心文件是/include/common.php,很多文件都包含了这个文件。看下这个文件,114-117行:
foreach(Array('_GET','_POST','_COOKIE') as $_request){ foreach($$_request as $_k => $_v) ${$_k} = _RunMagicQuotes($_v);}
GET、POST和Cookie的参数会经过一次变量覆盖,然后把值$_v赋给对应的变量$$_k,这里会对$_v进行处理,单引号啥的都会被转义。这里既然可以进行变量覆盖,也就是说像一些全局变量我都可以控制!!!感觉这里问题很大。
接下来看20-26行:
foreach($_REQUEST as $_k=>$_v){ if( strlen($_k)>0 && m_eregi('^(cfg_|GLOBALS)',$_k) && !isset($_COOKIE[$_k]) ) { exit('Request var not allow!'); }}
这里的代码在上一步进行变量覆盖之前进行了一次过滤,不能覆盖全局变量$GLOBALS和一些配置变量(以$cfg_开头)。但是过滤的是$_REQUEST,也就是说只能过滤$_POST和$_GET。这里把想覆盖的变量放到在Cookie里面就可以绕过。比如下图,cfg_runmode,cfg_paramset两个配置变量的值都被赋值为1。
0x03
上一步确定了思路,接下来找一波包含common.php的文件。大概有这么多,忽略掉后台的。
这些文件在进行变量覆盖后都会做一些整数化处理,比如
类似这种处理的地方特别多。
0x04
第二处变量覆盖。
在/detail/index.php文件中。echoContent($vId)方法中,第38行进行了sql语句拼接,$row=$dsql->GetOne("Select d.*,p.body as v_playdata,p.body1 as v_downdata,c.body as v_content From `sea_data` d left join `sea_playdata` p on p.v_id=d.v_id left join `sea_content` c on c.v_id=d.v_id where d.v_id='$vId'"),这里把$vId拼接了进去,并且需要单引号闭合才可以注入。
if($GLOBALS['cfg_runmode']==2||$GLOBALS['cfg_paramset']==0){ $paras=str_replace(getfileSuffix(),'',$_SERVER['QUERY_STRING']); $id=intval($paras); $id = (isset($id) && is_numeric($id) ? $id : 0);}else{ $id=$$GLOBALS['cfg_paramid'];}if($id==0){ showmsg('参数丢失,请返回!', -1); exit;}echoContent($id);function echoContent($vId){ global $dsql,$cfg_iscache,$mainClassObj,$t1,$cfg_user; $row=$dsql->GetOne("Select d.*,p.body as v_playdata,p.body1 as v_downdata,c.body as v_content From `sea_data` d left join `sea_playdata` p on p.v_id=d.v_id left join `sea_content` c on c.v_id=d.v_id where d.v_id='$vId'"); if(!is_array($row)){ShowMsg("该内容已被删除或者隐藏