无列名bypass
bypass information_schema
一般而言在常规的sql注入中,我们在查询表名时,都会使用information_schema这个库,但是waf都会把这个给禁了,所以我们要尝试其他方法(看了大佬的wp,进行总结)
bypass之前先了解下information_schema这个库的作用:
information_schema简单来说无非就是获取到table_schema,table_name,column_name
1 | 简单来说,这个库在mysql中就是个信息数据库,它保存着mysql服务器所维护的所有其他数据库的信息,包括了数据库名,表名,字段名等。 |
Mysql5.7的新特性
1 | 由于performance_schema过于发杂,所以mysql在5.7版本中新增了sys schemma,基础数据来自于performance_chema和information_schema两个库,本身数据库不存储数据。 |
sys.schema_auto_increment_columns
可以知道的是,我们在设计数据库时,比如说id这样的数据都是呈自增的的数字,那么我们可以发现一个注入中在mysql默认情况下可以替代information_schema库的方法
1 | chema_auto_increment_columns,该视图的作用简单来说就是用来对表自增ID的监控。 |
比如说建立了security和fortest这两个库,来熟悉下shcema_auto_increment_columns的结构组成还有特性
# fortest库
data 表存在自增id
no_a_i_table 表不存在自增id
test 表存在自增id
# security库
emails,referers,uagents,users
可以发现,fortest库中的no_a_i_table并不在这里存在,然而其他非系统库的表信息全部在这里。根据前面介绍的schema_auto_increment_columns视图的作用,也可以发现我们可以通过该视图获取数据库的表名信息,也就是说找到了一种可以替代information_schema在注入中的作用的方法
那如果没有设置自增的数据呢?翻阅文档可以发现另外一个库
schema_table_statistics_with_buffer,x$schema_table_statistics_with_buffer
1 | 查询表的统计信息,其中还包括InnoDB缓冲池统计信息,默认情况下按照增删改查操作的总表I/O延迟时间(执行时间,即也可以理解为是存在最多表I/O争用的表)降序排序,数据来源:performance_schema.table_io_waits_summary_by_table、sys.x$ps_schema_table_statistics_io、sys.x$innodb_buffer_stats_by_table |
sys.schema_table_statistics_with_buffer
可以看到,在上一个视图中并没有出现的表名在这里出现了。
# sys.x$schema_table_statistics_with_buffer.
上面的库都可以让我们获取数据库中表名信息,但并没有找到类似information_schema中的columns,也就是目前我们并不能获取数据
join
常见ctf思路,利用join进行无列名注入
join…using(xx)
以上文的环境为例,这里waf会把information_schema完全过滤
1 | # schema_auto_increment_columns |
其他的都是类似的
获取列名
1 | # 获取第一列的列名 |
无列名注入
顾名思义就是在不知道列名的情况下,进行sql注入
在 mysql => 5 的版本中存在一个名为 information_schema 的库,里面记录着 mysql 中所有表的结构。通常,在 mysql sqli 中,我们会通过此库中的表去获取其他表的结构,也就是表名、列名等。但是这个库经常被 WAF 过滤
假设我们通过暴力破解或者其他手段得到了表名,但是没有列名
1 | 在 information_schema 中,除了 SCHEMATA、TABLES、COLUMNS 有表信息外,高版本的 mysql 中,还有 INNODB_TABLES 及 INNODB_COLUMNS 中记录着表结构。 |
使用条件&方法
无列名注入主要是适用于已经获取到数据表,但无法查询列的情况下,在大多数 CTF 题目中,information_schema 库被过滤,使用这种方法获取列名。
无列名注入的原理其实很简单,类似于将我们不知道的列名进行取别名操作,在取别名的同时进行数据查询,所以,如果我们查询的字段多于数据表中列的时候,就会出现报错。
不使用表名查询
正常的SQL查询:
1 | select * from `admin`; |
其中列名为id,name,password,使用union查询
1 | select 1,2,3 union select * from admin; |
如图,我们的列名被替换为了对应的数字。也就是说,我们可以继续数字来对应列,如 3 对应了表里面的 password:
1 | select `3` from (select 1,2,3 union select * from admin)a; |
末尾的 a 可以是任意字符,用于命名。
当然,多数情况下,会被过滤。当
不能使用的时候,使用别名来代替
1 | select b from (select 1,2,3 as b union select * from admin)a; |
同时查询多个列:
1 | select concat(`2`,0x2d,`3`) from (select 1,2,3 union select * from admin)a limit 1,3; |
简而言之,可以通过任意命名进入该表,然后使用 SELECT 查询这些字段中的任何已知值