第5关-------------------------------------------->
前端直接不会显示账号密码的打印;但是在接收前端的数据的那部分后端那里,会看前端传递过来的值是否正确,如果不正确,后端接收值那里就会当MySQL语句执行错误,直接打印MySQL的报错信息
怎样解决第二关:通过这一个打印错误信息的漏洞,再让updatexml()方法去真正执行MySQL语句,肯定会出现报错情况,出现了就会打印报错信息;
前面的第一关注入是在web页面会打印当前登录的账号,最后如果我们知道了表名和对应表的字段名的话,我们就可以通过group_concat(username),group_concat(password)这种将所有的username账号和password密码打印出来了;
但是要是前端web页面并不会显示你的账号密码,前端压根就不会答应你的账号密码怎么办?
拓展:
1、group_concat()和concat()的区别
---1、group_concat()函数是将同一个组内的所有指定的值拼接起来;
如:group_concat(id,':',name);
就是将整个表中的所有id和所有name都拼接起来
是一行一行拼接的,拼接的内容是将第一行的id值和‘:’和name值都拼接起来,
然后再将第二行的id值和‘:’和name值都拼接起来,
...最后整个表中的数据都拼接完成后再一次性打印在一行里面
---2、concat()
concat就是将每行记录的都分别拼接起来,分别打印在每一行
如:select concat(id,':',username) from users ;
---所以concat是拼接出来的有多个成员行数或者所成员个数不变,但是group_concat()是将所有的成员拼接成一个成员,所以成员变成了一个,适合有limit(0,1)的场合
注意:不管是group_concat()还是concat()都不能直接对 * 进行字符串连接,就是不能类似于这样写:select group_concat(*) from users / select concat(*) from users ;
报错注入:
本篇后端php会打印你的MySQL报错信息,
答应代码:
这篇文章开头已经说了当你的web前端页面并不会打印你的账号密码,那怎么查询你的账号和密码的值呀?
---说实话,基本上现在的web前端页面都不会打印你的账号密码的,你看看有哪个网站会打印这些重要信息
1、利用updatexml()的报错功能
原理:updatexml()方法实际上是去更新我们后端的xml文档,但是我们在xml文档路径的位置写一个MySQL的查询语句,然后我就会报错,但是他在报错的时候其实已经执行了这个MySQL的查询语句,因为就是执行了才知道这个是错误的;
注意执行的代码是mysql_quer()这个函数;
updatexml(old_value,xpath,new_value);
1、old_value ---要更新的数据
2、xpath ---这个xml文档的路径
3、new_value ---更新后的数据
当有如下xml数据时:
那么我们想要将指定部分修改成新的数据,我们就应该:
这里的text()是获取student[1]下所有的数据;
----所以说从这里可以看出来我们在xpath位置必须准确的写上xpath路径,那么如果我们写上一个特殊字符,这系统显然就能看出来这个路径是不符合规定的,所以mysql_quer()在执行时就会发现updatexml的路径不对,所以就会报错但是mysql_quer()函数是会将其MySQL代码进行执行;
至于这个报错有没有被打印出来,那就要看这个后端有没有打印MySQL执行错误的相关代码了,反正第5关是打印了的;
2、直接在updatexml中在xpath部分执行MySQL代码;
---显然xpath部分是应该写xml路径的,这里我们写了写了一个不符合规定的路径就会报错,如在路径里写了特殊符号就显然会在mysql_quer()执行的时候报错,
但是报错的同时里面的MySQL代码还是会被执行;这个是updatexml的一个特性
你看,这里我就通过concat()方法将特殊字符‘~’插入到了xpath路径里面去,同时路径里面还包含了一个MySQL代码,mysql_quer()在执行的时候就会发现这个updatexml的路径部分有问题,自然而然的就会爆报错,同时还是会将其中MySQL代码进行执行;
最后因为后端的写了--将这个接收的参数值如果运行出问题的话,就会将其打印--的这种代码,所以最后就会将这个报错信息打印出来;
127.0.0.1/sqli-labs-master/Less-5/?id=1'andupdatexml(1,concat('!',(database())),1)--+
注意你要执行的那个嵌入在路径里面的MySQL代码必须要加括号,不然updatexml是真的无法识别你的MySQL代码;其实updatexml本意是不想识别出你的MySQL代码的,但是你加入了(),那她就没办法迫不得已识别出来了
---这样就把当前使用的数据库找到了;
---通过这种类似的方法最终像前面一样将你的数据库,列成员一个一个搞出来;
3、找到数据库,找数据表
127.0.0.1/sqli-labs-master/Less-5/?id=1'and updatexml(1,concat('!',(selectgroup_concat(table_name)frominformation_schema.tableswheretable_schema=security)),1)--+
4、通过数据表查找列成员 ---条件:禁止了information_schema库
127.0.0.1/sqli-labs-master/Less-5/?id=1' and updatexml(1,concat('!',(select * from (select * from users a join users b using(id,username)) c)),1)--+
这里就是查找到了password这个成员;
5、通过列成员查找列成员的值,
这样如果我们直接输入:select * from users时,
会出现以下字样,这种显示的意思是应该只输出一个成员,这里显然 * 代表了id,username,password三个成员了;
因为updatexml的错误返回只能是一行字符串,所以我们需要使用到group_concat()或concat方法将他们合并;
--->
127.0.0.1/sqli-labs-master/Less-5/?id=1'andupdatexml(1,concat('!',(selectgroup_concat(concat(id,username,password))fromusers)),1)--+
---但是显然我们可以看出,这个账号密码并没有全部显示出来!
这是因为我们的updatexml()方法的报错返回最大只能返回32个字节;
所以我们接下来就需要对获取到的数据进行分段获取
分段获取有两种方法1、通过concat()和limit 0,1一起搭配使用
---http://127.0.0.1/sqli-labs-master/Less-5/?id=1%20and%20updatexml(1,concat(!,(select%20concat(id,username,password)%20from%20users%20limit 0,1)),1)--+
因为concats是将多个字段值合并成类似于一个字段值,但是行数不变;所以我们这里可以通过limit 一行一行的截取下来
2、通过substring(str,起始位置,终止位置)来对字符串进行截取;
---http://127.0.0.1/sqli-labs-master/Less-5/?id=1%20and%20updatexml(1,concat(!,(select%20substring(group_concat(concat(id,username,password)),32,64)%20from%20users)),1)--+
------------------》》extractvalue(1,xpath)这个函数和updatexml函数除开参数个数不一样,其余全部一样!!!
6、如果updatexml和extractvalue函数都被拦截禁用了,那么我们该怎么办;
--------------没办法,只能换函数!!
报错注入的7大函数:
floor函数
1、7大函数中很多函数都被禁用了,且很多函数因为版本问题都是被淘汰或者还没有开发出来;
但是有一个报错注入函数在MySQL5.0到8.x依然还在继续用,这个函数就是---->floor函数
floor报错注入函数需要搭配rand()随机函数、group by分组count()统计一起搭配使用;
---->
127.0.0.1/sqli-labs-master/Less-5/?id=1' and
select 1 from
(select count(*),concat(version(),floor(rand(0)*2))a from information_schema.tables group by a)x)--+
我们一步一步来解释
1、floor(rand(0)*2) ---生成一个0到1的随机数,floor是向下取整,然后给这个列取别名为a
2、group by是将a进行分组,0为一组,1为一组
3、count(*)是计算所有组分别有多少个元素
好,问题就出现在这了,当我们的count(*)计算分组的元素个数时,会出现错误;
错误:我们的MySQL计算分组时,是这样的一个步骤
---1、我们的count(*)在计算分组的条数时,他会看到分组group by 是根据floor(rand(0)*2)的值来产生数据的;
所以我们的count(*)在取记录时首先是先去执行floor(rand(0)*2),执行结果是0;
好,count(*)发现0这个分组并没有被记录在案;好我们就需要把0插入到我的count(*)这个表中;
但又来了,我们的count(*)在插入时,还会执行一次group by的floor(rand(0)*2);
所以我们最终记录进入count(*)中的分组是1,且条数是1;
---floor(rand(0)*2)的前6条是011011,因为rand(0)会变得固定
---2、第二次取数据,实际上是第三次计算数据;
得出结果是1,然后count(*) 发现这个数据我们count(*)表是有的,所以我们就直接在有的基础上+1(取数据,和新插入数据才会对group by 的floor(rand(0)*2进行执行))
---3、第三次取数据,实际上是第四次计算数据;
我们计算出来的结果是0,然后count(*)又发现这个0是表中没有的,所以就需要进行插入,但是在进行插入的时候,又会对group by 的floor(rand(0)*2)进行计算一次;
这样原本要新插入的0就变成了要新插入的1,当这个1插入的时候,系统就会报错,这个1已经是存在了的 ,你这里再插入是属于重复插入;重复插入1;
就是这种就会出现报错;
所以最终我们随便去from一个表都可以,只要是有6个行数据即可;
只要能到达floor(rand(0)*2)=011011=6个数据即可
2、这里我们的重复插入1变成了重复插入 :版本号1,因为我们把版本号和1结合起来了;
所以最后就是因为出问题了,而且MySQL还是将其含有的MySQL代码执行了,
因为后端有php对MySQL执行出错误时的报错打印,这样版本号就会被连着错误一起打印出来了;
3、select 1 from ()
为什么要在最外层加入色了select 1 from ()?
因为不加入的话,select count(*),concat(version(),floor(rand(0)*2))a from information_schema.tables group by a ,这行代码代表的是有两个输出成员,不管最终有没有输出这两个列成员,但是这MySQL代码已经真真切切的表示了这里将会有两个列成员,所以系统会直接报错;
成员多了是在编译时就会被发现(编译时的错误)
报错注入是在运行时才会被发现,才会被报出有错误(运行时的错误)
但是打印MySQL错误的这行代码的时候
说明,floor错误信息的返回只能是一连串的字符串,不允许打印多行或多列的这种类似于表的东西;
所以我们不能直接打印,你说能直接使用concat(count(*),concat(version(),floor(rand(0)*2))a)吗,那你里面取的别名还有什么意义,你都包起来形成新的列了,所以不行;
所以你只能在最前加select 1 from ();
哎?这样也只是让列变少了,但是行数还是实打实的跟select count(*),concat(version(),floor(rand(0)*2))a from information_schema.tables group by a的行数一样呀,就是两行,0和1呀?
no,我们刚刚已经算过了,压根还没有真正新插入0这一行,单单就在插入1这里就出现了重复1的情况,所以select 1 from(...)实际上是只输出了一行,
所以最终是输出的一行一列,在满足MySQL错误打印的要求:只能打印一连串字符串,不能打印多行或多列的类似于表的东西;
---其余查询的用法就是和updatexml/extractvalue一样了
127.0.0.1/sqli-labs-master/Less-5/?id=1and(select1from(selectcount(*),concat((select*from(select*fromusersbjoinusers using(id))a),floor(rand(0)*2))afrominformation_schema.tablesgroupbya)b)--+
如查询字段id的值:group_concat(id)
---当我们查询字段username/password的值时:
值得注意的是:
1、我们前面已经说过,我们的update错误返回只能是一行的这种连续的字符串;
不能是多行或多列的显示;而对于floor来说,返回的错误信息,如果字符串太长,floor错误提示也会出问题,说你只能显示一行;
floor和count(*)组合的错误提示只能是一行的一连串的字符串,且这个字符串不能太长,比update的错误提示条件又多了一个字符串错误信息返回不能太长
2、我们的MySQL代码都需要用()包裹起来;
3、注意:这里的重复和自己组合起来的新表确实是有多行,但是你注意没有,MySQL的执行顺序是先把内层的执行了再执行外层的,这里肯定也是一样,先执行内层的,
而内层是select * from users b join users ;
注意这里也是不会直接进行展示的,这里是需要先进行把表连接起来,然后才进行展示;
最终:而在连接表的时候就会出现字段重复的现象!!最终达到破解字段名的目的;
---MySQL的执行顺序:从内到外,从右到左;
---updatexml/extractvalue 他们错误返回的是最多32个字节的字符串
---floor和count()返回的错误信息只能是字符串,且长度不能太长;
---order by是将字段值进行一个排序;
当一个数据表中有id,name,passwd时;那么order by 3就是将passwd字段的所有值进行一个排序
---------------------------------我们只讲思路,不讲详细过程;
过程是背,思路是理解;
想要走多远,10%背+90%理解
祝你年薪百万,成绩辉煌!!!