暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

代码审计之SQL注入

编码安全研究 2021-10-15
532

SQL注入漏洞的原理非常简单,由于开发者在编写操作数据库代码时,直接将外部可控的参数拼接到SQL语句中,没有经过任何过滤就直接放入数据库引擎执行。

由于SQL注入是直接面对数据库进行攻击的,所以它的危害不言而喻,通常利用SQL注入的攻击方式有下面几种:一是在权限较大的情况下,通过SQL注入可以直接写入webshell,或者直接执行系统命令等。二是在权限较小的情况下,也可以通过注入来获得管理员的密码等信息,或者修改数据库内容进行一些钓鱼或者其他间接利用。

针对SQL注入漏洞的利用工具也是越来越智能,sqlmap是目前被使用最多的注入工具,这是一款国外开源的跨平台SQL注入工具,用Python开发,支持多种方式以及几乎所有类型的数据库注入,对SQL注入漏洞的兼容性也非常强。

SQL注入经常出现在登录页面、获取HTTP头(user-agent/client-ip等)、订单处理等地方,因为这几个地方是业务相对复杂的,登录页面的注入现在来说大多是发生在HTTP头里面的client-ip和x-forward-for,一般用来记录登录的IP地址,另外在订单系统里面,由于订单涉及购物车等多个交互,所以经常会发生二次注入。我们在通读代码挖掘漏洞的时候可以着重关注这几个地方。

SQL注入有多种的利用方式,如普通注入、盲注、报错注入、宽字节注入、二次注入等,但是它们的原理都是大同小异的,下面笔者会介绍怎么挖掘到这些注入漏洞。


1. 普通注入

这里说的普通注入是指最容易利用的SQL注入漏洞,比如直接通过注入union查询就可以查询数据库,一般的SQL注入工具也能够非常好地利用。普通注入有int型和string型,在string型注入中需要使用单或双引号闭合,下面简单演示普通注入漏洞,后面所有测试SQL注入漏洞的数据表中数据都如图所示

测试代码如下:

    <?php
    $uid=$_GET['id'];
    $sql="SELECT * FROM userinfo where id=$uid";
    $conn=mysql_connect('localhost','root','123456');
    mysql_select_db("test",$conn);
    $result=mysql_query($sql, $conn);
    print_r('当前SQL语句:'.$sql.'<br >结果:');
    print_r(mysql_fetch_row($result));
    ?>

    测试代码中GET id参数存在SQL注入漏洞,测试方法如图所示

    从截图可以看到原本的SQL语句已被注入更改,使用了union查询到当前用户。

    从上面的测试代码中可以发现,数据库操作存在一些关键字,比如select from、mysql_connect、mysql_query、mysql_fetch_row等,数据库的查询方式还有update、insert、delete,我们在做白盒审计时,只需要查找这些关键字,即可定向挖掘SQL注入漏洞。

    2. 编码注入

    程序在进行一些操作之前经常会进行一些编码处理,而做编码处理的函数也是存在问题的,通过输入转码函数不兼容的特殊字符,可以导致输出的字符变成有害数据,在SQL注入里,最常见的编码注入是MySQL宽字节以及urldecode/rawurldecode函数导致的。

    • 宽字节注入

    在使用PHP连接MySQL的时候,当设置“set character_set_client=gbk”时会导致一个编码转换的注入问题,也就是我们所熟悉的宽字节注入,当存在宽字节注入漏洞时,注入参数里带入%df%27,即可把程序中过滤的\(%5c)吃掉。举个例子,假设/1.php?id=1里面的id参数存在宽字节注入漏洞,当提交/1.php?id=-1’and 1=1%23时,MySQL运行的SQL语句为select*from user where id=’1\’and 1=1#’很明显这是没有注入成功的,我们提交的单引号被转义导致没有闭合前面的单引号,但是我们提交/1.php?id=-1%df’and 1=1%23时,这时候MySQL运行的SQL语句为:

      select * from user where id='1運' and 1=1#'

      这是由于单引号被自动转义成\',前面的%df和转义字符\反斜杠(%5c)组合成了%df%5c,也就是“運”字,这时候单引号依然还在,于是成功闭合了前面的单引号。

      出现这个漏洞的原因是在PHP连接MySQL的时候执行了如下设置:

        set character_set_client=gbk

        告诉MySQL服务器客户端来源数据编码是GBK,然后MySQL服务器对查询语句进行GBK转码导致反斜杠\被%df吃掉,而一般都不是直接设置character_set_client=gbk,通常的设置方法是SET NAMES'gbk',但其实SET NAMES'gbk'不过是比character_set_client=gbk多干了两件事而已,SET NAMES'gbk'等同于如下代码:

        SET
        character_set_connection='gbk'
        character_set_results='gbk'
        character_set_client=gbk

        这同样也是存在漏洞的,另外官方建议使用mysql_set_charset方式来设置编码,不幸的是它也只是调用了SET NAMES,所以效果也是一样的。不过mysql_set_charset调用SET NAMES之后还记录了当前的编码,留着给后面mysql_real_escape_string处理字符串的时候使用,所以在后面只要合理地使用mysql_real_escape_string还是可以解决这个漏洞的,关于这个漏洞的解决方法推荐如下几种方法:

        1)在执行查询之前先执行SET NAMES'gbk',character_set_client=binary设置character_set_client为binary。

        2)使用mysql_set_charset('gbk')设置编码,然后使用mysql_real_escape_string()函数被参数过滤。

        3)使用pdo方式,在PHP5.3.6及以下版本需要设置setAttribute(PDO:ATTR_EMULATE_PREPARES,false);来禁用prepared statements的仿真效果。

        如上几种方法更推荐第一和第三种。

        下面对宽字节注入进行一个简单测试。

        测算代码如下:

          <?php
          $uid=addslashes($_GET['id']);
          $sql="SELECT * FROM userinfo where id=$uid";
          $conn=mysql_connect('localhost','root','123456');
          mysql_select_db("test",$conn);
          mysql_query("SET NAMES 'gbk'",$conn);
          $result=mysql_query($sql, $conn);
          print_r('当前SQL语句:'.$sql.'<br >结果:');
          print_r(mysql_fetch_row($result));
          mysql_close();
          ?>

          成功注入的效果如图所示。

          对宽字节注入的挖掘方法也比较简单,只要搜索如下几个关键字即可:

          SET NAMES
          character_set_client=gbk
          mysql_set_charset('gbk'

          • 二次urldecode注入

          只要字符被进行转换就有可能产生漏洞,现在的Web程序大多都会进行参数过滤,通常使用addslashes()、mysql_real_escape_string()、mysql_escape_string()函数或者开启GPC的方式来防止注入,也就是给单引号(')、双引号(")、反斜杠(\)和NULL加上反斜杠转义。如果某处使用了urldecode或者rawurldecode函数,则会导致二次解码生成单引号而引发注入。原理是我们提交参数到WebServer时,WebServer会自动解码一次,假设目标程序开启了GPC,我们提交/1.php?id=1%2527,因为我们提交的参数里面没有单引号,所以第一次解码后的结果是id=1%27,%25解码的结果是%,如果程序里面使用了urldecode或者rawurldecode函数来解码id参数,则解码后的结果是id=1’单引号成功出现引发注入。

          测试代码:

            <? php 
            $a=addslashes($_GET['p']);
            $b=urldecode($a);
            echo '$a='.$a;
            echo '<br />'
            echo '$b='.$b;
            ?>

            测试效果如图所示

            既然知道了原理主要是由于urldecode使用不当导致的,那我们就可以通过搜索urldecode和rawurldecode函数来挖掘二次urldecode注入漏洞

            • espcms搜索注入分析

            这里以一个笔者在2013年发现的一个小CMS程序espcms搜索注入的漏洞为例,我们目前尽量以相对好理解的漏洞来举例。

            漏洞在interface/search.php文件和interface/3gwap_search.php文件in_taglist()函数都存在,一样的问题,以interface/search.php为例说明:

            打开文件看到如下代码:

              function in_taglist() {
              parent:start_pagetemplate();
              include_once admin_ROOT .'public/class_pagebotton.php'
              $page = $this->fun->accept('page''G');
              $page = isset($page) ?intval($page) :1
              $page = isset($page) ?intval($page) :1
              $lng = (admin_LNG == 'big5') ?$this->CON['is_lancode'] :admin_LNG;
              $tagkey = urldecode($this->fun->accept('tagkey''R'));
              $takey = $this->fun->inputcodetrim($tagkey);
              $db_where = ' WHERE lng=\'' .$lng .'\' AND isclass=1'
              ifempty($tagkey)) {
              $linkURL = $_SERVER['HTTP_REFERER'];
              $this->callmessage($this->lng['search_err'], $linkURL, $this->lng ['gobackbotton']);
              }
              if (!empty($tagkey)) {
              $db_where.=" AND FIND_IN_SET('$tagkey',tags)"
              }

              其中:

              $tagkey = urldecode($this->fun->accept('tagkey' 'R'));  

              这行代码得到$_REQUEST['tagkey']的值,由于$tagkey变量使用了urldecode,从而可以绕过GPC:

              $db_where.=" AND FIND_IN_SET('$tagkey',tags)"

              经过判断$tagkey不为空则拼接到SQL语句中,导致产生注入漏洞。

                原文:https://zhuanlan.zhihu.com/p/261158169

                从代码层讲解漏洞原理,学习更多技术,关注我:

                觉得文章不错给点个‘再看’吧
                文章转载自编码安全研究,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论