审计悟空的缘由是看见某云爆出CRM的getshell,于是就想着去挖出来瞅瞅!但可能自己把自己给局限了,就想着去挖那些无限制访问的文件。
漏洞文件:/App/Lib/Action/LogAction.class.php
public function wxadd(){ if($_POST['subject']){ $log = M('Log'); $log->create(); $log->create_date = time(); $log->update_date = time(); $log->role_id = $_GET['id']; if($log->add()){ $this->success(L('ADD SUCCESS', array(L('LOG')))); }else{ $this->error(L('ADD_LOG_FAILED')); } }else{ $this->display(); } }
判断是否为post请求,如果条件为真则进入添加(这里要说一下,网站是开启了GPC的,但是存储入数据库时是进行了还原的)
public function index(){ $m_log = M('Log'); $m_comment = M('Comment'); $where = array(); $params = array(); $order = "create_date desc"; if($_GET['desc_order']){ $order = trim($_GET['desc_order']).' desc'; }elseif($_GET['asc_order']){ $order = trim($_GET['asc_order']).' asc'; } $below_ids = getSubRoleId(false); $all_ids = getSubRoleId(); $module = isset($_GET['module']) ? trim($_GET['module']) : ''; $by = isset($_GET['by']) ? trim($_GET['by']) : ''; switch ($by) { case 'today' : $where['create_date'] = array('gt',strtotime(date('Y-m-d', time()))); break; case 'week' : $where['create_date'] = array('gt',(strtotime(date('Y-m-d', time())) - (date('N', time()) - 1) * 86400)); break; case 'month' : $where['create_date'] = array('gt',strtotime(date('Y-m-01', time()))); break; case 'add' : $order = 'create_date desc'; break; case 'update' : $order = 'update_date desc'; break; case 'sub' : $where['role_id'] = array('in',implode(',', $below_ids)); break; case 'me' : $where['role_id'] = session('role_id'); break; default : $where['role_id'] = array('in',implode(',', $all_ids)); break; } if ($_GET['r'] && $_GET['module']){ $m_r = M($_GET['r']); $log_ids = $m_r->getField('log_id', true); $where['log_id'] = array('in', implode(',', $log_ids)); } if (!isset($where['role_id'])) { $where['role_id'] = array('in',implode(',', getSubRoleId())); } if(intval($_GET['type'])){ $where['category_id'] = intval($_GET['type']); }else{ $where['category_id'] = array('neq',1); } if ($_REQUEST["field"]) { $field = trim($_REQUEST['field']); $search = empty($_REQUEST['search']) ? '' : trim($_REQUEST['search']); $condition = empty($_REQUEST['condition']) ? 'eq' : trim($_REQUEST['condition']); if ('create_date' == $field || 'update_date' == $field) { $search = strtotime($search); } elseif ('role_id' == $field) { $condtion = "is"; } $params = array('field='.$_REQUEST['field'], 'condition='.$condition, 'search='.trim($_REQUEST["search"])); $field = trim($_REQUEST['field']) == 'all' ? 'subject|content' : $_REQUEST['field']; switch ($_REQUEST['condition']) { case "is" : $where[$field] = array('eq',$search);break; case "isnot" : $where[$field] = array('neq',$search);break; case "contains" : $where[$field] = array('like','%'.$search.'%');break; case "not_contain" : $where[$field] = array('notlike','%'.$search.'%');break; case "start_with" : $where[$field] = array('like',$search.'%');break; case "end_with" : $where[$field] = array('like','%'.$search);break; case "is_empty" : $where[$field] = array('eq','');break; case "is_not_empty" : $where[$field] = array('neq','');break; case "gt" : $where[$field] = array('gt',$search);break; case "egt" : $where[$field] = array('egt',$search);break; case "lt" : $where[$field] = array('lt',$search);break; case "elt" : $where[$field] = array('elt',$search);break; case "eq" : $where[$field] = array('eq',$search);break; case "neq" : $where[$field] = array('neq',$search);break; case "between" : $where[$field] = array('between',array($search-1,$search+86400));break; case "nbetween" : $where[$field] = array('not between',array($search,$search+86399));break; case "tgt" : $where[$field] = array('gt',$search+86400);break; default : $where[$field] = array('eq',$search); } } $p = isset($_GET['p']) ? intval($_GET['p']) : 1 ; $list = $m_log->where($where)->page($p.',10')->order($order)->select(); $count = $m_log->where($where)->count(); import("@.ORG.Page"); $Page = new Page($count,10); if (!empty($_REQUEST['by'])){ $params['by'] = 'by=' . trim($_REQUEST['by']); } if (!empty($_REQUEST['r']) && !empty($_REQUEST['module'])) { $params['r'] = 'r=' . trim($_REQUEST['r']); $params['module'] = 'module=' . trim($_REQUEST['module']); } if (!empty($_REQUEST['type'])) { $params['type'] = 'type=' . trim($_REQUEST['type']); } $this->parameter = implode('&', $params); if ($_GET['desc_order']) { $params[] = "desc_order=" . trim($_GET['desc_order']); } elseif($_GET['asc_order']){ $params[] = "asc_order=" . trim($_GET['asc_order']); } $Page->parameter = implode('&', $params); $show = $Page->show(); $this->assign('page',$show); foreach($list as $key=>$value){ $list[$key]['creator'] = getUserByRoleId($value['role_id']); if($m_comment->where("module='log' and module_id=%d", $value['log_id'])->select()){ $list[$key]['is_comment'] = 1; } } $this->category_list = M('LogCategory')->order('order_id')->select(); //获取下级和自己的岗位列表,搜索用 $d_role_view = D('RoleView'); $this->role_list = $d_role_view->where('role.role_id in (%s)', implode(',', $below_ids))->select(); $this->assign('list',$list); $this->alert = parseAlert(); $this->display(); }
首先要说下此为需要登录后的权限才能访问的页面,然后则是查询咱们log表中的内容,以正序的方式查询。最后用assign直接输出到了当前模板中0.0 我这里就简单的说明了。函数内容太多!!!一个个的说挺费劲的。说的不对希望指出
验证漏洞:
这里还感谢90群里面的roisatm表哥提供了一种绕过悟空CRM的webscan方法
未登录下可访问的页面
填入咱们的测试XSS
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgiSm9zZXBoIik7PC9zY3JpcHQ+="> </object>
没有拦截,只有顺畅的感觉就是棒棒哒
现在登录去后台看下
【via@91ri-小续】