:從POC編寫到批量檢測全流程)
很多做安全測試的朋友應(yīng)該都有過這種經(jīng)歷甲方給了一個挺大的IP段說“幫忙看看有沒有暴露的老設(shè)備”你總不能一臺臺手工登錄去試。這時候有個趁手的批量檢測工具就特別重要。Nuclei作為一款模板化的漏洞掃描器正好能解決這個痛點。這篇文章我就拿“天融信TOPSEC命令執(zhí)行漏洞”這個具體場景完整走一遍從POC編寫到批量檢測的流程包括里面容易踩的坑和排查思路希望對剛接觸Nuclei或者正在做類似工作的朋友有點幫助。1. 項目背景與檢測思路拆解1.1 天融信TOPSEC設(shè)備為什么值得重點關(guān)注天融信的TOPSEC系列防火墻在國內(nèi)政企內(nèi)網(wǎng)里存量非常大不少單位用了很多年固件版本常年不更新。這類設(shè)備一旦爆出遠程命令執(zhí)行漏洞影響面通常很廣而且因為設(shè)備本身就是為了“隔離內(nèi)外網(wǎng)”而存在的被攻破后攻擊者拿到的是內(nèi)網(wǎng)的一個穩(wěn)定跳板風(fēng)險等級相當(dāng)高。我平時做授權(quán)測試時遇到TOPSEC設(shè)備會格外留意幾個點第一設(shè)備的Web管理面板是否直接暴露在非信任網(wǎng)絡(luò)第二固件版本是否老舊是否已經(jīng)出現(xiàn)在公開漏洞庫中第三是否存在一些歷史遺留的默認口令或調(diào)試接口。命令執(zhí)行類漏洞是這里面最嚴(yán)重的一種它能直接讓測試人員從“能訪問管理頁”變成“能執(zhí)行系統(tǒng)命令”接下來無論是讀取配置文件還是做內(nèi)網(wǎng)跳板都變得十分順滑。1.2 Nuclei這類模板化掃描器好在哪里Nuclei是ProjectDiscovery團隊開源的漏洞掃描器核心設(shè)計思路是“用YAML模板描述漏洞檢測邏輯”掃描引擎負責(zé)并發(fā)發(fā)起請求、匹配響應(yīng)、提取結(jié)果。社區(qū)維護著非常龐大的模板庫很多公開漏洞在PoC公布后幾天內(nèi)就會有對應(yīng)的nuclei模板。相比直接用Python腳本或Burp Suite的IntruderNuclei有幾個明顯優(yōu)勢并發(fā)效率高默認就能跑幾十上百個并發(fā)掃一個大網(wǎng)段不用等太久。模板可復(fù)用一個寫好的模板之后遇到同類設(shè)備隨時復(fù)用。結(jié)果結(jié)構(gòu)化輸出JSON、Markdown等格式方便后續(xù)對接工單或報告。內(nèi)置多種協(xié)議支持除了HTTP還能寫DNS、TCP等協(xié)議的檢測邏輯。這次要做的TOPSEC命令執(zhí)行檢測本質(zhì)上就是構(gòu)造一個特殊的HTTP請求看設(shè)備是否會在響應(yīng)中回顯命令執(zhí)行的結(jié)果。Nuclei的HTTP請求模板能力足夠覆蓋這個場景而且不需要額外寫復(fù)雜的Python腳本。1.3 漏洞檢測的整體流程拆解整個檢測過程可以分成四步走確認目標(biāo)設(shè)備確實是天融信TOPSEC避免誤報。構(gòu)造命令執(zhí)行探測請求通過響應(yīng)特征判斷漏洞是否存在。用Nuclei模板批量跑目標(biāo)IP段收集存活且存在漏洞的設(shè)備。對掃出來的結(jié)果做人工復(fù)核確認不是誤報。這里我想強調(diào)一點批量掃描工具只是輔助最終確認漏洞可利用性還是得靠手工驗證。Nuclei的職責(zé)是幫你把“可能有問題”的設(shè)備從成千上萬個IP里篩出來而不是替你下結(jié)論。2. Nuclei與POC設(shè)計核心邏輯2.1 YAML模板的基礎(chǔ)結(jié)構(gòu)先看一個Nuclei模板最基礎(chǔ)的結(jié)構(gòu)長什么樣后面寫POC都是在這個框架上擴展的id: tosec-rce-check info: name: TianRong TOPSEC RCE Detection author: your-name severity: critical description: Detects command execution vulnerability in TOPSEC devices tags: tosec, rce, firewall requests: - method: GET path: - {{BaseURL}}/xxx matchers: - type: word words: - uidid字段是這個模板的唯一標(biāo)識info區(qū)塊存漏洞元數(shù)據(jù)requests區(qū)塊定義具體的檢測請求。模板寫完后把文件放到nuclei的templates目錄下的某個子目錄里比如http/cves/執(zhí)行掃描時用-tags參數(shù)就能篩出來。2.2 命令執(zhí)行漏洞POC的常見設(shè)計思路命令執(zhí)行類漏洞的POC設(shè)計核心是解決一個問題怎么在不直接進行破壞性操作的前提下可靠地判斷命令確實被執(zhí)行了。最常用的思路是讓設(shè)備執(zhí)行一個無害且結(jié)果可回顯的命令比如id、whoami、cat /etc/passwd等。如果設(shè)備把命令輸出直接拼在HTTP響應(yīng)里那恭喜你這是一個教科書級的回顯型命令執(zhí)行。以TOPSEC的某類接口為例漏洞點常出現(xiàn)在一些調(diào)試用或維護用的功能參數(shù)上。比如某個參數(shù)會把用戶輸入直接拼進系統(tǒng)命令中執(zhí)行檢測時就能構(gòu)造類似這樣的請求GET /tosec/cmd.action?cmdid HTTP/1.1 Host: target如果響應(yīng)體里出現(xiàn)了uid這樣的字符串基本可以確定命令執(zhí)行漏洞存在。當(dāng)然實際代碼中的參數(shù)名和路徑需要根據(jù)具體固件版本調(diào)整我這里就不寫具體設(shè)備的細節(jié)了避免被惡意利用。思路是通用的拿到一個命令執(zhí)行漏洞第一步就是找能在響應(yīng)中回顯命令結(jié)果的最小驗證請求。2.3 為什么選擇Nuclei DSL而不是普通正則匹配Nuclei的DSL領(lǐng)域特定語言提供了比單純正則匹配更靈活的響應(yīng)判斷能力。比如你可以用contains(body, uid)這樣的表達式還能組合多個條件做AND/OR判斷。寫復(fù)雜檢測邏輯時DSL的價值會體現(xiàn)得更明顯。比如說你檢測一個漏洞需要同時滿足“響應(yīng)狀態(tài)碼是200”和“響應(yīng)體中包含特定字符串”用純正則寫起來比較繞但用Nuclei DSL就是matchers-condition: and matchers: - type: status status: - 200 - type: word words: - uid這樣邏輯一目了然后續(xù)維護也方便。另外Nuclei的DSL還支持extractors能從響應(yīng)中提取動態(tài)的內(nèi)容比如提取命令執(zhí)行的結(jié)果保存下來方便人工復(fù)核。3. 實操配置與POC編寫詳解3.1 環(huán)境準(zhǔn)備與工具安裝Nuclei的安裝方式很簡單有Go環(huán)境的話一行命令go install github.com/projectdiscovery/nuclei/v3/cmd/nucleilatest沒有Go環(huán)境的話直接去GitHub的Releases頁面下載編譯好的二進制文件扔到/usr/local/bin下就行。Windows環(huán)境就用exe文件命令用法一樣。裝完之后更新一下模板庫nuclei -update-templates這個是必須做的因為社區(qū)模板每天都在更新。當(dāng)然我們這種自定義POC屬于私有模板不會在社區(qū)模板庫里需要單獨管理。3.2 自定義POC模板的完整寫法下面我給一個實際可用的模板框架假設(shè)檢測路徑是/tosec/cmd.action參數(shù)是cmd命令執(zhí)行結(jié)果會直接回顯到響應(yīng)體中實際場景需根據(jù)目標(biāo)版本調(diào)整但方法完全一致id: tosec-cmd-exec info: name: TianRong TOPSEC Command Execution author: security-researcher severity: critical description: Detects command execution vulnerability in TOPSEC devices by injecting id command tags: tosec,rce,firewall,cnvd requests: - method: GET path: - {{BaseURL}}/tosec/cmd.action?cmdid headers: User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 matchers-condition: and matchers: - type: word part: body words: - uid - gid condition: and - type: status status: - 200 extractors: - type: regex part: body regex: - uid\\d\\([^)]\\)這個模板做的事情很簡單發(fā)一個GET請求往cmd參數(shù)里塞入id命令然后匹配響應(yīng)體里是否同時出現(xiàn)uid和gid。如果返回200同時包含這兩個特征就認為存在命令執(zhí)行漏洞。3.3 編寫POC時容易忽略的細節(jié)寫這個模板時有幾個細節(jié)值得特別注意路徑大小寫某類設(shè)備的Web服務(wù)對路徑大小寫敏感最好在模板里明確寫出實際測試過可用的路徑。如果把/tosec/cmd.action寫成/Tosec/Cmd.action遇到區(qū)分大小寫的服務(wù)就直接404了。URL編碼如果命令里有空格或特殊字符需要在請求里做URL編碼。id命令本身不含空格但如果后續(xù)想執(zhí)行cat /etc/passwd空格在URL里要么寫成%20要么寫成不同設(shè)備對這兩種編碼的接受程度不一樣建議在手工驗證時都試一遍。請求頻率控制Nuclei默認并發(fā)不算低批量掃大量IP時如果目標(biāo)設(shè)備性能較差很多老防火墻的CPU和內(nèi)存都很感人容易把設(shè)備掃掛。后面會講到怎么控制速率。指紋確認批量檢測時大量非TOPSEC設(shè)備也會出現(xiàn)在目標(biāo)列表里如果直接發(fā)送POC請求一方面會產(chǎn)生大量無意義的告警日志另一方面可能觸發(fā)一些安全設(shè)備的封禁策略。建議先加一個指紋匹配步驟確認是TOPSEC設(shè)備再發(fā)命令執(zhí)行探測請求。3.4 指紋預(yù)篩的設(shè)計方法想加指紋預(yù)篩最簡單的方式是利用Nuclei的stop-at-first-match配合多級請求?;蛘吒苯右稽c先做一次指紋請求用internal變量把結(jié)果傳給后面的請求判斷。Nuclei支持在同一個模板里寫多個請求后面的請求可以引用前面請求的響應(yīng)內(nèi)容比如提取到的關(guān)鍵字符串。思路大概是這樣的requests: - method: GET path: - {{BaseURL}}/ matchers: - type: word name: tosec_fingerprint words: - TOPSEC - 天融信 condition: or internal: true上面第一個請求先判斷首頁是否有TOPSEC相關(guān)指紋。如果匹配成功再走第二個請求做漏洞探測。不過實際寫起來有一個坑Nuclei的internal變量和請求間數(shù)據(jù)傳遞在復(fù)雜模板中不太好維護新手容易寫出“第一個請求的matcher沒生效第二個請求照樣發(fā)”的情況。這里我更推薦一個懶人做法分兩步跑。第一步用指紋模板掃描目標(biāo)列表篩出TOPSEC設(shè)備id: tosec-detect info: name: TOPSEC Device Fingerprint severity: info requests: - method: GET path: - {{BaseURL}}/ matchers: - type: word words: - TOPSEC - 天融信WebSec第二步把指紋命中的IP作為目標(biāo)用命令執(zhí)行檢測模板再掃一遍。這樣邏輯清楚排查問題也方便。4. 批量檢測執(zhí)行流程實錄4.1 目標(biāo)列表準(zhǔn)備批量檢測的第一步是準(zhǔn)備目標(biāo)列表。實際工作中目標(biāo)來源一般有三種甲方給的一個網(wǎng)段、資產(chǎn)測繪平臺導(dǎo)出的IP列表、或者子域解析出來的IP集合。無論來源是哪種建議先把列表做去重和存活探測。去重可以用sort -u存活探測可以用httpx這樣的工具cat targets_raw.txt | sort -u | httpx -silent -status-code -title -tech-detect -o alive_targets.txt這個命令會探測每個IP的HTTP服務(wù)是否存活并輸出狀態(tài)碼、頁面標(biāo)題和識別到的技術(shù)棧。這里有個小技巧別把所有IP都直接丟給nuclei掃先篩一次能節(jié)省大量時間也減少誤報源。有些地址根本不開Web服務(wù)掃了也是白掃。4.2 指紋預(yù)篩的執(zhí)行過程假設(shè)現(xiàn)在有一個存活目標(biāo)文件alive_targets.txt內(nèi)容是IP加端口格式大概是https://192.0.2.10:8443這種。先跑指紋模板nuclei -l alive_targets.txt -t tosec-detect.yaml -o tosec_ips.txt -jsonl -silent-jsonl參數(shù)會輸出JSON格式的結(jié)果方便后續(xù)腳本提取命中的IP。執(zhí)行完之后查看tosec_ips.txt里面應(yīng)該有所有指紋命中的目標(biāo)。這一步輸出的是“疑似TOPSEC設(shè)備”數(shù)量通常不會太多。如果一下子命中幾百上千個說明目標(biāo)網(wǎng)段里這種設(shè)備確實多后面命令執(zhí)行檢測會很熱鬧。4.3 漏洞檢測與結(jié)果輸出接下來用命令執(zhí)行模板掃指紋命中的目標(biāo)nuclei -l tosec_ips.txt -t tosec-cmd-exec.yaml -o rce_results.txt -jsonl -rate-limit 10 -c 5這里兩個參數(shù)要重點解釋一下-rate-limit 10限制每秒最多發(fā)10個請求。這樣設(shè)置是為了照顧老設(shè)備的承受能力。-c 5限制并發(fā)模板數(shù)也就是同時只有5個目標(biāo)在處理。有朋友可能會覺得這個速率是不是太慢了。我實際測過一批設(shè)備很多是用了五年以上的老防火墻Web服務(wù)模塊用的是很老的架構(gòu)并發(fā)稍微一高就無響應(yīng)了。與其掃崩了再重啟再補掃不如一開始就悠著點慢工出細活。執(zhí)行完之后屏幕上會高亮顯示命中的目標(biāo)同時結(jié)果寫入rce_results.txt。每一行是一個JSON對象包含目標(biāo)地址、模板ID、匹配到的詳細信息等。4.4 結(jié)果的人工復(fù)核與利用驗證這一步非常關(guān)鍵。Nuclei命中的結(jié)果不一定是真的漏洞有可能是某種特殊的頁面剛好包含uid這樣的字符串。所以需要拿命中的目標(biāo)再做一次人工復(fù)核。我的標(biāo)準(zhǔn)操作是瀏覽器訪問目標(biāo)手工構(gòu)造POC請求觀察響應(yīng)。如果條件允許用一個無害命令驗證一下比如id或uname -a確認命令確實執(zhí)行了。如果能夠回顯命令結(jié)果再截圖留證。這里要特別提醒授權(quán)是硬前提。沒有目標(biāo)單位書面授權(quán)做任何掃描都是不合規(guī)的。別拿這個流程去掃不在授權(quán)范圍內(nèi)的目標(biāo)這是底線問題。5. 常見問題與排查技巧實錄5.1 模板不生效所有目標(biāo)都顯示未命中這種情況最常見的兩個原因一是路徑或參數(shù)名不對二是目標(biāo)設(shè)備固件版本已經(jīng)修復(fù)漏洞。排查路徑很簡單先手工發(fā)一個請求看看響應(yīng)如果手工請求都拿不到預(yù)期的特征說明POC里的路徑或者命令不對需要換一個接口或參數(shù)。如果是固件修復(fù)了那就是漏洞本身已不存在模板更新也沒用。另外還有一種情況設(shè)備把帶命令參數(shù)的請求交給WAF模塊攔截了返回了一個攔截頁面當(dāng)然匹配不到特征。這種要看看響應(yīng)頭或響應(yīng)體里是否有WAF的標(biāo)識比如Powered by XXX之類的。5.2 誤報率過高響應(yīng)里到處都有uid如果命中的結(jié)果里有一堆明顯不是TOPSEC的設(shè)備可能是指紋模板寫得太寬泛了。比如匹配詞里只寫了TOPSEC結(jié)果有些第三方登錄頁也用這個詞做了品牌展示。解決方法是增加指紋的權(quán)重比如同時匹配TOPSEC和頁面標(biāo)題中的特定關(guān)鍵字或者匹配特定的JS文件名、Logo路徑等。多條件AND匹配能篩掉大部分誤報。5.3 掃描過程中目標(biāo)設(shè)備無響應(yīng)批量掃TOPSEC設(shè)備時容易被掃掛的設(shè)備通常有這些特征老版本固件、開啟了很多不必要的服務(wù)、內(nèi)存較小。處理辦法是先確認設(shè)備是徹底宕機還是只是Web服務(wù)無響應(yīng)。如果只是Web服務(wù)重啟等幾分鐘再補測即可。如果是整機宕機可能需要聯(lián)系現(xiàn)場人員重啟。所以前面強調(diào)的-rate-limit和-c參數(shù)真的別不放在心上。還有一個經(jīng)驗盡量避免在業(yè)務(wù)高峰期掃描這類老設(shè)備。老防火墻除了做安全隔離往往還承載著一些歷史遺留的轉(zhuǎn)發(fā)任務(wù)你把它的Web服務(wù)掃掛了整個辦公網(wǎng)出口都可能受影響。5.4 Nuclei命令執(zhí)行模板無法提取命令結(jié)果有些場景下命令執(zhí)行漏洞確實存在但命令結(jié)果是異步返回的比如設(shè)備把命令放到后臺執(zhí)行結(jié)果寫到某個臨時文件里需要另一個接口去讀。這種時候Nuclei的單請求模板就不夠用了得用多請求模板。思路是第一個請求觸發(fā)命令執(zhí)行把輸出重定向到一個可訪問的靜態(tài)文件路徑下第二個請求去讀取這個文件匹配內(nèi)容。這類漏洞檢測模板寫起來復(fù)雜度高一些但也不是不能寫。Nuclei支持同模板內(nèi)定義多個requests按順序執(zhí)行前一個請求的輸出可以作為后一個請求的輸入。5.5 批量掃描結(jié)果如何高效整理掃出來的結(jié)果往往是純文本或JSON直接貼給甲方肯定不行。我一般用一個小腳本把JSON結(jié)果轉(zhuǎn)成表格cat rce_results.txt | jq -r [.host, .matched-at, .info.name] | tsv然后貼到表格軟件里按漏洞等級、目標(biāo)IP、URL、驗證狀態(tài)列成清單再補充修復(fù)建議。這個流程看起來很笨但確實是項目交付時最實用的做法。6. 經(jīng)驗總結(jié)與進一步擴展思路6.1 模板維護的小技巧Nuclei模板寫多了建議按設(shè)備廠商或漏洞類型建好目錄結(jié)構(gòu)比如templates/tosec/下面放這類設(shè)備的全部模板templates/rce/放通用命令執(zhí)行檢測模板。每次掃描前用nuclei -update-templates更新社區(qū)庫同時檢查一下自己的私有模板是否需要修正路徑或匹配詞。我習(xí)慣給每個模板文件寫清楚注釋包括漏洞編號、影響版本、檢測命令、驗證方法。Nuclei的YAML支持#注釋多寫點說明后續(xù)自己回來看也能快速想起來當(dāng)時為什么這么寫。6.2 從單點檢測到體系化復(fù)盤這次批量檢測做完后我建議把整個流程沉淀下來模板文件、命令、結(jié)果、人工復(fù)核記錄都整理成一套文檔。下次遇到同類設(shè)備直接復(fù)用這套流程就行。同時也別只盯著一個漏洞點TOPSEC這種設(shè)備往往同時存在多個已知漏洞比如未授權(quán)訪問、信息泄露等。把多個檢測模板組合起來跑一次掃描就能對目標(biāo)設(shè)備的安全狀況有個比較全面的認識。6.3 實際工作里我個人的幾個心得第一工具永遠是輔助手工驗證是必須項。再好的模板也有誤報也有抓不到的繞過姿勢。批量檢測幫你圈定范圍人工確認幫你下結(jié)論。第二速率控制是對目標(biāo)最基本的尊重。有些設(shè)備承載著生產(chǎn)業(yè)務(wù)你掃掛了它影響的可能是一整片網(wǎng)絡(luò)這個責(zé)任誰也擔(dān)不起。寧可多花點時間也別圖快。第三結(jié)果整理比掃描本身更花精力。真正靠譜的安全測試核心交付物是一份結(jié)論清晰、證據(jù)完整的報告。一個IP一個結(jié)論加截圖比打印十頁掃描原始輸出有價值得多。第四POC不等于武器庫。寫命令執(zhí)行檢測模板的目的是幫助確認風(fēng)險、推進修復(fù)而不是拿去做一些不該做的事情。合規(guī)這條線任何時候都不能松。這篇文章里寫的檢測方法在我自己處理過的不少授權(quán)測試項目中都驗證過流程是可復(fù)用的。整體思路也不只適用于TOPSEC換一個設(shè)備型號本質(zhì)上就是換一組指紋、換一條漏洞路徑、換一組匹配特征方法骨架完全一致。希望這份從POC編寫到批量落地的心得能幫你少走一些彎路。