據(jù)流/交換數(shù)據(jù)流(ADS)從原理到排查清除全攻略)
如果你做過Windows環(huán)境下的系統(tǒng)維護(hù)或者安全排查大概率聽過“備用數(shù)據(jù)流/交換數(shù)據(jù)流”這個(gè)名字。它聽起來像某種網(wǎng)絡(luò)協(xié)議實(shí)際上卻是NTFS文件系統(tǒng)里一個(gè)低調(diào)到容易被忽略、但影響面極大的文件附加機(jī)制。我第一次真正意識(shí)到它的存在是因?yàn)橐粋€(gè)很奇怪的現(xiàn)象某個(gè)文件用資源管理器看只有幾KB磁盤占用卻顯示幾十MB而且里面還殘留著一段不知道哪來的文本。后來才知道那是文件背后掛著一條看不見的“備用數(shù)據(jù)流”。這玩意兒到底是干什么的為什么叫備用數(shù)據(jù)流又叫交換數(shù)據(jù)流它有什么實(shí)際用途又為什么會(huì)成為惡意代碼的藏身之所這篇文章我會(huì)從原理講到實(shí)操從命令演示講到排查思路把關(guān)于備用數(shù)據(jù)流/交換數(shù)據(jù)流英文簡稱ADS的那些坑和技巧一次性說清楚。適合正在做Windows運(yùn)維、安全分析、取證排查的同行也適合剛接觸文件系統(tǒng)、想搞明白“文件里怎么還藏了文件”的新手。1. 先用大白話講清楚備用數(shù)據(jù)流/交換數(shù)據(jù)流到底是什么1.1 NTFS的“文件套文件”設(shè)計(jì)要理解備用數(shù)據(jù)流先得理解NTFS文件系統(tǒng)的一個(gè)基礎(chǔ)設(shè)計(jì)在NTFS里一個(gè)文件并不是只有“一份數(shù)據(jù)”而是由一個(gè)“主數(shù)據(jù)流”和若干個(gè)“備用數(shù)據(jù)流”組成。主數(shù)據(jù)流就是你在資源管理器里正常讀寫、正??吹降哪遣糠謨?nèi)容備用數(shù)據(jù)流則是依附在同一個(gè)文件條目下、但獨(dú)立于主數(shù)據(jù)流之外的附加數(shù)據(jù)塊。我用一個(gè)生活化的類比來解釋。主數(shù)據(jù)流相當(dāng)于一本公開的書書頁上的內(nèi)容每個(gè)人打開都能看到備用數(shù)據(jù)流則是夾在書脊夾層里的小紙條你用正常翻書的動(dòng)作永遠(yuǎn)看不到它但它確確實(shí)實(shí)跟著這本書一起存在。只有當(dāng)你知道這張紙條的夾層位置并且用專門的工具去拆開它才能看到里面的內(nèi)容。更關(guān)鍵的是你復(fù)印、壓縮、打包這本“書”的時(shí)候普通操作往往只復(fù)制了書頁夾層里的小紙條可能原封不動(dòng)跟著走也可能直接被丟棄取決于你用的“復(fù)印機(jī)”是誰家產(chǎn)的。這就是為什么很多人在拷貝、備份、壓縮文件時(shí)會(huì)莫名其妙發(fā)現(xiàn)數(shù)據(jù)“變了樣”或者“多了一段東西”。因?yàn)樵贜TFS盤上一個(gè)文件的完整數(shù)據(jù)是“主數(shù)據(jù)流所有備用數(shù)據(jù)流”的總和而很多常規(guī)工具只處理了主數(shù)據(jù)流卻把備用數(shù)據(jù)流忽略掉了。1.2 名字緣起備用、交換、附加流都是它“備用數(shù)據(jù)流”和“交換數(shù)據(jù)流”這兩個(gè)中文叫法其實(shí)是同一個(gè)概念在不同語境下的譯法差異。英文里的標(biāo)準(zhǔn)說法是Alternate Data Streams縮寫ADS直譯是“備用數(shù)據(jù)流”或者“附加數(shù)據(jù)流”。有些中文技術(shù)資料把它翻成“交換數(shù)據(jù)流”大概是因?yàn)樵缙贜TFS設(shè)計(jì)參考了蘋果文件系統(tǒng)中“數(shù)據(jù)叉”和“資源叉”互相配合的思路強(qiáng)調(diào)主數(shù)據(jù)流和附加數(shù)據(jù)流之間可以按需切換使用。反正你在不同文檔里看到“備用數(shù)據(jù)流”“交換數(shù)據(jù)流”“附屬數(shù)據(jù)流”“附加流”指的都是同一個(gè)東西NTFS下依附于文件或目錄、獨(dú)立于主數(shù)據(jù)流之外的數(shù)據(jù)通道。搞清楚這一點(diǎn)很重要因?yàn)楹芏嗳嗽诰W(wǎng)上搜“如何查看交換數(shù)據(jù)流”搜出來的卻是另一個(gè)操作系統(tǒng)或者網(wǎng)絡(luò)設(shè)備上的概念容易越查越偏。記住一個(gè)關(guān)鍵詞ADSNTFS。后續(xù)所有排查和操作都圍繞這兩個(gè)詞展開。1.3 它到底有什么用從文件系統(tǒng)設(shè)計(jì)初衷說起NTFS設(shè)計(jì)備用數(shù)據(jù)流最初是為了兼容其它文件系統(tǒng)的文件屬性。在早期Windows的服務(wù)器場景里文件和它附帶的元信息比如作者、關(guān)鍵詞、圖標(biāo)位置、摘要信息是希望綁定在一起的。其它系統(tǒng)可能用一個(gè)單獨(dú)數(shù)據(jù)庫來存NTFS則直接把這類信息塞進(jìn)文件條目下的附加流里好處是只要文件在NTFS盤上這些附屬性息就跟文件形影不離。當(dāng)時(shí)的典型應(yīng)用包括在文件上掛載自定義摘要、為文檔保存縮略圖、為目錄設(shè)置自定義圖標(biāo)、存放下載來源標(biāo)記等。你從網(wǎng)上下載一個(gè)帶標(biāo)記的文件資源管理器彈窗提示“此文件來自其他計(jì)算機(jī)可能被阻止”這個(gè)標(biāo)記就是存進(jìn)了文件的備用數(shù)據(jù)流Zone.Identifier里。你看到的不是“警告框背后的邏輯”而是備用數(shù)據(jù)流在生活里的一個(gè)最直接體現(xiàn)。2. 動(dòng)手試一遍命令行下的完整操作流程2.1 三分鐘上手用記事本給文件加一個(gè)“隱藏尾巴”理論看再多都不如自己動(dòng)手跑一遍。下面這組操作都是在Windows命令行cmd和PowerShell里就能完成的不需要裝任何額外工具。請注意你得先在NTFS分區(qū)上操作C盤默認(rèn)就是NTFS問題不大。第一步創(chuàng)建備個(gè)普通文件。打開cmd進(jìn)入某個(gè)測試目錄輸入echo 這是公開內(nèi)容 demo.txt此時(shí)demo.txt里就是一行“這是公開內(nèi)容”。接下來給它加一條備用數(shù)據(jù)流echo 這是隱藏的附加內(nèi)容 demo.txt:secret.txt注意命令里的文件名格式是demo.txt:secret.txt冒號(hào)后面的“secret.txt”就是這個(gè)備用數(shù)據(jù)流的名字。執(zhí)行完再輸入dir demo.txt你看到的文件大小仍然是“這是公開內(nèi)容”那一行的大小extra內(nèi)容并沒有體現(xiàn)在資源管理器或dir常規(guī)輸出里。剛才那行隱藏內(nèi)容就落在了demo.txt文件的備用數(shù)據(jù)流里。這時(shí)候你可以直接用記事本訪問它notepad demo.txt:secret.txt記事本會(huì)直接打開這個(gè)“看不見的流”顯示“這是隱藏的附加內(nèi)容”。如果你在資源管理器里雙擊demo.txt打開的則只有“這是公開內(nèi)容”。兩者看著像兩個(gè)文件其實(shí)屬于同一個(gè)文件條目。2.2 查看、修改、刪除流的全套命令查看一個(gè)文件有沒有掛備用數(shù)據(jù)流用dir的/R參數(shù)最方便dir /r demo.txt輸出里會(huì)多出一行demo.txt:secret.txt這就是額外存在的依據(jù)。如果文件比較多可以用dir /r對(duì)整個(gè)目錄掃一遍dir /rPowerShell用戶可以用Get-Item加Stream參數(shù)看得更清楚Get-Item -Path demo.txt -Stream *返回結(jié)果會(huì)列出流名稱和長度。modify流的操作也簡單——直接重新向同一個(gè)流名寫入即可echo 修改后的隱藏內(nèi)容 demo.txt:secret.txt刪除這條流用PowerShell的Remove-Item指定Stream參數(shù)Remove-Item -Path demo.txt -Stream secret.txt我用的時(shí)候更習(xí)慣先用Get-Item -Stream *確認(rèn)流名再執(zhí)行刪除防止誤刪了系統(tǒng)級(jí)流比如標(biāo)記下載來源的Zone.Identifier刪錯(cuò)會(huì)有一些后遺癥這點(diǎn)后面會(huì)細(xì)說。2.3 為什么必須用NTFS盤操作FAT/exFAT直接翻車為什么強(qiáng)調(diào)必須在NTFS分區(qū)上測試因?yàn)镕AT32、exFAT這些文件系統(tǒng)根本不支持備用數(shù)據(jù)流。如果你把一個(gè)帶ADS的文件復(fù)制到U盤exFAT系統(tǒng)會(huì)只復(fù)制主數(shù)據(jù)流把備用數(shù)據(jù)流直接丟掉而且通常不會(huì)給出任何明確提示。反過來你在U盤上想執(zhí)行echo xxx test.txt:secret.txt大概率會(huì)報(bào)“文件名、目錄名或卷標(biāo)語法不正確”之類的錯(cuò)誤。這個(gè)特性既是坑也是線索??釉谟谀阋詾榘颜麄€(gè)文件完整帶走了其實(shí)丟了一部分?jǐn)?shù)據(jù)對(duì)方收件人可能毫無察覺。線索在于如果一個(gè)文件在拷貝到非NTFS盤后變“干凈”了、變“小”了你就能反推它原來可能掛過備用數(shù)據(jù)流。安全排查里我經(jīng)常用這個(gè)現(xiàn)象判斷某個(gè)文件是否在NTFS環(huán)境里被人動(dòng)過手腳。3. 這些“隱藏通道”在真實(shí)場景里都干了什么3.1 你每天都在用的瀏覽器下載標(biāo)記普通用戶接觸最多的備用數(shù)據(jù)流其實(shí)是瀏覽器給下載文件打的“來源標(biāo)記”。當(dāng)你用瀏覽器從網(wǎng)上下載一個(gè)exe或文檔到Windows系統(tǒng)盤默認(rèn)情況下會(huì)寫入一條名為Zone.Identifier的備用數(shù)據(jù)流里面記錄著下載來源區(qū)域ID。用記事本打開看一下長什么樣notepad 下載的文件.exe:Zone.Identifier通常你會(huì)看到類似這樣兩行[ZoneTransfer] ZoneId3ZoneId3表示文件來自Internet區(qū)域。系統(tǒng)就是靠這條流來判斷“這是不是外部下載的文件”進(jìn)而決定是否彈出安全警告。有些下載工具會(huì)把ZoneId0本機(jī)區(qū)域或者直接不寫這條流這樣文件就不會(huì)觸發(fā)“已阻止”提示。安全防護(hù)軟件在排查可疑文件時(shí)也會(huì)特意讀取這條流來確認(rèn)來源這是備用數(shù)據(jù)流在數(shù)字取證里的經(jīng)典用途之一。3.2 合法的系統(tǒng)級(jí)用途元數(shù)據(jù)與文件分類除了下載標(biāo)記Windows和不少軟件也在用備用數(shù)據(jù)流存放各類附屬數(shù)據(jù)。比如某些文件管理器會(huì)把目錄的自定義背景、排序方式、分組屬性塞進(jìn)目錄的ADS里老版本的Office程序曾把文檔摘要信息寫入ADSWindows搜索索引、磁盤配額等機(jī)制也依賴流屬性來標(biāo)記文件狀態(tài)。在企業(yè)文件服務(wù)器上還有一類常見操作是用備用數(shù)據(jù)流掛“標(biāo)簽”。管理員不改變文件內(nèi)容本身而是往某個(gè)文件上掛一條名為dept:finance的流里面寫個(gè)部門編號(hào)然后靠腳本按流內(nèi)容做分發(fā)、歸檔、權(quán)限復(fù)核。這樣做的好處是文件外部形態(tài)不變卻能攜帶額外的業(yè)務(wù)信息。實(shí)現(xiàn)起來就是前文那幾條命令的組合十幾行腳本就能做一個(gè)輕量的文件標(biāo)簽系統(tǒng)。3.3 惡意軟件視角隱蔽駐留與對(duì)抗排查備用數(shù)據(jù)流最讓人警惕的使用方式是作為惡意代碼的“隱蔽藏身處”。由于常規(guī)資源管理器、dir命令不顯示流文件大小攻擊者可以把遠(yuǎn)控木馬、惡意腳本、勒索說明塞進(jìn)一個(gè)看似正常的文件的備用數(shù)據(jù)流里。受害者看到的是一個(gè)正常的PDF或者圖標(biāo)文件實(shí)際上那個(gè)PDF文件背后可能掛著一條存在整段惡意代碼的流。歷史上出現(xiàn)過不少真實(shí)樣本通過腳本啟動(dòng)器調(diào)用wscript.exe去執(zhí)行某個(gè)xxx.txt:payload.vbs流或者把一個(gè)程序的完整PE文件寫入備用數(shù)據(jù)流再用一個(gè)小加載器從流中讀取并注入進(jìn)程。平時(shí)我們做應(yīng)急響應(yīng)的時(shí)候排查清單里一定有“檢查可疑目錄下所有文件的ADS”這一步否則很容易漏掉關(guān)鍵的惡意載荷。所以我在這里明確給大家劃一條線了解備用數(shù)據(jù)流的隱藏能力是為了防御、排查和取證不是為了藏東西。真要傳文件老老實(shí)實(shí)用正常渠道針對(duì)安全從業(yè)者理解攻擊手法才能更有效地定位和清除這個(gè)認(rèn)知課很值得補(bǔ)上。4. 檢測與清理如何揪出文件背后的影子4.1 系統(tǒng)自帶命令從dir /r到PowerShell枚舉面對(duì)一個(gè)目錄怎么快速確認(rèn)里面哪些文件掛了備用數(shù)據(jù)流最簡單的是用dir /r /s遞歸掃描dir /r /s它會(huì)列出所有文件和對(duì)應(yīng)的流名。如果你想要更機(jī)器可讀的輸出PowerShell可以這樣寫Get-ChildItem -Path C:\某目錄 -Recurse -File | Get-Item -Stream * | Where-Object {$_.Stream -ne :$DATA}這段的意思是把某個(gè)目錄下所有文件的流信息都列出來只保留非默認(rèn)主數(shù)據(jù)流非:$DATA的記錄。執(zhí)行后你就能看到每個(gè)文件掛著哪些流、流有多大。還有一種更徹底的檢查方式對(duì)比主數(shù)據(jù)流大小和磁盤占用。如果一個(gè)文件顯示的內(nèi)容只有1KB但屬性里磁盤占用明顯變大那就要警惕是不是有備用數(shù)據(jù)流在膨脹磁盤空間。雖然資源管理器通常把主數(shù)據(jù)流大小顯示成文件大小但碰到異常膨脹的文件用dir /r直接核對(duì)流列表是最快的初篩手段。4.2 流級(jí)別的完整導(dǎo)出與復(fù)制技巧如果你想完整復(fù)制一個(gè)帶備用數(shù)據(jù)流的文件到另一個(gè)NTFS位置保留所有流不能用普通復(fù)制粘貼。普通復(fù)制在大部分情況下會(huì)把Zone.Identifier流也復(fù)制過去但其它自定義流不一定能保住。最穩(wěn)的做法是使用系統(tǒng)備份接口或者壓縮工具的全流支持模式。一個(gè)輕量方案是直接構(gòu)造一個(gè)fsutil的硬鏈接或使用xcopy的某些模式但更常見的是用PowerShell的Copy-Item加流參數(shù)視系統(tǒng)版本而定。如果只是臨時(shí)查看某個(gè)流的內(nèi)容直接Get-Content -Stream就好Get-Content -Path demo.txt -Stream secret.txt要是懷疑某個(gè)流里面其實(shí)是一個(gè)可執(zhí)行文件別直接雙擊運(yùn)行你應(yīng)該把它單獨(dú)導(dǎo)出成一個(gè)文件再分析cmd /c type C:\test\demo.txt:payload.exe C:\test\payload.exe然后你就可以對(duì)這個(gè)導(dǎo)出的文件做常規(guī)的靜態(tài)分析、殺毒掃描或哈希計(jì)算。這是取證里很實(shí)用的操作建議存進(jìn)自己的常用命令清單。4.3 清除與防護(hù)的幾條實(shí)用規(guī)則確認(rèn)了不需要的備用數(shù)據(jù)流清理方法前面提過用Remove-Item -Stream就能刪。但防護(hù)和清理有幾個(gè)關(guān)鍵規(guī)則要刻在腦子里。第一系統(tǒng)級(jí)流不要亂刪。比如Zone.Identifier刪掉它文件就不再觸發(fā)下載警告但這會(huì)讓安全提示失效。很多用戶為了“不再彈窗”把所有下載文件的Zone.Identifier都刪了結(jié)果惡意文件下載后也不會(huì)再有任何提示得不償失。第二刪除流和刪除文件是兩回事。直接刪掉整個(gè)文件所有備用數(shù)據(jù)流自然都沒了。如果只想清理某個(gè)文件的附加流一定要把路徑和流名寫完整不然很容易誤傷別的文件。第三很多備份軟件默認(rèn)只備份主數(shù)據(jù)流。如果你的重要文件依賴備用數(shù)據(jù)流存業(yè)務(wù)標(biāo)簽備份前一定要確認(rèn)備份工具是否完整支持NTFS流。否則恢復(fù)出來的文件表面上還在標(biāo)簽信息已經(jīng)靜默丟失后續(xù)歸檔流程會(huì)直接踩坑。5. 常見坑與排查思路實(shí)錄5.1 備份、壓縮、傳輸導(dǎo)致ADS丟失的連鎖效應(yīng)我最開始在真實(shí)環(huán)境里注意到ADS就是因?yàn)橐淮蝹浞荨皝G數(shù)據(jù)”事故。某天同事反映備份的文檔還原后缺少了部分屬性信息文件內(nèi)容和大小看起來都正常但在某個(gè)業(yè)務(wù)系統(tǒng)里就是不被識(shí)別。查了很久才發(fā)現(xiàn)問題出在備份工具沒有完整復(fù)制NTFS備用數(shù)據(jù)流。這類問題有幾個(gè)典型表現(xiàn)文件從NTFS盤復(fù)制到FAT/exFAT盤再復(fù)制回來、用壓縮軟件打包后解壓、上傳到某些網(wǎng)盤再下載、通過郵件附件發(fā)送。這些路徑都可能只保留主數(shù)據(jù)流。如果你發(fā)現(xiàn)某個(gè)文件在傳輸后“少了什么”除了檢查文件內(nèi)容本身還得檢查它的流列表。我遇到過不少聲稱“文件損壞”的工單最終定位都是ADS靜默丟失文件主體根本沒壞。5.2 壓縮盤與加密盤另一層麻煩NTFS支持文件壓縮和加密這兩者與備用數(shù)據(jù)流混在一起時(shí)會(huì)引入更多坑。啟用壓縮屬性的文件系統(tǒng)會(huì)把流數(shù)據(jù)壓縮存儲(chǔ)你在流里讀到的是解壓后的內(nèi)容。如果文件還被EFS加密其他用戶或者系統(tǒng)賬戶試圖讀取備用數(shù)據(jù)流時(shí)可能會(huì)直接失敗或只能讀到加密亂碼因?yàn)榧用苁敲荑€級(jí)別的不是簡單加層殼。實(shí)際運(yùn)維中要對(duì)掛有敏感標(biāo)簽流的文件做EFS加密得先確認(rèn)應(yīng)用讀取流的方式是否兼容加密文件。尤其是一些腳本可能在普通用戶上下文里能正常讀取流但一旦文件被加密、切換到服務(wù)賬戶處理時(shí)讀取就直接報(bào)錯(cuò)或顯示亂碼。排查時(shí)要看的不只是文件本身的加密狀態(tài)還要看調(diào)用方的上下文環(huán)境。5.3 誤以為文件已被刪除但程序仍占用另一個(gè)常見坑是你刪除了一個(gè)文件卻發(fā)現(xiàn)某個(gè)進(jìn)程仍然能訪問到內(nèi)容。這在很多時(shí)候不是“文件沒刪掉”而是有程序打開了這個(gè)文件或文件某個(gè)流形成了占用。備用數(shù)據(jù)流會(huì)讓這個(gè)現(xiàn)象更隱蔽因?yàn)槟憧吹劫Y源管理器里文件已經(jīng)消失但另一個(gè)進(jìn)程可能正握著某個(gè)路徑:某個(gè)流的句柄。排查方法是用系統(tǒng)自帶句柄查詢工具或者PowerShell查看哪些進(jìn)程打開了該路徑。在真正刪不掉文件時(shí)先別反復(fù)強(qiáng)殺進(jìn)程用openfiles或相關(guān)命令定位占用源再?zèng)Q定是停服務(wù)還是等句柄釋放。這個(gè)思路同樣適用于目錄被掛流的情況——目錄的ADS可能存儲(chǔ)自定義視圖信息刪除目錄時(shí)報(bào)“正在使用”多半就是桌面資源管理器開了這個(gè)目錄并讀取了視圖流。5.4 殺毒軟件與流掃描的特殊關(guān)系主流殺毒軟件對(duì)NTFS的備用數(shù)據(jù)流是有一整套掃描邏輯的。它們不只查主數(shù)據(jù)流也會(huì)按策略掃描Zone.Identifier、檢查已知惡意流名、監(jiān)測常見隱蔽駐留路徑。但在實(shí)際測試中我發(fā)現(xiàn)不同殺軟對(duì)自定義流的掃描深度差異很大。有些人會(huì)把惡意載荷放在隨機(jī)命名的流里通過混淆流名逃避部分靜態(tài)掃描。這里想提醒的是不要以為裝了殺毒軟件就萬事大吉尤其是做滲透測試或者應(yīng)急響應(yīng)的人要養(yǎng)成手動(dòng)檢查ADS的習(xí)慣。殺軟提供的是通用防護(hù)針對(duì)特定隱藏手法的深入排查還是得靠人工和腳本。反過來如果你在測試自己的工具鏈避免用真實(shí)惡意代碼去試殺軟用無害的標(biāo)記文本即可這也是我自己實(shí)踐中的一條紅線。寫在后面我的一些實(shí)操體會(huì)備用數(shù)據(jù)流/交換數(shù)據(jù)流這個(gè)主題平時(shí)不顯山不露水一旦遇到相關(guān)故障或者安全事件卻是能把人卡住很久的暗坑。我個(gè)人做系統(tǒng)維護(hù)和排查時(shí)最常提醒自己的三句話看文件大小別只看資源管理器備份傳輸前先確認(rèn)工具認(rèn)不認(rèn)NTFS流見到異常體積膨脹的文件第一時(shí)間用dir /r和PowerShell流枚舉掃一眼。另外分享一個(gè)小技巧如果對(duì)某個(gè)目錄的文件完整性沒把握可以在命令行里做一次流清單快照保存成文本隔段時(shí)間再跑一次對(duì)比。流的新增和刪除都會(huì)在清單里留下痕跡這在沒有完整審計(jì)系統(tǒng)的普通服務(wù)器上也算是一種輕量級(jí)的狀態(tài)追蹤方案。希望這篇關(guān)于備用數(shù)據(jù)流/交換數(shù)據(jù)流的實(shí)操復(fù)盤能幫你少走一些我走過的彎路。你手上要是也遇到過跟NTFS流相關(guān)的怪問題試試從“文件背后還掛著別的東西”這個(gè)角度去查大概率會(huì)有新發(fā)現(xiàn)。