全解析:從 v1.0.0 到 v1.0.3 的 CLR 托管能力變遷)
網(wǎng)絡(luò)安全【免費(fèi)下載鏈接】sliverAdversary Emulation Framework項(xiàng)目地址https://gitcode.com/gh_mirrors/sl/sliver點(diǎn)擊查看免費(fèi)下載本篇技術(shù)指南圍繞隨 Sliver 植入體源碼一并 vendored 的第三方依賴 go-clr 變更日志 展開完整梳理該庫四個(gè)已發(fā)布版本v1.0.0 ~ v1.0.3的每一次變更背景并結(jié)合倉庫內(nèi) go-clr 源碼接口封裝、STDOUT/STDERR 重定向、HRESULT 處理與 Sliver 植入體在taskrunner中的實(shí)際集成調(diào)用鏈說明這些版本演進(jìn)如何影響 Windows 端進(jìn)程內(nèi) .NET 程序集執(zhí)行能力。讀完本文你將掌握 go-clr 各版本核心修復(fù)的底層原理以及 Sliver 如何通過LoadCLR/LoadAssembly/InvokeAssembly組合復(fù)用它執(zhí)行內(nèi)存中的 .NET 程序集。一、go-clr 是什么為什么它出現(xiàn)在 Sliver 倉庫中g(shù)o-clr 是一個(gè)用純 Go 編寫的 PoC 級庫目標(biāo)是在 Go 進(jìn)程內(nèi)托管 CLRCommon Language Runtime從而直接從磁盤加載 .NET DLL或從內(nèi)存加載 .NET 程序集assembly并執(zhí)行。它的實(shí)現(xiàn)方式是不依賴 cgo而是通過封裝 Windows 系統(tǒng)調(diào)用syscall與大量unsafe.Pointer操作從內(nèi)存中按 COM 接口 vtable 布局裝載結(jié)構(gòu)體逐一調(diào)用ICLRMetaHost、ICLRRuntimeInfo、ICORRuntimeHost等托管宿主接口。該庫以 vendored 依賴的形式被固定在 Sliver 倉庫中目錄 implant/vendor/github.com/Ne0nd0g/go-clr并在植入體側(cè)的 dotnet_windows.go 中被顯式導(dǎo)入使用其角色是支撐 Windows 植入體執(zhí)行execute-assembly類任務(wù)。其自述文檔README.md也坦誠說明這是實(shí)驗(yàn)性 PoC大量使用unsafe并非生產(chǎn)級穩(wěn)定代碼但作為攻防對抗Adversary Emulation框架的進(jìn)程內(nèi) .NET 執(zhí)行組件其價(jià)值在于完全用 Go 復(fù)刻了 CLR 托管的關(guān)鍵路徑。二、版本 1.0.02021-04-08從 fork 出發(fā)的初始標(biāo)簽版變更日志對本版本的描述是Initial tagged version from forked——即從上游 fork 后打出的第一個(gè)正式標(biāo)簽。2.1 新增STDOUT/STDERR 重定向緩沖本版本最重要的新增特性被明確記錄為Added buffer to collect redirected STDOUT/STDERR新增緩沖區(qū)以收集重定向后的 STDOUT/STDERR。這一能力在今天的源碼中依然保留構(gòu)成了 io.go 的核心包級聲明了Stdout、Stderr兩個(gè)bytes.Buffer見 io.go以及用于串行化讀寫的mutexRedirectStdoutStderr()通過os.Pipe()分別為 STDOUT/STDERR 創(chuàng)建新的讀寫文件句柄再用windows.SetStdHandle將STD_OUTPUT_HANDLE與STD_ERROR_HANDLE指向新句柄見 io.go隨后啟動(dòng)BufferStdout()/BufferStderr()兩個(gè) goroutine以 4KB 緩沖區(qū)循環(huán)讀取管道內(nèi)容并調(diào)用bytes.TrimRight(buf, \x00)剔除空字節(jié)后寫入Stdout/Stderr緩沖見 io.go。之所以必須重定向原因在代碼注釋中寫得很清楚CLR 執(zhí)行托管程序集時(shí)運(yùn)行在 Go 進(jìn)程之外無法用普通的 Go 方式捕獲輸出因此只能把進(jìn)程級句柄換掉再收回來。這一點(diǎn)正是 C2 框架需要拿到程序集輸出并回傳的關(guān)鍵機(jī)制Sliver 正是這一設(shè)計(jì)的直接受益者。三、版本 1.0.12021-04-08標(biāo)簽與導(dǎo)入路徑的修正變更日志用一句帶過Learned how to correctly use tags and updated import學(xué)會(huì)正確使用標(biāo)簽并更新了導(dǎo)入路徑。從倉庫現(xiàn)狀可以印證這次修正的結(jié)果項(xiàng)目模塊從github.com/ropnop/go-clr遷至github.com/Ne0nd0g/go-clrREADME 中的安裝示例使用go get github.com/ropnop/go-clr而 Sliver 的導(dǎo)入語句已是clr github.com/Ne0nd0g/go-clr見 dotnet_windows.go模塊元數(shù)據(jù)在 implant/go-mod 與 implant/go-sum 中記錄為github.com/Ne0nd0g/go-clr該版本同日發(fā)布與 v1.0.0 在同一天說明這是對初版標(biāo)簽的快速修正而不是功能性迭代。對使用者而言這個(gè)版本變化意味著導(dǎo)入路徑從原作者 fork 的倉庫切換到了維護(hù)者的新倉庫Sliver 倉庫內(nèi)記錄的正是修正后的版本路徑。四、版本 1.0.22022-04-02目標(biāo)程序集加載修復(fù)與依賴升級本版本有兩項(xiàng)變更4.1 修復(fù)正確加載目標(biāo)程序集變更日志記錄合并了來自 audibleblink 的 Pull 2fixed an error when attempting to load correctly targeted assemblies修復(fù)了嘗試加載正確目標(biāo)程序集時(shí)出現(xiàn)的錯(cuò)誤。結(jié)合源碼可以理解這項(xiàng)修復(fù)對應(yīng)的執(zhí)行路徑。程序集從字節(jié)數(shù)組加載的完整鏈路位于 go-clr.go 的ExecuteByteArray中通過CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost)創(chuàng)建 metahost調(diào)用GetInstalledRuntimes枚舉已安裝運(yùn)行時(shí)并按目標(biāo)版本篩選如v4取得ICLRRuntimeInfo后調(diào)用IsLoadable()校驗(yàn)可加載性取得ICORRuntimeHost并獲取默認(rèn) AppDomain將原始字節(jié)構(gòu)造為 SafeArrayCreateSafeArray調(diào)用appDomain.Load_3(safeArrayPtr)從內(nèi)存加載程序集通過assembly.GetEntryPoint()取入口方法再依據(jù)方法簽名是否接收參數(shù)來決定是否用PrepareParameters構(gòu)造參數(shù) SafeArray最后methodInfo.Invoke_3執(zhí)行。任何一步的 HRESULT 非零都會(huì)返回錯(cuò)誤——v1.0.2 的修復(fù)正是針對加載到的程序集并非調(diào)用方所期望的目標(biāo)程序集這類尋址/加載問題確保Load_3與后續(xù)入口點(diǎn)解析能命中正確的程序集映像。4.2 變更升級支撐 Go 模塊同步更新了所依賴的 Go 模塊版本。倉庫內(nèi) hresult.go 依賴golang.org/x/sys/windowsio.go 中大量使用其句柄與 DLL 函數(shù)這類基礎(chǔ)設(shè)施模塊的升級通常伴隨接口簽名或常量定義的同步更新也是該版本Change而非Fix的原因。五、版本 1.0.32022-11-10錯(cuò)誤處理與無控制臺(tái)輸出兩大修復(fù)這是變更日志中記錄的最新一個(gè)版本包含一次行為變更和一次缺陷修復(fù)也是 Sliver 倉庫 vendored 到的版本。5.1 行為變更返回錯(cuò)誤而非直接退出程序合并自 mec07 的 Pull 3return errors instead of exiting the program返回錯(cuò)誤而非直接退出程序。這一變更把庫的失敗模式從進(jìn)程級os.Exit改為函數(shù)級返回 error對庫的宿主尤其是 C2 植入體至關(guān)重要——如果 .NET 程序集執(zhí)行失敗就令整個(gè)植入體進(jìn)程退出后果是災(zāi)難性的。從當(dāng)前源碼看這一設(shè)計(jì)已貫穿所有對外 APIExecuteDLLFromDisk在ExecuteInDefaultAppDomain返回非零值時(shí)會(huì)返回the ICLRRuntimeHost::ExecuteInDefaultAppDomain method returned a non-zero return value: %d錯(cuò)誤見 go-clr.goLoadCLR對運(yùn)行時(shí)枚舉失敗返回包裝錯(cuò)誤見 go-clr.goInvokeAssembly在執(zhí)行出錯(cuò)時(shí)仍繼續(xù)讀取重定向緩沖區(qū)并返回 stdout/stderr注釋明確Dont return because there could be data on STDOUT/STDERR見 go-clr.go。5.2 缺陷修復(fù)無控制臺(tái)應(yīng)用無法返回輸出對應(yīng) Issue 4 ——Applications Without A Console Will Not Return Output無控制臺(tái)的應(yīng)用程序不會(huì)返回輸出。該問題的根因與修復(fù)邏輯完全保留在 io.go 中kernel32 : windows.NewLazySystemDLL(kernel32.dll) getConsoleWindow : kernel32.NewProc(GetConsoleWindow) // Ensure the process has a console because if it doesnt there will be no output to capture hConsole, _, _ : getConsoleWindow.Call() if hConsole 0 { // https://learn.microsoft.com/en-us/windows/console/allocconsole allocConsole : kernel32.NewProc(AllocConsole) ret, _, err : allocConsole.Call() if ret 0 { return fmt.Errorf(there was an error calling kernel32!AllocConsole with return code %d: %s, ret, err) } // Get a handle to the newly created/allocated console hConsole, _, _ getConsoleWindow.Call() // Hide the console window user32 : windows.NewLazySystemDLL(user32.dll) showWindow : user32.NewProc(ShowWindow) ret, _, err showWindow.Call(hConsole, windows.SW_HIDE) ... }修復(fù)邏輯分三步先用GetConsoleWindow探測當(dāng)前進(jìn)程是否有控制臺(tái)若沒有hConsole 0典型場景即 C2 植入體這種無控制臺(tái)的 GUI/服務(wù)進(jìn)程則調(diào)用AllocConsole分配一個(gè)隱藏控制臺(tái)再用ShowWindow(..., SW_HIDE)將其隱藏最后才執(zhí)行SetStdHandle重定向。這一步順序非常關(guān)鍵重定向到管道的前提是進(jìn)程存在可寫的標(biāo)準(zhǔn)輸出設(shè)備沒有控制臺(tái)時(shí)重定向無從談起。代碼注釋還記錄了一個(gè)容易踩的坑被重定向的 STDOUT 寫入端一旦被關(guān)閉后續(xù)的Invoke_3會(huì)觸發(fā)COR_E_TARGETINVOCATION0x80131604定義見 hresult.go因此緩沖讀取采用永不關(guān)閉寫入端、依賴 goroutine 持續(xù)讀管道的模式。六、Sliver 側(cè)的真實(shí)消費(fèi)方式從版本能力到實(shí)戰(zhàn)鏈路go-clr 的 API 在 Sliver 植入體中被組織成單例 CLR 程序集緩存的使用模式見 dotnet_windows.goCLRInstance結(jié)構(gòu)體持有一個(gè)*clr.ICORRuntimeHost與sync.Mutex通過GetRuntimeHost懶加載首次調(diào)用時(shí)執(zhí)行clr.LoadCLR(runtime)并在失敗時(shí)盡力clr.RedirectStdoutStderr()見 dotnet_windows.goLoadAssembly(data, assemblyArgs, runtime)先按sha256.Sum256計(jì)算程序集哈希并查詢assemblies緩存未命中才調(diào)用clr.LoadAssembly(rtHost, data)加載隨后統(tǒng)一走clr.InvokeAssembly(methodInfo, assemblyArgs)執(zhí)行見 dotnet_windows.go一個(gè)細(xì)節(jié)處理當(dāng)參數(shù)為[]時(shí)會(huì)改寫為[ ]因?yàn)榭兆址衅瑫?huì)讓 CLR 加載器去匹配無參方法導(dǎo)致Main(String[] args)無法命中見 dotnet_windows.go。再往上task_windows.go 的InProcExecuteAssembly在調(diào)用LoadAssembly前還會(huì)視開關(guān)執(zhí)行 AMSI 補(bǔ)丁patchAmsi與 ETW 補(bǔ)丁patchEtw形成完整的進(jìn)程內(nèi) execute-assembly鏈路。可以說go-clr 各版本積累的錯(cuò)誤返回、無控制臺(tái)輸出捕獲、目標(biāo)程序集加載修復(fù)最終都轉(zhuǎn)化為 Sliver Windows 植入體執(zhí)行 .NET 程序集時(shí)的穩(wěn)定性和可觀測性。七、版本采納與升級建議當(dāng)前 Sliver 倉庫 vendored 的 go-clr 即為變更日志中的最新版v1.0.32022-11-10可通過 modules.txt 與 go-sum 核實(shí)其精確依賴版本。結(jié)合各版本變更點(diǎn)可以得出以下采納要點(diǎn)版本日期核心變更對 Sliver 類宿主的意義v1.0.02021-04-08fork 初始標(biāo)簽新增 STDOUT/STDERR 重定向緩沖奠定進(jìn)程內(nèi)輸出捕獲基礎(chǔ)v1.0.12021-04-08修正標(biāo)簽與導(dǎo)入路徑導(dǎo)入路徑固定為github.com/Ne0nd0g/go-clrv1.0.22022-04-02修復(fù)目標(biāo)程序集加載升級 Go 模塊提升從內(nèi)存加載程序集的命中準(zhǔn)確性v1.0.32022-11-10錯(cuò)誤返回替代進(jìn)程退出修復(fù)無控制臺(tái)輸出丟失植入體健壯性與輸出完整性的關(guān)鍵版本在將 go-clr 集成進(jìn)長駐進(jìn)程如 C2 植入體時(shí)應(yīng)從 v1.0.3 起步它保證 .NET 執(zhí)行失敗不會(huì)拖垮宿主進(jìn)程且為無控制臺(tái)進(jìn)程補(bǔ)齊了AllocConsole 隱藏窗口的兜底路徑。同時(shí)需要注意其 PoC 定位——代碼大量依賴unsafe與 COM vtable 手動(dòng)布局如ICLRRuntimeInfoVtbl的逐方法指針定義見 iclrruntimeinfo.go在引入生產(chǎn)環(huán)境前應(yīng)充分評估其穩(wěn)定性邊界并在植入體側(cè)如 Sliver 所做的那樣以互斥鎖、程序集緩存與獨(dú)立的 stdout/stderr 緩沖來隔離其不確定性。贊分享網(wǎng)絡(luò)安全【免費(fèi)下載鏈接】sliverAdversary Emulation Framework項(xiàng)目地址https://gitcode.com/gh_mirrors/sl/sliver點(diǎn)擊查看免費(fèi)下載相關(guān)推薦從 v1.0.0 到 v1.0.3Sliver 內(nèi)置 go-clr 庫的版本演進(jìn)與 .NET 內(nèi)存執(zhí)行實(shí)現(xiàn)解析從 v1.0.0 到 v1.0.3Sliver 內(nèi)置 go clr 庫的版本演進(jìn)與 .NET 內(nèi)存執(zhí)行實(shí)現(xiàn)解析 本文以 Sliver 倉庫中內(nèi)置的 githu網(wǎng)絡(luò)安全Podman 中的 go-zfs 庫演進(jìn)全解析從 v1.0.0 到 v3.0.0 的 ZFS 管理能力變遷Podman 中的 go zfs 庫演進(jìn)全解析從 v1.0.0 到 v3.0.0 的 ZFS 管理能力變遷 go zfs github.com/mistif容器運(yùn)行時(shí)云原生CLI深入 go-clr在 Go 進(jìn)程中托管 .NET CLR 并執(zhí)行 DLL 與內(nèi)存程序集的技術(shù)剖析深入 go clr在 Go 進(jìn)程中托管 .NET CLR 并執(zhí)行 DLL 與內(nèi)存程序集的技術(shù)剖析 本文基于 Sliver 倉庫內(nèi) vendored 的 git網(wǎng)絡(luò)安全創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考