Guidant AI 資安檢視總報告 · 連線清單
客戶用「網址」建檢測基準時,派掃描的代理程式自己去那個網址把規則包抓下來、交給檢測引擎執行的那條線。規則包是程式(InSpec 的 Ruby DSL),抓到什麼就跑什麼。雲端那頭已經先抓過一次、算了指紋(C19),但代理程式這頭沒有拿那個指紋來比對。這一頁回答:這條線怎麼連、哪些功能走它、它帶來什麼威脅、駭客怎麼打、我們要怎麼防、目前做到哪。
這一頁怎麼來的:DFD Level 0 把產品運作時的連線編成 C01~C25;STRIDE 六頁每一條問題都標了發生在哪幾條線。這裡以連線為單位整理。命中 1 條,另兩種手法依特性補(表內標明)。個別問題修了沒不在這頁講。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB'}}}%%
flowchart LR
ADMIN(["👤 客戶管理員<br/>填規則包網址"])
subgraph MAIN["🏠 主系統"]
API["guidant-api<br/>C19:抽取一次<br/>只收 https・擋內網・限 50MB<br/>算 sha256 落庫"]
end
URL(["🌐 規則包網址<br/>(客戶或第三方的伺服器)"])
MITM(["🕵 路徑上的人<br/>或網址的主人"])
subgraph AGENTBOX["🏢 客戶機房"]
direction TB
AG["evidence-agent<br/>收到 {url, sha256}"]
CINC["cinc-auditor<br/>自己抓 url・載入即執行<br/>(不比 sha256)"]
AG -- "url 原樣交出" --> CINC
end
HOSTS(["受檢主機(C23)"])
ADMIN -- "C02" --> API
API -. "C19 · https" .-> URL
API -- "C20 · 派工帶 {url, sha256}" --> AG
CINC == "C24 · https(舊基準可能 http)" ==> URL
MITM -. "換內容" .-> CINC
CINC -- "帶客戶帳密去跑規則" --> HOSTS
classDef ext fill:#FBFCFC,stroke:#9AA8AA,stroke-dasharray:3 2
classDef open fill:#FDECEC,stroke:#C0392B
class ADMIN,URL,MITM,HOSTS ext
class CINC open
| 項目 | 現況 |
|---|---|
| 誰 → 誰 | 客戶機房的 evidence-agent(實際是它起的 cinc-auditor 子行程)→ 客戶管理員在「檢測基準」頁填的網址。那個網址可能是客戶自己的檔案伺服器、GitHub、或任何第三方 |
| 協定/埠 | HTTPS(新登記只收 https://——白名單直接取 safe_http_fetch.ALLOWED_SCHEMES,不另立一份;http:// 回錯誤碼 DETECTION_TOOLS_400013)。既有的 http 基準不強制失效(避免客戶既有基準一次全滅),抽取時寫一筆醒目警告 |
| 傳什麼 | 規則包壓縮檔(InSpec profile:inspec.yml+Ruby 檢查腳本)。cinc-auditor 下載、解壓、載入——載入即執行,規則包裡的 Ruby 想做什麼就做什麼 |
| 怎麼驗身分 | 無。網址的伺服器不認 agent,agent 也不認伺服器(走 https 時由 cinc-auditor 的 HTTP 客戶端驗系統 CA;http 時全無) |
| 完整性比對 | 雲端有做、agent 沒接。雲端第一次抽取成功時把下載到的壓縮檔 sha256 落庫(detection_tool_profile_versions.sha256),派工時 build_payload() 把 {uid, source_type: "url", url, sha256} 下發;agent 端 profile_cache.resolve_profile_source() 對 url 型「原樣回傳 URL 字串,不經 cache、不比對」——程式註解明寫「平台不經手檔案,交給 cinc-auditor 自己去取(現行為)」。所以雲端解析過的那份與 agent 實際跑的那份,沒有任何機制保證是同一份 |
| 跟 file 型的差別 | file 型規則包經 C20 從雲端下載、sha256 對帳 required=True(缺指紋直接擋)、安全解壓到 cache;url 型全部跳過,由第三方工具直接抓 |
| 雲端那頭(C19)怎麼抓 | safe_http_fetch.fetch_url_bytes:只收 https、解析後每一顆 IP 不得落私有/保留段(擋 RFC1918、loopback、雲端 metadata)、限 50MB、自己走重導向最多 3 跳(每跳重驗 IP)。這些保護只在雲端那一跳,agent 這一跳沒有 |
| 何時存在 | 客戶用網址(不是上傳檔)建了檢測基準,且派 CINC Auditor/GCB 掃描時 |
依據:套件 jedi_detection/app/service/detection_profile_service.py:959-962(只收 https)、jedi_detection/common/detection_profile_ref.py:43-73(build_payload url 型帶 sha256)、jedi_detection/common/safe_http_fetch.py(C19 的四道保護)、detection_profile_extraction_service.py:456-469(抽取時算 sha256);agent repo core/profile_cache.py:124-144(url 型原樣交出)、core/task_executor_connectors/inspec.py:855-884(_resolve_profile 交給 profile_cache 分流)。
| 群 | 功能 | 對威脅的意義 |
|---|---|---|
| ① 掃描時載入規則包 | 派 CINC Auditor/GCB 掃描、基準是 url 型 → 每台 agent 每次掃描都去抓一次 | 規則包決定「檢查什麼、怎麼判定合規」——換了規則包,掃描報告就是假的;規則包是 Ruby,換了規則包也能在 agent 容器裡跑任意程式(C23b T1) |
| ② 雲端抽取(C19,不是本線) | 建基準或更新來源時,雲端抓一次解析控制項清單給畫面看 | 這一跳有完整保護;問題是它保護的那份內容,agent 不一定拿到同一份 |
掃描在這條線上抓到 1 條問題(M03-5)。依這條線的特性再補兩種手法(T2、T3 無掃描實例,風險欄標明)。
| # | 風險 | 威脅 | 駭客怎麼打 | 得手什麼 | STRIDE | 實例 |
|---|---|---|---|---|---|---|
| T1 | 🟡 中 | 在下載路徑上換掉規則包——不加密連線不用破解,加密連線也沒人比指紋 | ① 客戶管理員用一個網址建了檢測基準(舊基準可能是 http);② 攻擊者站在代理程式下載的網路路徑上(同網段、DNS、代理伺服器);③ 代理程式領到掃描工作,cinc-auditor 去抓規則包時,攻擊者回一包換過的;④ 規則包裡的 Ruby 在 agent 容器(root)執行——讀憑證、開後門;同時用客戶主機帳密登進受檢主機跑假檢查,報告也一起造假;⑤ 若是 http,連破解都不用;若是 https,雲端下發的 sha256 agent 沒比,換了也不知道 | 在客戶內網的代理程式上執行任意程式碼,並偽造掃描結果 | T 竄改資料、E 權限提升 | M03-5 網址型規則包放行不加密連線、也不記指紋 🟡 |
| T2 | —(無掃描實例) | 網址的主人事後換內容——雲端看到的是 A、agent 跑的是 B | ① 攻擊者控制那個網址(自己架的、或攻下了客戶的檔案伺服器、或 GitHub 帳號被盜);② 建基準時放一份正常的規則包,讓雲端抽取、解析、算指紋、管理員在畫面上審過;③ 之後換成惡意版本;④ 每次派掃描 agent 都去抓最新的——拿到的是 B,但畫面上顯示的控制項清單還是 A 的;⑤ 不需要站在網路路徑上,不需要破解 TLS | 同 T1,但攻擊者不必在內網 | T 竄改資料、E 權限提升 | — |
| T3 | —(無掃描實例) | 用規則包網址讓 agent 替人探內網、或抓巨檔撐爆 | ① 有權建檢測基準的人填 https://10.0.0.5/x.tar.gz——雲端那跳擋內網 IP,抽取會失敗;但基準仍可建立(抽取失敗只是沒有控制項清單),派工時 url 照樣下發;② agent 的 cinc-auditor 從客戶內網去抓——這一跳沒有內網限制、沒有大小上限;③ 連得上/連不上從掃描失敗訊息看得出;或指向一個數 GB 的檔讓 agent 磁碟吃光 |
客戶內網探測;agent 停擺 | I 資料外洩、D 讓服務停擺 | — |
三種手法的共同點:全部來自同一個結構性缺口——雲端審的那份與 agent 跑的那份是兩次獨立下載,中間沒有任何東西把它們釘在一起。sha256 已經算了也下發了,agent 端沒用上;C19 的四道保護(https、擋內網、限大小、限重導向)只保護雲端那一跳。這條線的本質風險不是「網址不安全」,是「同一個網址抓兩次,信任只建立在第一次」。
對應三種威脅,防線分五條。D1、D2 直接對應掃描抓到的手法;標 ◇ 的是依這條線的特性補的,沒有實例。「目前」欄寫程式裡實際有的機制;⚠️ 表示沒有機制。
| # | 防線 | 擋哪種威脅 | 目前做到哪 |
|---|---|---|---|
| D1 | 只收加密連線:新登記 https 限定;既有 http 基準要有退場(警告→期限→停用) | T1 的「不用破解」那半 | ✅ 新登記只收 https,白名單取自 safe_http_fetch.ALLOWED_SCHEMES 不另立;✅ 既有 http 抽取時寫醒目警告。⚠️ 既有 http 基準不強制失效、沒有退場期限——裁定避免客戶既有基準一次全滅,但也等於 http 這條路永遠開著 |
| D2 | agent 端比對雲端下發的指紋:url 型也走 agent 自己的下載器(而非交給 cinc-auditor)→ sha256 對帳 → 安全解壓 → 餵目錄給引擎,與 file 型同一條路 | T1 的「沒人比指紋」那半、T2 全部 | ⚠️ 沒有。雲端 build_payload() 已下發 sha256;agent profile_cache.resolve_profile_source() 對 url 型原樣回傳不比對。M03-5 的狀態欄明寫「代理端比對指紋屬後續小卡,本次未見對應 commit」——這就是那張小卡要做的事,兩頭差一步 |
| D3 ◇ | 內容漂移要可見:雲端定期(或每次派工前)重抓算 sha256,與落庫值不符就警告管理員「網址內容已變更,請重新審核」;派工時若不符則拒派 | T2 | ⚠️ 沒有。抽取是一次性的(建基準或手動重新解析);之後網址內容變了雲端不知道、畫面上的控制項清單是舊快照(程式註解明寫「url 型的清單是外部網址某一刻的快照,內容會漂」) |
| D4 ◇ | agent 這一跳也要有 C19 那四道:擋內網 IP、限大小、限重導向、逾時——不信雲端一定擋過 | T3 | ⚠️ 沒有。cinc-auditor 自己抓,agent 管不到它的下載行為。做了 D2(改走 agent 下載器)這條就順帶有了——agent 的 _get_stream 已有大小上限與取消檢查,再加 IP 檢查即可 |
| D5 ◇ | url 型基準要過審才能派:抽取失敗(網址抓不到、不是合法規則包)的版本不得進入可派工狀態 | T3 的「抽取失敗仍可建」那一步 | ⚠️ 抽取失敗只影響控制項清單顯示,基準本身仍可建立並派工(build_payload 對 url 型只要 profile.url 有值就下發;sha256 可為 None,註解寫「屬已知缺口而非錯誤」)。沒查到「抽取成功才可發布」的狀態機約束 |
最該補的一步:D2,而且它已經在排程裡(M03-5 的後續小卡)。做法就是把 url 型從「原樣交出」改成「走 file 型同一條路」:agent 自己下載(順帶拿到 D4 的大小與 IP 限制)→ 比對下發的 sha256(T1、T2 一起擋掉)→ 解壓進 cache → 餵目錄給 cinc-auditor。profile_cache.py 的 file 型流程全部現成,差的是 url 型那個 return url 的分支改接過去。
依據:STRIDE 六頁信任邊界連線標記(CM-2403/2404 驗收後版本)、套件 jedi_detection/app/service/detection_profile_service.py/detection_profile_extraction_service.py/common/detection_profile_ref.py/common/safe_http_fetch.py、agent repo core/profile_cache.py/core/task_executor_connectors/inspec.py/core/task_executor.py、DFD Level 0 §2.3(C19)/§2.4(C24)。