帮助中心FAQ


information_schema数据库是MySQL自带的数据库,MySQL 5.x以下没有这个数据库,它提供了整个MySQL访问数据库元数据的方式,其中就包含MySQL里面所有的数据库名或表名,列的数据类型,或访问权限等。也就是说information_schema中保存着关于MySQL服务器所维护的所有其他数据库的信息。在INFORMATION_SCHEMA中,有数个只读表。

 

```

mysql> show databases;

+--------------------+

| Database           |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| security           |

| test               |

+--------------------+

 

mysql> use information_schema;

 

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                |

+---------------------------------------+

```

 

上面显示的表,实际上是视图,而不是基本表,在数据库的数据保存目录是看不到这个数据库的实体文件,而数据库的数据保存在MySQLdata目录,mysql、performance_schema、test、security

 

```

# 查看当前数据保存目录

mysql> select @@datadir;

+-------------------------------------+

| @@datadir                           |

+-------------------------------------+

| D:phpStudyPHPTutorialMySQLdata |

+-------------------------------------+

```

 

```

mysql> select schema_name from information_schema.schemata;

+--------------------+

| schema_name        |

+--------------------+

| information_schema |

| mysql              |

| performance_schema |

| security           |

| test               |

+--------------------+

```

 

可以看到他们的作用是一样的,都是列出所有数据库,跟我们前面说的一样,SCHEMA_NAME字段保存了所有的数据库名。

 

每一个目录对应数据库中的一个数据库,在数据库中执行show databases;的时候,可以看到存在5个数据库,正是少了information_schema这个数据库。

 

- 在SQL注入中,重点关注的表有如下几个,因为主要的时候主要利用这几个表来获取数据:

    - SCHEMATA:提供了当前mysql数据库中所有数据库的信息,其中SCHEMA_NAME字段保存了所有的数据库名。show databases的结果取自此表。

    - TABLES:提供了关于数据库中的表的信息,详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息,其中table_name字段保存了所有列名信息,show tables from schemaname的结果取自此表。

    - COLUMNS:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息,其中column_name保存了所有的字段信息。show columns from schemaname.tablename的结果取自此表。

 

```

mysql> desc tables;

+-----------------+---------------------+------+-----+---------+-------+

| Field           | Type                | Null | Key | Default | Extra |

+-----------------+---------------------+------+-----+---------+-------+

| TABLE_CATALOG   | varchar(512)        | NO   |     |         |       |

| TABLE_SCHEMA    | varchar(64)         | NO   |     |         |       |

| TABLE_NAME      | varchar(64)         | NO   |     |         |       |

| TABLE_TYPE      | varchar(64)         | NO   |     |         |       |

| ENGINE          | varchar(64)         | YES  |     | NULL    |       |

| VERSION         | bigint(21) unsigned | YES  |     | NULL    |       |

| ROW_FORMAT      | varchar(10)         | YES  |     | NULL    |       |

| TABLE_ROWS      | bigint(21) unsigned | YES  |     | NULL    |       |

| AVG_ROW_LENGTH  | bigint(21) unsigned | YES  |     | NULL    |       |

| DATA_LENGTH     | bigint(21) unsigned | YES  |     | NULL    |       |

| MAX_DATA_LENGTH | bigint(21) unsigned | YES  |     | NULL    |       |

| INDEX_LENGTH    | bigint(21) unsigned | YES  |     | NULL    |       |

| DATA_FREE       | bigint(21) unsigned | YES  |     | NULL    |       |

| AUTO_INCREMENT  | bigint(21) unsigned | YES  |     | NULL    |       |

| CREATE_TIME     | datetime            | YES  |     | NULL    |       |

| UPDATE_TIME     | datetime            | YES  |     | NULL    |       |

| CHECK_TIME      | datetime            | YES  |     | NULL    |       |

| TABLE_COLLATION | varchar(32)         | YES  |     | NULL    |       |

| CHECKSUM        | bigint(21) unsigned | YES  |     | NULL    |       |

| CREATE_OPTIONS  | varchar(255)        | YES  |     | NULL    |       |

| TABLE_COMMENT   | varchar(2048)       | NO   |     |         |       |

+-----------------+---------------------+------+-----+---------+-------+

```

 

分别记录一个表名和一个这个表所属的库名。其中TABLE_NAME保存的是表名,而TABLE_SCHEMA保存的是这个表名所在的数据库。我们可以查询一条记录看看,在查询前,先看看有多少条记录,避免记录太多查看不方便,执行select count(*) from tables;

 

```

mysql> select count(*) from tables;

+----------+

| count(*) |

+----------+

|       85 |

+----------+

```

 

说明当前所有数据库中的表数量为85。查询任意一条记录查看,这里选择最后一条记录,SQL语句为:select * from tables limit 141,1G由于在客户端中,默认查询结果显示不友好,所以,可以把语句后面的分号改成G,他会让一条记录显示一行,看起来不那么乱。G只支持在客户端中用,在其他连接数据库的软件中,使用G会报错。

 

```

mysql> select * from tables limit 84,1G

*************************** 1. row ***************************

  TABLE_CATALOG: def

   TABLE_SCHEMA: security

     TABLE_NAME: users

     TABLE_TYPE: BASE TABLE

         ENGINE: MyISAM

        VERSION: 10

     ROW_FORMAT: Dynamic

     TABLE_ROWS: 8

 AVG_ROW_LENGTH: 24

    DATA_LENGTH: 196

MAX_DATA_LENGTH: 281474976710655

   INDEX_LENGTH: 2048

      DATA_FREE: 0

 AUTO_INCREMENT: 9

    CREATE_TIME: 2020-08-21 02:13:29

    UPDATE_TIME: 2020-08-21 02:13:29

     CHECK_TIME: NULL

TABLE_COLLATION: utf8_general_ci

       CHECKSUM: NULL

 CREATE_OPTIONS:

  TABLE_COMMENT:

```

 

可以看到,最后一条记录的TABLE_NAME是users,TABLE_SCHEMA为security。查看security数据库中的表,SQL语句为:show tables from security;可以看到确实存在users表。

 

```

mysql> show tables from security;

+--------------------+

| Tables_in_security |

+--------------------+

| emails             |

| referers           |

| uagents            |

| users              |

+--------------------+

```

 

既然information_schema的TABLES表中的TABLE_SCHEMTA字段是保存的数据库名,而TABLE_NAME保存了表名,那么就可以使用TABLE_SCHEMTA字段作为查询条件,查询TABLE_NAME,即可得知所有指定数据库中的所有表名。

 

比如,我们想要通过information_schema数据库来查询security数据库中所有的表,那么就可以使用如下SQL语句:select table_name from information_schema.tables where table_schema ='security';

 

如果当前库为information_schema,则可以省略不写,否则跨库查询的时候,需要带上库名。

 

```

mysql> select table_name from information_schema.tables where table_schema ='security';

+------------+

| table_name |

+------------+

| emails     |

| referers   |

| uagents    |

| users      |

+------------+

```

 

通过修改 TABLE_SCHEMA 的限制,可以查询任意数据库中的所有表名,网上的通过注入爆表名便是这个原理。

 

知道了表名,那么如何获取表中的字段呢?要知道我们没有表名的话,会把所有的数据查询出来,而如果注入没有回显,不能进行union查询,那么想要获取我们的标目数据,无疑效率极低。

 

幸运的是,在information_schema数据库中,同样存在一个表,它保存了整个数据中,所有的列名,这个表就是COLUMNS。同样先查看该表结构。

 

```

mysql> desc columns;

+--------------------------+---------------------+------+-----+---------+-------+

| Field                    | Type                | Null | Key | Default | Extra |

+--------------------------+---------------------+------+-----+---------+-------+

| TABLE_CATALOG            | varchar(512)        | NO   |     |         |       |

| TABLE_SCHEMA             | varchar(64)         | NO   |     |         |       |

| TABLE_NAME               | varchar(64)         | NO   |     |         |       |

| COLUMN_NAME              | varchar(64)         | NO   |     |         |       |

| ORDINAL_POSITION         | bigint(21) unsigned | NO   |     | 0       |       |

| COLUMN_DEFAULT           | longtext            | YES  |     | NULL    |       |

| IS_NULLABLE              | varchar(3)          | NO   |     |         |       |

| DATA_TYPE                | varchar(64)         | NO   |     |         |       |

| CHARACTER_MAXIMUM_LENGTH | bigint(21) unsigned | YES  |     | NULL    |       |

| CHARACTER_OCTET_LENGTH   | bigint(21) unsigned | YES  |     | NULL    |       |

| NUMERIC_PRECISION        | bigint(21) unsigned | YES  |     | NULL    |       |

| NUMERIC_SCALE            | bigint(21) unsigned | YES  |     | NULL    |       |

| CHARACTER_SET_NAME       | varchar(32)         | YES  |     | NULL    |       |

| COLLATION_NAME           | varchar(32)         | YES  |     | NULL    |       |

| COLUMN_TYPE              | longtext            | NO   |     | NULL    |       |

| COLUMN_KEY               | varchar(3)          | NO   |     |         |       |

| EXTRA                    | varchar(27)         | NO   |     |         |       |

| PRIVILEGES               | varchar(80)         | NO   |     |         |       |

| COLUMN_COMMENT           | varchar(1024)       | NO   |     |         |       |

+--------------------------+---------------------+------+-----+---------+-------+

```

 

这里面,与注入相关的存在3个字段,分别是TABLE_SCHEMA、TABLE_NAME以及COLUMN_NAME,不难猜到,如果在该表中查询一条记录,TABLE_SCHEMA保存了这条记录保存的字段所属的数据库名,而TABLE_NAME保存的是该字段所属表名,COLUMN_NAME则是一个列名记录,查询一条记录验证一下,首先确定该表有多少条记录,执行select count(*) from columns;,得知一共有820条记录。

 

```

mysql> select count(*) from columns;

+----------+

| count(*) |

+----------+

|      820 |

+----------+

```

 

获取最后一条记录,执行select * from columns limit 819,1G

 

```

mysql> select * from columns limit 819,1G

*************************** 1. row ***************************

           TABLE_CATALOG: def

            TABLE_SCHEMA: security

              TABLE_NAME: users

             COLUMN_NAME: password

        ORDINAL_POSITION: 3

          COLUMN_DEFAULT: NULL

             IS_NULLABLE: NO

               DATA_TYPE: varchar

CHARACTER_MAXIMUM_LENGTH: 20

  CHARACTER_OCTET_LENGTH: 60

       NUMERIC_PRECISION: NULL

           NUMERIC_SCALE: NULL

      CHARACTER_SET_NAME: utf8

          COLLATION_NAME: utf8_general_ci

             COLUMN_TYPE: varchar(20)

              COLUMN_KEY:

                   EXTRA:

              PRIVILEGES: select,insert,update,references

          COLUMN_COMMENT:

```

 

其中COLUMNS_NAME为password,TABLE_NAME为users,TABLE_SCHEMA为security,这说明,在security这个数据中,users表存在一个password的列,也就是我们常说的password字段。

 

查看security的users表是否存在该字段,执行SQL语句:show columns from security.users;

 

```

mysql> show columns from security.users;

+----------+-------------+------+-----+---------+----------------+

| Field    | Type        | Null | Key | Default | Extra          |

+----------+-------------+------+-----+---------+----------------+

| id       | int(3)      | NO   | PRI | NULL    | auto_increment |

| username | varchar(20) | NO   |     | NULL    |                |

| password | varchar(20) | NO   |     | NULL    |                |

+----------+-------------+------+-----+---------+----------------+

```

 

可以看到确实存在该字段。

 

既然在columns中,TABLE_NAME保存了字段所属的表名,TABLE_SCHEMA保存了该字段所属的库名,与通过TABLES表获取表名一样,我们就可以查询把TABLE_NAME 和TABLE_SCHEMA做为查询条件,查询符合条件的COLUMN_NAME,也就是查询指定数据库中某表中的字段。

 

比如,我们要通过information_schema数据库的columns表查询security数据库中user表中所有的字段,可以执行如下SQL语句:select column_name from information_schema.columns where table_schema='security' and table_name='users';

 

```

mysql> select column_name from information_schema.columns where table_schema='security' and table_name='users';

+-------------+

| column_name |

+-------------+

| id          |

| username    |

| password    |

+-------------+

```

 

查询结果与show columns from security.users; 一致。

 

```

mysql> show columns from security.users;

+----------+-------------+------+-----+---------+----------------+

| Field    | Type        | Null | Key | Default | Extra          |

+----------+-------------+------+-----+---------+----------------+

| id       | int(3)      | NO   | PRI | NULL    | auto_increment |

| username | varchar(20) | NO   |     | NULL    |                |

| password | varchar(20) | NO   |     | NULL    |                |

+----------+-------------+------+-----+---------+----------------+

```

 

知道了库名、表名、字段,如果有回显且支持联合查询,就可以直接通过在注入点后面注入一个联合查询语句,即可直接获取数据,如果不能回显,则可能需要通过盲注获取数据。