*
号,这样我们就无法使用块注释法来代替空格。而且也去掉了#
,这样我们添加单引号后,也无法通过结尾注释法来消除原本就存在在末尾的单引号。除此之外,其还去掉了包括UNION
在内的一众SQL关键字。这样我们就得想想办法了。substr
函数来截取子字符串。shaoqunliu'AND SUBSTR(password, 1, 1)='?
时,拼接后的SQL语句为:username
为shaoqunliu
的时候,并且?
处的字符为数据库中密码hash串的第一个字符时,这条SQL语句是有输出的。在外面看,其会返回“密码错误”。当?
处的字符不为正确密码hash的第一个字符时,这条SQL是不会有输出的,从外面看,其会返回“用户名错误”。SUBSTR(password, 1, 1)
里面的逗号用不了了,我们可以使用FROM ... FOR ...
来代替,上述截取字符串逻辑即可写成SUBSTR(password FROM 1 FOR 1)
。然后突然发现,你FOR
也用不了,于是我们只能倒着推导密码字符串,也就是使用SUBSTR(password FROM 32)
,从32到1,每次试验前将之前试对的字符都拼接在要试验的字符后面。FOR
的问题解决了,我们发现我们AND
也用不了,同时被屏蔽的还有AND
的替代字符&
。AND
可以用子查询配合等号来替代,我们先来试着写一下子查询,子查询需要保证的条件就是当用户名为shaoqunliu
且密码hash串的后几位为?
的时候返回true
,在不符合上述条件时无返回,且不能使用AND
,如下:#
注释符消除掉最后的那个'
给整个SQL带来的影响,如下:shaoqunliu
的那一行的时候:?
,则等号的前半部分返回值为1,后半部分返回值亦为1,所以整个WHERE
子句的值为真,整个SQL有返回。?
的时候,等号的前半部分为1,后半部分返回值为NULL
,整个SQL无返回。shaoqunliu
的其它行的时候,对于子查询而言,SELECT 1 FROM user WHERE username='shaoqunliu'
返回值首先就为NULL
,接着就会导致整个子查询部分返回NULL
。因此在这种情况下SQL是无返回的。背景知识:在SQL中NULL
与任何数做比较结果都只能是NULL
。例如SQL:SELECT NULL=0, NULL=1, NULL<1, NULL>1
的返回结果就为4个NULL
。
AND
不能用的问题呢,我们再来看一下我们的SQL,我们需要解决的下一个问题,即是#
注释符不能用了,而且--
也被屏蔽了,所以我们需要采用别的手段来消除掉最后那个’
。我们前面已经使用了一个等号,而且SQL是支持连等的,所以我们可以通过='
或者!='
来消除最后的'
,而且此时我们还必需保证对前半部分的语义没有影响。我们先假设用='
来处理:((username='shaoqunliu')=(SELECT 1 FROM (SELECT 1 FROM user WHERE username='shaoqunliu') C WHERE SUBSTR(password FROM 32)='?'))
,作为一个整体X
,对于等式,X=''
,X
处的内容只有为0或者它自身的时候,等式才为true
。shaoqunliu
且密码hash串的后几位恰好为?
时,SQL才能有返回,也就是WHERE
子句的值为1。而这个时候,前半部分,也就是整体X
的值为为1,1=''
的值为0,恰好不符合我们的要求,所以我们在此处填充的应该是!='
。走完这一步,我们得到了如下SQL:shaoqunliu
这个账户,密码的MD5值为A61A78E492EE60C63ED8F2BB3A6A0072
,我们找了一个线上的MD5彩虹表网站——CMD5,经过彩虹表反查得知此hash所对应的密码值为pa$word
。就这样,我们即可使用这个用户名密码登陆系统啦。