【SQL注入】SQL注入知识总结v1.0

2019-10-08 15:44:54 浏览数 (1)

Hello,各位小伙伴周末晚上好~

话说最近有小伙伴在公众号留言问我:

“小编,怎么盗QQ号?”

“小编,在某某网站用什么代码就可以免费充值?”

“小编,能不能帮我攻击一下某某网站?”

“小编,出来挨打!”

First of all,你的目标得有可以利用的漏洞才行,不存在什么万能代码的...

第二,第二步之后的所有行为都是违反国家网络安全法的!!!

我们要做遵纪守法,维护国家网络安全的白帽子~

而不是搞黑产的黑客~~

好啦,言归正传,其实早就写完了这篇SQL注入总结,但一直没有发布。

因为SQL注入可以写的东西实在太多了,最后决定还是先发出来,后期继续丰富吧。

Part.0

目录

目录

一、SQL概述

  1. 什么是SQL注入?

二、SQL注入点的类型

  1. 注入点分类

三、常见的注入方式

  1. 报错注入
  2. 联合查询注入
  3. 盲注
  4. 时间盲注
  5. 存储过程

四、存在SQL注入的点

  1. GET
  2. POST
  3. HTTP 文件头注入

五、一些WAF的绕过方法

六、SQL注入的防护

  1. SQL注入的危害
  2. 使用预编译语句
  3. 对输入进行严格的过滤
  4. 最小权限原则

Part.1

SQL概述

什么是SQL注入?

利用web应用程序对用户输入验证上的疏忽,攻击者在输入的数据中包含对某些数据库系统有特殊意义的符号或命令。

通过将这些恶意命令拼接到正常的SQL执行语句中一并执行,达到对后台数据库系统直接下达命令的攻击方式,称为SQL注入。

为什么可以把构造的SQL命令插入到正常的SQL执行语句中一并执行呢?

SQL查询支持and、or、union等多种查询方法,攻击者可以通过这些方法,将恶意执行语句拼接到正常的查询语句中去。

例如:

http://x.x.x.x/dyshow.php?dyid=42 union select 1,version(),user(),4

通过union拼接恶意查询语句,查出了数据库版本,和当前管理员账号:

因此,SQL注入攻击的本质,其实就是把用户输入的数据当作代码执行了。

注入攻击需要满足两个条件:

  • 用户能够控制输入。
  • 原本程序要执行的代码,拼接了用户输入的恶意数据。

Part.2

SQL注入点的类型

注入点分类

根据后端服务器传递到SQL数据库的查询语句的不同,我们可以分为两类:

1、数字型注入

SELECT name FROM users WHERE user_id = $id

数字型注入,直接在后面拼接我们构造的SQL语句即可。

2、字符型注入

SELECT name FROM users WHERE user_id = '$id'

字符型注入,如果直接拼接SQL语句,拼接的语句并不会被执行,也不会报错,需要将我们构造的语句从' ' 中逃逸出来才行。

比如下面这个例子:

//and 1=2 没有生效,如果生效应该什么都查不出来才对。

逃逸方法:

如果传入的参数是被' '引起来的,我们可以传入id = 1' and 1=2 # ,即:

SELECT name FROM users WHERE user_id = '1' and 1=2 # '

//第一个' 使 and 1=2 逃逸出来,#注释掉后面多余的'号。

//注释符也可以用--空格,但在URL中输入时,如果在最后加上-- ,浏览器在发送请求的时候会把URL末尾的空格舍去,所以我们用-- 代替-- ,原因是 在URL被URL编码后会变成空格。

Part.3

常见的注入方法

报错注入

报错注入:

  • 给参数赋值为and 1=1 或and 1=2,根据页面显示判断该节点是否为注入点
  • 给参数赋值单引号,根据报错类型判断为数字型还是字符型注入点
  • 通过order by 语句,根据报错判断当前列表的字段数等等

联合查询注入

联合查询注入:

  • 通过union select 1,2,3,4 语句,查看可显示的字段位置
  • 通过union select 1,version(),database(),4 ,可查询数据库敏感信息
  • 通过union select 1,username,passwd,4 from users ,可爆出其他表单的字段内容。

报错注入 联合注入就可以爆出一些简单的数据库的字段内容了,具体实战内容请参考之前的文章:

【SQL注入】通过实战教你手工注入MySql数据库

盲注

很多时候,Web服务器不显示数据库中查询的内容,只反馈对错,此时就可以根据逻辑判断是否正确利用盲注来获取信息。

以sqli-labs的第5关为例:

http://192.168.211.174/sqli-labs-master/Less-5/?id=11

注入点为?id,当我们输入可查询到的id时,页面只显示you are in ... ,不显示查询到的内容,如下:

当我们输入一个查询不到的id时,页面什么都不显示,例如输入:

http://192.168.211.174/sqli-labs-master/Less-5/?id=110000

我们来看看网站源码:

可以看出正确查询时,只返回 You are in...

该页面不存在显示点,我们只能采用盲注的方法进行猜测。

输入http://192.168.211.174/sqli-labs-master/Less-5/?id=1'

语法报错如下:

输入:http://192.168.211.174/sqli-labs-master/Less-5/?id=1' and 1=1 --

语法上显示正常,如下:

现在知道注入格式了,但是union联合查询在这里肯定是不适用的,因为当查询到数据时,该页面只会显示一个You are in ...

此时,就需要我们用到盲注了,这里介绍几个盲注可以用到的函数。

(1)利用 left(str,length) 函数获取信息 这是一个字符串函数,它返回具有指定长度的字符串的左边部分。

例如猜数据库版本第一个字符是不是5:http://192.168.211.174/sqli-labs-master/Less-5/?id=1' and left(version(),1)=5 --

说明是5.X版本,可以利用information_schema数据库。

(2)利用length函数判断数据库等的长度

一个汉字是算三个字符,一个数字或字母算一个字符。

http://192.168.211.174/sqli-labs-master/Less-5/?id=1' and length(database())=8 --

可以看出数据库名长度为8,这时我们再来一个字符,一个字符进行猜解。

如:http://192.168.211.174/sqli-labs-master/Less-5/?id=1' and left(database(),1)='s' --

如果显示 you are in ,则说明第一个字符是s,接着猜第二个字符,直到猜出完整数据库名为止。

(3)利用substr或substring函数获取信息

  • substr(str,pos,len):表示从pos开始的位置,截取len个字符(空白也算字符)。
  • ascii():可以计算字符的ascii值。

如果输入 >100 显示you are in...,输入>101则什么都不显示,则表示第一个字符ASCII值为101,即英文字母 e。

通过以上盲注方法,我们就可以逐步猜解出数据库名等一系列我们需要的敏感信息,但是通过手工注入,效率也是非常繁琐的,此时就需要配合我们的自动化工具来帮助我们完成这个过程了,例如sqlmap。

输入:root@kali:~# sqlmap -u "http://192.168.211.174/sqli-labs-master/Less-5/?id=1' 即可。

间盲注

但有时候,Web服务器不但不显示数据库中查询的内容,甚至连错误回显都关闭了,无论我们输入的数据能否在数据库中查询到,页面都不返回任何信息。

这个时候就需要用到时间盲注了,时间盲注可以利用sleep()函数或者benchmark() 函数。

sleep()函数:

构造注入语句:id=1'and if(ascii(substr(database(),1,1))=115,1,sleep(5))--

当查询失败时会有5秒的时间延迟

benchmark() 函数:

id=1' union select (if(substring(current,1,1)=char(115),benchmark(50000000,encode('MSG','by 5 seconds')),null)),2,3 from (select database() as current) as tb1--

当结果正确的时候,运行encode('MSG',‘by 5 second’)操作500000000次,会占用一定的时间,以此来判断查询是否正确。

同样,使用sqlmap可以快速完成注入。

存储过程

在MS SQL Server和Oracle数据库中,有着大量内置的存储过程。存储过程为数据库提供了强大的功能,存储过程需要使用CALL或者EXEC来执行。

存储过程也就是SQLServer为了实现特定任务,而将一些需要多次调用的固定操作语句编写成程序段。

例如在SQL Server中,我们可以利用xp_cmdshell执行系统命令:

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

Part.4

存在SQL注入的点

GET

SQL注入常见的注入点就是GET,我们通过输入的URL进行注入。例如:

前端代码通过GET方法,将username&password传入到后端服务器:

后端服务器再通过GET方法接收数据,并调用到sql查询语句中去。

//该代码并未对输入的内容做任何转义以及过滤

POST

POST方法不同于GET方法,传输的内容并不会在URL中进行显示:

前端使用POST方法,代码如下:

后端使用$_POST进行数据接收:

抓包可发现传递的参数在HTTP请求的正文中:

HTTP 文件头注入

除了用户主动提交的数据以外,有时候,服务器会读取HTTP头部的中的信息存入到数据库中去。

常见的HTTP文件头注入点如下:

  • User-Agent:使得服务器能够识别客户使用的操作系统,游览器版本等.(很多数据量大的网站中会记录客户使用的操作系统或浏览器版本等存入数据库中)
  • Cookie:网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密).
  • X-Forwarded-For:简称XFF头,它代表客户端,也就是HTTP的请求端真实的IP,(通常一些网站的防注入功能会记录请求端真实IP地址并写入数据库or某文件[通过修改XXF头可以实现伪造IP]).
  • Rerferer:浏览器向 WEB 服务器表明自己是从哪个页面链接过来的.
  • Host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号

例如,PHP后端会使用$_SERVER[‘HTTP_X_FORWARDED_FOR’]来获取HTTP头部中的XFF头,使用$_SERVER['HTTP_HOST']来获取Host头,如果没有对这些输入点进行过滤就存入数据库中,就会存在SQL注入风险。

Part.5

一些WAF的绕过方法

WAF绕过

1、大小写绕过

例如WAF拦截了union,可以使用大写UNION的方式。

2、编码绕过

如果大小写都过滤了,可以采用编码的方式进行绕过。

例如用16进制代替U,就是Union,当然还有char(ASCII码)等多种方式来替换字符。

3、注释符绕过

(1)WAF过滤了一次危险语句的情况

例如:?id=1 union select 1,2,3

可以写成

(2)绕过空格过滤

使用/**/代替空格:

4、分隔与重写绕过

在WAF采用了正则表达式的情况下,使用注释符充当分隔符,例如:

同样是在WAF只过滤了一次的情况下,我们可以使用重写来进行绕过。

例如:?id=1 ununionion selselectect 1,2,3

5、宽字节绕过

在注入攻击中,我们常常会使用单引号',或者双引号"进行注入点判断。

因此,开发者为了安全,一般会使用转义字符 对我们输入的特殊字符进行转义。转义后的特殊字符,就变成了我们查询的内容,失去了他们原本的作用。

但是,当Mysql数据库使用了GBK编码时,会认为两个字符为一个汉字,例如�就是一个汉字(前一个ascii码大于128才能到汉字的范围)。

因此经过转义后,�(为 )就会变成一个汉字,把转义符吃掉。

我们在构造注入语句时,需要写成:

id=-1�'union select 1,user(),3 --

防护方法:统一数据库、操作系统、Web应用所使用的字符集,以避免各层对字符的理解存在差异,例如都设置为utf-8。

Part.6

SQL注入的防护

SQL注入的危害

SQL注入漏洞存在的原因有两个:

  • 用户能够控制输入。
  • 原本程序要执行的代码,拼接了用户输入的恶意数据

SQL注入带来的危害:

  • 绕过登陆验证:使用万能密码登陆网站后台
  • 获取敏感信息:获取网站管理员账号、密码
  • 文件系统操作:完成读取、写入文件操作
  • 注册表操作:读取、写入、删除注册表
  • 执行系统命令:远程执行命令

(此处准备再开一篇文章通过案例进行说明,先挖个坑~~)

我们来看看SQL注入的防护方式有哪些。

使用预编译语句

使用预编译语句后,SQL语句的语义不会发生改变,我们直接来看一个例子:

我们来分析一下代码:

第一行使用?表示变量,我们可以将?替换为整型,字符串,双精度浮点型和布尔值。

第三行绑定了 SQL 的参数,且告诉数据库参数的值。其中 “sss” 参数列处理其余参数的数据类型,告诉数据库后面三个参数的类型为字符串。

通过这种方法,SQL语句的结构已经被固定,即使攻击者输入?firstname=li' and '1'='1这样的字符串,也只会被当作firstname整体来进行查询。

对输入进行严格的过滤

(1)检查数据的类型

在将变量代入到sql语句之前,先检查变量的数据类型是否正确。

例如输入?id=1,我们可以使用is_numeric($id)检查$id的值是不是一个数字,不是的话则报错。

又比如输入的是时间、日期时,也必须严格按照时间、日期的格式进行输入。

(2)使用安全函数

如果用户提交的时字符串,那么检查数据类型的方法可能就不好用了。此时我们需要配合一些安全函数,对输入的字符进行过滤。

例如,使用mysqlrealescapestring()、addslashes()、strreplace()等函数,对敏感字符进行过滤。

最小权限原则

从数据库的角度来说,应该避免web应用直接使用root等最高权限直接连接数据库。

web应用使用的数据库账户,也不应该有创建自定义函数、操作本地文件的权限。

Part.7

结束语

好啦,这就是今天的全部内容了。

再次强调大家不可以干违法乱纪的事哦~

Peace !

0 人点赞