BUUCTF [SUCTF 2019]EasySQL1 做题笔记/心得(自留)

前言

本篇并不作为一篇write up,仅记录自己的部分观点以及思考过程,此题在该站上已经有了许多优质题解,大家可以先去熟悉题目以及解法思路之后再来阅读该篇文章,而笔者作为一名web新人,基础较差,很多地方看待问题存在钻牛角尖以及常识缺乏问题,欢迎大家提议讨论,在不久的将来如果实力成熟,也会再次回过头来回顾自己如今的思考过程以及产生误区的原因进行分析。

参考文章

提交方法是post,回显信息过少,最初的想法是盲注。注了半天页面没什么反应,对页面的反应情况经验真的很差。按理这个时候应该要判断出来已经被过滤了。这里我直接贴一篇另外一位博主的思路。http://t.csdnimg.cn/MfnGn,我认为这篇题解的质量远远高于其他同题题解,大家可以参考。

正题:

开篇详细的对一般注入方法进行排除,最后博主同样查看了题解。这里也是大多数人进行的一步,根据目前已知的题解我们可以得到以下信息:

select $post['query']||flag from Flag";

这个是大佬推理出来的后端源码,$post['query']为用户提交的内容,而基于此模版,网友给出了以下两种题解

payload 1: 1;set sql_mode=PIPES_AS_CONCAT;select 1
​
payload 2: *,1

事实上最开始我两种办法都没有看懂,这里就从第一个版本的答案开始讨论吧。

payload 1
  • 首先合成语句,将语句模版与版本1答案拼接可以得到:

select 1;set sql_mode=PIPES_AS_CONCAT;select 1||flag from Flag";
  • 在这里,我们可以得到三句话:

select 1;
set sql_mode=PIPES_AS_CONCAT;
select 1||flag from Flag";

于此同时我们再来对照该版本的回显flag(注意哦,两个版本的回显是不一样的,这里也反应了两个版本解法的不同之处,是个细节)

 

Array ( [0] => 1 )Array ( [0] => 1flag{0bef1495-9554-4cae-94f7-e42dcf8f60de} ) 

此时我们对应回显,一段段分析payload

第一句select的貌似没有起到什么作用,因为第三句也是select,他的回显已经被覆盖了。也就是说这一个flag的回显是第三句话的功能带出来的。

而第二句的作用是将||从或的功能改为concat拼接的功能

而第三句,也就是这一句话输出了最终的flag回显。事实上这段回显我们也可以拆成两个部分来看

Array ( [0] => 1 )
Array ( [0] => 1flag{0bef1495-9554-4cae-94f7-e42dcf8f60de} ) 

第一段如果大家对题目熟悉不难发现这就是传参为1时的回显,而此时他和flag被拼接在了一起,同时显示了出来,这也就是第二句话的作用所在,在set sql_mode=PIPES_AS_CONCAT;的加持下:

原产生回显的payload:select 1||flag from Flag";
可以看成是:select concat(1,flag) from Flag;

也就是说他将传入1和传入flag的值连在了一起,这才有了最后的答案。

  • 总结:

    • 两个解法首先最终要的是要推出来select $post['query']||flag from Flag";这个后端源码的格式,否则两种解法都不好写。但是关于这一步我暂时还没想到很好的硬性逻辑推理,只能说实在想不到也没办法,之后有时间再想想吧,或者多积累经验。

    • 其次一个就是set sql_mode=PIPES_AS_CONCAT;这段关键的转换。我感觉也挺冷门的。

    总之就是这种解法虽然都分析出来了,但是就目前来说让我自己去写还是会有点无从下手。

payload 2

这一段代码才是我真正想拿出来讲的地方

首先是同样的步骤

payload 2: *,1//仅三个字符

拼接

select *,1||flag from Flag";

需要注意的是在这个地方||就是作为运算符出现

那么我们同样来拆解一下这句话:

首先我们知道select 1,2 from user可以得到查找1和2的结果,因此这里也可以以逗号为界看成两句话(这里看成两句话仅仅是为了易于理解,在输出方式上和第一个payload还是有区别的,这里后面会题)

select * from Flag
select 1||flag from Flag

由于他们是用逗号隔开,所以这里两条语句的结果应该都会显示,第一句话很简单,显示的是flag表里所有的信息,目前看来flag是以一个数组的形式出现,我对这块不太了解所以不好说什么。没怎么学php,数组这块一窍不通。

而第二句话!!

看见很多篇题解说的是什么 1||flag,短路原则,1为真,后面的不判断了。直接输出1;

其实我就不懂这是什么意思,select 1||flag,搜索到了1所以就不会执行flag了吗?

但是这样的话意思不就是为了不执行flag才在前面加个1。但是人家都叫flag了,肯定是要搜flag才好,第一个payload也可以看出我们是select flag才找到的答案。那如果我直接输入0.语句拼接成·select 0||flag from Flag难到就会因为他是0,直接搜flag给我蹦答案吗?试了之后发现也不会,甚至回显都没得了。这里就不得不再次提到开头说到的那篇题解。

那篇文章对于||以及payload 1中的的用法自己在sql里一个个实验并且证明了,也是唯一一个没有提出 *,1中的1是为了使条件为真的解答,很庆幸看到了那篇解答,但是我脑子比较笨,看了一会不是很懂。自己拿phpdmin实验了半天。

但是我比较懒,大家自己去看那篇题解吧,里面截图很明白了

假设我select column1||column2 from tab;e

实际上返回的值是根据我c1和c2里每一行的值决定的

还是贴个图吧

对比会发现0和user里怎么||都是0,因为user里都是字符,0||0只能得到0.而0||id是可以得到1或者0的,只要id内的值不是0,不出现0||0,就可以返回1.

说到这里,我们又绕回来select 1||flag from Flag,这句话后面是flag,里面很大可能是字符串,返回为0.看似前面的值很重要。如果前面写1||flag,返回1.写0||flag,此时两边都是0,返回0.但是实际上。我管他返回什么,他又不返回flag。真正起到作用返回flag的是第一句,select*from,也就是说实际上我把这题payload改成

*,0

也是可以过的。所以写题很忌讳看完答案就想当然,给自己编一个说服自己答案是这个的理由,而不去思考深究其中的原理。

弄明白了其中原理,我们再来整体看看这句话select *,1||flag from Flag";

第[0]列输出的是select*的内容(这里面应该只有flag这一个值)

第[1]列输出的1(实际上你把代码改成*,0的话array[1]李输出的就是0了),那么这又是为什么呢。往下看。

Array ( [0] => flag{0bef1495-9554-4cae-94f7-e42dcf8f60de} [1] => 1 ) 

终于到了最后一步,我们来对比一下两种不同payload 给出的结构

1:Array ( [0] => 1 )Array ( [0] => 1flag{0bef1495-9554-4cae-94f7-e42dcf8f60de} ) 
2:Array ( [0] => flag{0bef1495-9554-4cae-94f7-e42dcf8f60de} [1] => 1 ) 

可以看到第一个方法输出了两个array[0],因为他其实进行了两次select 每次只输出一列,然后把他们拼在了一起。

而第二个方法有array[0]和array[1];

因为是用的逗号隔开,因此他们其实是一次性输出的

array[0]输出的是select*,array[1]输出的1||flag

这也是为什么当我们把payload改成*,0的时候array[1]的结果是0.

因为前面说了1||flag等于1,0||flag=0,他改变的其实是后面一列的值,并不重要,主要是为了满足句子。

其他补充说明:

  • 官方给出的答案其实是第一种,绕开select post内容||**flag** from Flag 中的||直接取到后面flag的值,而后面那个是大佬写的,事实上如果不是因为他只在那个列里装了flag,select *很有可能会因为回显位不够找不到。

  • 另外也不能直接使用flag的搜索。因为对内容似乎做了截取,抓包后会发现无论如何只能传上去一个字符。

  • 第三,在我参考的那篇帖子中,作者提出了这样的疑问:

    payload:*,1
    ​
    ​
    ​
    1可以换成任何数字,但不能是其他(原因不知道)

说真的我很开心看见这样的疑问,作者也有在很认真的思考每一句话,每一个变量的含义,对于不明白的地方就会自己去尝试,即使没有想到结构,也不会因为是题解编随便填答案一样交一个看上去合理的糊弄过去。

在这里我给出这个问题的一种可能性,我们知道后面的这个1是作为一个列名和||后面的f**lag**去组成运算。

我在其他帖子中看见这样的回答,即使这个名为1的列不存在,系统也会创建出一个值全部为1的列,与后面的列进行比较运算。我们可以由此猜测,当paload为:*,数字时,后面的数字位即使找不到该列,也会自动创建,与flag进行运算,但如果是字符型,系统便无法创建。这一点也很好验证,我可以拿number和flag进行||,但是如果我拿其他不存在的列名进行||运算系统便会报错。

总结:这篇题解写的很舒畅,虽然已经很晚了,断断续续写了两个小时左右,以前和朋友们玩本格推理的时候一致认为写题解也是一个盘思路的好机会,在这个过程中能发现完善很多没有注意到的细节。事实上在这篇文章的创作过程中也发生了这样的事情,很多东西都是我边写便理思路,然后根据整理好的内容进一步得出新的结论(有几次差点以为自己翻车了,全部白写,思路全是错的。好在后面又缕回来了)

对我而已这篇题解在解题角度可能没有什么实质性的帮助,我做不到像payload1那样了解各种语句的用法,也没有payload2

那样强大的判断力。但是对我来说这题仍然是一道细节很多的sql题。更重要的是在其中找到了那种解题的快乐,同时希望能帮助到大家。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值