SQL注入详解

本文将从5个方面叙述SQL注入的一些问题,漏洞原理,测试,利用,危害,修复。接着介绍一些SQL注入的技巧东西。SQL注入一般被分为5种类型,基于布尔的盲注,基于时间的盲注,基于报错的注入,联合查询注入,堆查询注入。

类型

描述

基于布尔盲注

即可以根据返回页面判断条件真假的注入

基于时间盲注

不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断。

基于报错注入

即页面会返回错误信息,或者把注入的语句的结果直接返回在页面中

联合查询注入

可以使用union的情况下的注入

堆查询注入

可以同时执行多条语句的执行时的注入

 

  • 漏洞原理

    注入类漏洞是OWASP TOP 10常客,SQL注入占比应该很高。顺带科普一下注入类漏洞,包括SQL,OS,LDAP注入。

    SQL注入是发生在应用程序数据库层面的漏洞,当不可信的数据作为查询语句的一部分,发给解析器的时候,在设计不良得应用程序当中忽略检查,攻击者就可以欺骗解析器,执行计划外的命令。

案例:

String query = "SELECT * FROM accounts WHERE custID='"+request.getParameter("id")+"'" ;    当攻击者传进来的id参数为' or '1'='1 时候

查询语句拼接成 SELECT * FROM accounts WHERE custID=' ' or '1'='1' ;

这个查询语句送到解析器里,解析执行后返回的结果为accounts表所有记录。更危险的攻击可能导致数据被篡改甚至存储过程被调用。

  • 漏洞测试

    漏洞测试可分为手工测试和自动化测试

    自动化测试:一般的扫描器例如AppScan,AWVS等都可以进行测试SQL漏洞的存在,Sqlmap,穿山甲,阿D等可以检测是否为注入点,是注入点可以进一步利用。

    手动测试:首先,需要猜测该SQL语句使用什么闭合,例如单引号,或者双引号,或者没有引号的数字型,如上一例的案例就是使用单引号闭合的。

    有回显注入无回显盲注的手工测试方法有所不同,下面先介绍有回显注入的测试方法。

    1.猜测闭合:假如有如下SQL语句

  1. String query1 = "SELECT * FROM accounts WHERE custID='"+request.getParameter("id")+"'" ;
  2. String query2 = 'SELECT * FROM accounts WHERE custID="'+request.getParameter("id")+'"';
  3. String query3= "SELECT * FROM accounts WHERE custID="+request.getParameter("id");

第一条为单引号闭合,第二条为双引号闭合,第三条为数字型,在事先不知道源代码的情况下,我们3种情况都尝试一遍,就可以确定是那种符号闭合。

一般情况下,我们测试一个注入点,在正常数据后添加一个单引号,,或者双引号报错,一般都存在SQL注入漏洞

三种POC:

1' or '1'='1

1" or "1"="1

1 or 1=1

根据回显来判断注入的语句是否成功执行

Mysql中的三种注释:# -- /**/,在SQL注入中一般使用注释符号注释掉后面内容,这个在后面会详说

2.确定返回字段数

我们一般使用order by语句确定字段数,例如accounts表有三个字段,分别为username,password,last_login, SELECT * FROM accounts WHERE custID=' ' or '1'='1' ; 这个查询语句会返回表中所有字段,即3个字段

当我们在poc里添加order by 1-- ,整个查询语句就会变为SELECT * FROM accounts WHERE custID=' ' or '1'='1' order by 1 -- (被注释掉的语句部分) 数据库解析执行,就会返回第一列的默认排序的序列,依次类推,数字为4的时候,因为没有第4个字段

所以这个查询语句非法,就会报错,我们可以利用这个分水岭确定Web应用程序查询该参数返回的字段数,字段数过多可以使用2分发来确定字段数目。

3.确定回显点

一般我们使用union select 特殊标识字符来确定会显点,特殊字符在页面显示的地方就是注入的回显点。

  • 漏洞利用

    4.查询数据库信息

    在我们确定·返回字段数,回显点之后,我们就可以进行查询数据库的信息,例如select @@version ,@@datadir ,user() ,database() 等,@@version, @@basedir, @@datadir 是 3 个内置变量,其中 @@version 用来获取 MySQL 的版本号;@@basedir 用来获取 MySQL 的安装路径,@@datadir 用来获取 MySQL 的数据路径。

    在Mysql 5.0之后,有个 information_schama的数据库,information_schema数据库是MySQL自带的,它提供了访问数据库元数据的方式。什么是元数据呢?元数据是关于数据的数据,如数据库名或表名,列的数据类型,或访问权限等。有些时候用于表述该信息的其他术语包括"数据词典"和"系统目录"。
    在MySQL中,把 information_schema 看作是一个数据库,确切说是信息数据库。其中保存着关于MySQL服务器所维护的所有其他数据库的信息。如数据库名,数据库的表,表栏的数据类型与访问权 限等。在INFORMATION_SCHEMA中,有数个只读表。它们实际上是视图,而不是基本表,因此,你将无法看到与之相关的任何文件。通过查询这个数据库,可以得到很多信息,这相对于Mysql5.0以前方便很多,低于5.0的版本的Mysql数据库,表名,库名都需要暴力跑字典跑出来。

    还可以通过文件读取 load_file()命令读取服务器的文件。

    当你拥有root权限,且知道物理路径(通常通过引发web应用程序异常获得)可以进行写webshell,通过 select … into outfile写入webshell。

    没有回显的注入如何手工进行呢?下面介绍基于时间,布尔的两种类型的盲注。

    Web应用程序关闭了错误回显,这时候可以进行盲注,通过构造简单的条件语句,根据返回页面是否发生变化,来判断SQL语句是否得到执行

    http://xxx.com/items.php?id=2 and 1=2     实际执行的SQL语句变为:SELECT title,desc,body from items where ID=2 and 1=2

    因为1=2永远为假,所以不会有结果返回给用户,为了进一步确认是否存在注入,该poc为2 and 1=1,这时若页面正常返回,证明sql语句的and成功执行,参数id存在SQL注入漏洞。

    其余利用流程和有回显注入的流程一样,我们要解决的主要问题是如何通过真假,或者时间的长短来判断每一个字符,这就对应布尔盲注和时间盲注。

    基于布尔盲注:

    1.确定字符串长度

    首先我们要确定字符串长度,逐一读取出来,使用and length('test') >1 的poc,结合二分法 ,几个步骤就可以确定字符串的长度

    2.拆解字符串

    substr(expr,start,length)方法可以拆解字符串,进行一个一个比对,更方便的可以把字符转为ascii 码 例如ascii(substr(database(),1,1)) 使用二分法确定字符

    基于延时注入

    If(expr1,expr2,expr3)函数可以进行条件判断,当exper1为真时候,返回expr2,否则返回expr3.

    Sleep(sec)可以休眠sec秒。

    sleep(if(length(database())=5,5,0))当database名字的长度等于5的时候,休眠5秒,这时我们可以通过页面返回的时间相对大小来判断是否成功执行语句。

    还可以·使用 benchmark(count,expr)重复执行多次函数来达到延时效果,和sleep使用过程一样。

    但是在sqlmap中,注入使用的函数有所不一样,但是思路基本是一样的

    Sqlmap中使用cast(expression as data_type)转换数据类型,例如Cast(database() as char),把database()结果转换为字符串,其实是一样的转换,可能sqlmap需要适应不同的使用环境,提高兼容性的方法。

    Sqlmap使用 ifnull(null,str) 判断字符是否为空。

    Sqlmap使用mid()拆分字符串,和substr()是一样的。

    Sqlmap中使用ord()来转换字符到ascii,ord函数和ascii在单字节中是一样的,双字节不一样。

    可以看出,sqlmap自动化利用思路是基本一致的,确定长度,拆分字符,利用布尔,或者时间不同,猜解每个字符串。

  • 漏洞危害

    在上述的描述中,我们可以指定,sql注入漏洞危害很大,可以读取数据库的内容,还可以读取服务器的文件,还可以写入webshell,通过udf可以执行系统命令,在mysql为4版本时候,若运行mysql用户为root,可以直接获取root权限(在本地文件系统中导入一个共享库文件作为自定义函数)在5版本之后,因自定义创建函数的过程不符合新规范,后来又找到另外的方法,通过lib_mysqludf_sys提供的几个函数执行系统命令,主要是sys_eval和sys_exec函数执行系统命令,sqlmap集成该功能—os-cmd/--os-shell

  • 漏洞修复

    最好的方法就是预编译语句,绑定变量,检查数据类型,使用安全函数。

  • 技巧

    除了上述的UDF执行命令外,还可以攻击存储过程。

    存储过程必须使用CALL 或者 EXECUTE执行,在MSSQL和Oracle中,内置大量存储过程,例如xp_cmdshell可以执行系统命令,还要很多其余的可以操作注册表的存储过程。

    编码问题(宽字节注入)

    例如在PHP中,使用addslashes()函数在某些字符前加一个转义字符'\',addslashe会转义4个字符,单引号,双引号,NUL,\。

    当Mysql使用GBK编码时候,插入的0xbf会和前面的0x27(\)被认为是一个双字节字符,就出现漏洞。

    要解决这种问题,需要同一数据库,操作系统,Web应用所使用的字符集,以避免各层对字符的理解存在差异

    Sql Column Truncation(截断问题)

    Mysql的sql_mode设置为default,即没有开启STRICT_ALL_TABLES选项时候,Mysql对于超长值只会提示warning,不是error,会出现超长字符截断,在数据表中出现数据重复,在不同业务场景可能会导致不同的逻辑问题。

posted @ 2017-10-03 22:17  Pdoge  阅读(718)  评论(0)    收藏  举报