Skip to content

SQL注入漏洞

前置知识

与MySql注入相关的知识点

MySQL默认在数据库中存放一个information_scheme该库中需要记住三个表名SCHEMEATATABLESCOLUMNS

SCHEMATA表存储该用户创建的所有数据库的库名,我们需要记住的数据库名的字段名为SCHEMA_NAME

TABLES表存储该用户创建的所有数据库的库名和表名,字段名分别为TABLE_SCHEMATABLE_NAME

COLUMNS表存储该用户创建的所有数据库的库名、表名和字段名,分别为TABLE_SCHEMATABLE_NAMECOLUMN_NAME

几个函数

database():当前网站使用的数据库

version():当前mysql的版本

user():当前mysql的用户

注释

注释符:# -- 空格 /**/

内联注释:/*! code */

SQL注入漏洞基本概念

SQL注入漏洞的产生需要满足的两个条件:

1、参数用户可控:前端传给后端的参数内容是用户可以控制的

2、参数带入数据库查询:传入的参数拼接到SQL语句,且带入数据库查询

当传入参数为1‘时数据库执行的代码如下所示,这不符合数据库语法规范,所以会报错(可控)

sql
select * from users where id = 1'

当传入参数为and 1=1 时执行的SQL语句如下,id=1为真,1=1也为真,所以页面会返回与id=1相同的结果 当传入的id参数为and 1=2时,由于1=2不成立,所以返回假,页面就会返回与id=1不同的结果(已带入并且返回)

sql
select * from users where id = 1 and 1=1

在实际环境中凡是满足上述两个条件的皆可能存在SQL漏洞

SQL注入漏洞深度剖析

前置条件

所谓的SQL注入,是利用了网页的地址,即URL。首先,URL是必须是动态网页,也就是能够通过网址进行参数传递,如’?id=1’;静态网页因为没有参数传递,所以不存在SQL注入的概念。

动态网页信息输入的原理

通常动态网页会涉及到信息的输入,例如网站登录时ID和密码的验证过程实际就是通过网址对数据库进行参数传递,首先验证信息,然后从数据库获取并返回数据。通过网页参数传递的过程,实际是一个通过前端(浏览器)向数据库进行数据获取的过程,这个过程实际上是通过SQL语句实现的,这个过程通过传递不同的参数,向数据库进行验证或者获得想要的数据。

TIP

但前端是无法看到传递的SQL语言是什么,我们只能在网页地址中看到传递的参数是什么。这个参数就像一个接口,前端通过它与向端数据库发送指令,即SQL语句。

SQL注入利用的就是这个接口(如?id=xxx),这里的id就是参数,在SQL语句中它是一个列属性,数据表的这一列记录的是不同的id。

我们知道通过网址传递的是一个参数(类似于程序语言中的变量),通过赋予它不同的值来获取数据库中对应的数据,如:

Select  username,password from information_schema.columns where  id=‘1’

这个语句,假设通过屏显返回的数据就是用户名和密码,那么在前端向服务器进行请求的过程中,参数’1’是根据我们输入的内容而变化的,在向数据库服务器请求数据时,SQL语句结构并不发生变化,只有参数id是根据前端输入改变的一个变量。

> [!IMPORTANT]

> 综上,在网址中进行参数传递,其实是执行一个只有参数变化的SQL查询语句,我们只能通过浏览器向后端传递参数,既然SQL语句是存在于后台的,我们无法控制查询的数据类型(如数据库名、表名、列名、参数为任意值时对应的其它属性信息等),如果要执行各种我们感兴趣的查询,就要使用SQL语句。

但如何向后端传递我们编写的SQL语句呢?请继续往后面看

传递查询语句

已知参数是在URL中通过?xx=xxx进行传递的,也就是说我们只能向服务器输入特定类型的参数(由后端封装),如果我们能想办法通过传参这个接口,将写好的SQL语句传给数据库,那么就达到了侵入数据库获取信息的目的,也就是SQL注入。

假设我们输入参数后后端执行的查询语句依然是

Select  username,password from information_schema.columns where id='1’

我们都知道SQL语句可以进行联合查询,这里讲述原理就使用联合查询进行举例。

假设我们能够在这个语句的基础上,插入一段union select xxxxxxxx的话,就能够让数据库执行后半句select语句并返回我们想要的信息。

联合查询是什么

什么是联合查询?将多个SQL的结果取并集,同时查询两张有一定关联性的表

SELECT column_name(s) FROM table_name1 UNION SELECT column_name(s) FROM table_name2

两条查询语句同时查询 前后两次查询的表 字段必须相同、列数也要相同、查询顺序必须相同

联合查询注入是联合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同,不然会出现报错。

union 用于合并两个或多个 SELECT 语句的结果集。等同于将一个表追加到另一个表,从而实现将两个表的查询组合到一起,使用谓词为UNION或UNION ALL。

UNION 内部的 SELECT 语句必须拥有相同数量的列,列也必须拥有相似的数据类型,每条 SELECT 语句中的列的顺序必须相同。

DANGER

两条查询语句同时查询 前后两次查询的表 字段必须相同、列数也要相同、查询顺序必须相同

联合查询注入是联合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。两个表的字段要数要相同,不然会出现报错。

union 用于合并两个或多个 SELECT 语句的结果集。等同于将一个表追加到另一个表,从而实现将两个表的查询组合到一起,使用谓词为UNION或UNION ALL。

UNION 内部的 SELECT 语句必须拥有相同数量的列,列也必须拥有相似的数据类型,每条 SELECT 语句中的列的顺序必须相同。

漏洞利用过程

猜测后端查询包装形式

我们如何将这个语句通过浏览器传递给后端呢?

首先我们要猜语句,就是通过向浏览器中输入一些无法识别的参数,如’,),"等(将这些符号作为参数时系统会返回语句错误信息),从语句错误信息中我们就可以分析出参数在语句中是如何被包装的。

因为参数可以有多种包装形式,如id=1,id=‘1’,id=(1),id=(‘1’),id=(“1”)都是正确的,所以我们需要从页面返回的语句错误信息来判别参数时如何被封装的。

如我们在URL中的传参部分输入id=1’,这时系统返回错误语句you have an error …near’‘1’’ LIMIT 0,1’ at line1

这里要重点分析的就是near后面的语句,注意观察1周围的几个上引号,左边第一个上引号与 LIMIT 0,1后面的上引号是一对,用来强调内容,那么错误语句中的参数1就变成了’1’’,也就是说SQL语句执行的参数是id=‘1’’,显然是因为在1后多输入了一个’造成的,这时我们就分析出了前端输入的参数在SQL语句中是通过两个单引号’_'进行包装的。

漏洞利用

我们都知道原本前端输入的参数是数字,后端执行时会给参数加上

这时语句就结束了

SELECT first_name, last_name FROM users WHERE user_id = '1'

如果我们在后面写一个union语句,就可以获得想要的数据

SELECT first_name, last_name FROM users WHERE user_id = 1 union select database()

但这里有一个问题前端默认传递的是一个参数,所以会在传参最后加一个单引号

SELECT first_name, last_name FROM users WHERE user_id =  '1 union select database() '

如果我们加了一个union语句,就会造成union前的select语句中的参数id只有左边一个单引号,这时如果我们在前端对参数id=1补充一个,再插入union语句,就可以使得前半句顺利的执行,后半句入侵语句也可以执行。

SELECT first_name, last_name FROM users WHERE user_id =  '1’ union select database() '

常见的注入手法

四大基本注入手法

联合注入:可以使用union的情况下的注入

报错注入:页面会返回错误信息,或者把注入的语句的结果直接返回在页面中

布尔盲注:可以根据返回页面判断条件真假的注入

时间盲注:用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断

其他类型注入手法

宽字节注入

二次注入

OOB带外注入

堆叠注入:可以同时执行多条语句的执行时的注入

TIP

均可以通过sqli-labs靶场进行练习了解 实战出真知 这里就不再赘述 后面空闲再补充

挖掘工具

sqlmap

https://github.com/sqlmapproject/sqlmap

使用文档

https://wiki.ckcsec.cn/web/tools/Sqlmap的使用.html

Xia sql

https://github.com/smxiazi/xia_sql

使用文档

http://www.nmd5.com/posts/2022-02-16-28/

防护方案

  1. 对输入进行严格的转义和过滤
  2. 使用参数化(Parameterized):目前有很多ORM框架会自动使用参数化解决注入问题,但其也提供了"拼接"的方式,所以使用时需要慎重!
  3. PDO预处理 (Java、PHP防范推荐方法:)

没有进行PDO预处理的SQL,在输入SQL语句进行执行的时候,web服务器自己拼凑SQL的时候有可能会把危险的SQL语句拼凑进去。但如果进行了PDO预处理的SQL,会让MYSQL自己进行拼凑,就算夹带了危险的SQL语句,也不会进行处理只会当成参数传进去,而不是以拼接进SQL语句传进去,从而防止了SQL注入。

PDO数据库抽象层学习:http://www.php.cn/course/868.html

网络层面:

  1. 通过WAF设备启用防SQL Inject注入策略(或类似防护系统)
  2. 云端防护(阿里云盾等)