⚠️ 歷史紀錄:本報告描述的問題已於 2026-11-07 全部修正並合回 Agent 主線(修正卡 CM-3559~3564、3573~3576、3584,驗收紀錄見母卡 CM-3507)。文中的檔名與行號是掃描當下(commit
bab2aa3)的版本,修正後已變動,請勿拿來定位現行程式。
找到 1 個問題,最嚴重是「中」:Agent 連客戶的 OpenVAS(GVMd)時完全不驗對方的 TLS 憑證,同網段能做中間人的人可以拿走 OpenVAS 帳密,或回一份假的弱點掃描報告讓 Agent 當證據上傳。三位檢查員 3:0 通過。OpenSCAP 這支沒有工具報出的問題;「重點看什麼」逐條人工查過,另外列了三個加固建議(不算漏洞)。
oscap xccdf eval(系統組態合規檢測,可帶 sudo),把 HTML 報告用 SFTP 拉回。遠端指令是字串,shlex.quote(把一段文字包成 shell 不會拆開的形式)是 shell 層唯一的防線。範圍:guidant_agent/scan/adapters/connectors/openscap.py、guidant_agent/scan/adapters/connectors/openvas.py,共 2 檔、約 880 行。為了查證,我翻了範圍外的 python-gvm 27.5.0 套件 gvm/xml.py(XML 怎麼組),只為查證而讀,本身不當作發現。
| # | 這是什麼問題 | 嚴重度 | 面板 |
|---|---|---|---|
| F1 | OpenVAS 連線不驗 TLS 憑證,帳密送給誰都行 | 中 | 3:0 通過 |
已對照主系統 security-scan-consolidated/README.md:#232 講的是「主控台改位址沿用舊密碼」,#235 講的是「InSpec 不驗 SSH 主機指紋」,都不是 F1,所以 F1 是新問題。
白話: TLS 憑證是伺服器的身分證。Agent 連 OpenVAS 時沒有檢查這張身分證,誰冒充 OpenVAS 都會被當成真的,Agent 照樣把帳號密碼交出去。
出事會怎樣:
要先有什麼:
base_url 那個主機名的 DNS。在哪裡: openvas.py:82 _gmp:
connection=TLSConnection(hostname=host, port=port, timeout=_CONNECT_TIMEOUT_SEC),我自己開了已安裝的 python-gvm 27.5.0 gvm/connections/_tls.py:77-81 核對:沒給 cafile/certfile/keyfile 時,它建的連線設定是 check_hostname = False、verify_mode = ssl.CERT_NONE,也就是完全不驗。接著 :90(probe)和 :136(run)呼叫 gmp.authenticate(username, password),把帳密送出去。repo 的守衛 test_no_cert_none 只掃我們自己的程式碼,所以抓不到第三方套件預設關掉驗證的情況,違反 .claude/rules/security/external-calls-and-tools.md 的「TLS 不得關驗證」(SR-03.2)。
怎麼修: 給連線一個信任來源。可以在工具設定裡放客戶 GVMd 的 CA 檔,或在第一次測試連線時記下伺服器憑證指紋、之後不符就拒絕(跟 SSH 的 ssh_host_keys 同一套做法),沒有信任來源或指紋不符就不連。注意 python-gvm 就算三個檔都給了,還是會關掉主機名檢查,所以要自己包一層會驗證的連線,或在 authenticate 之前先比對對方憑證指紋。守衛也要擴充,抓「第三方連線類別預設不驗」這種寫法。
| # | 重點 | 人工查過 | 為什麼 |
|---|---|---|---|
| 1 | :375-376 oscap 指令的三個值;_resolve_content_path 在遠端用什麼指令解析 content_path;profile='-x' 會不會被當選項 |
shell 層不成立;選項夾帶有一個加固點 | _resolve_content_path(:229)不在遠端跑任何吃 content_path 的指令:結尾是 .xml 就直接用,否則只跑寫死的 cat /etc/os-release(:219)再拼檔名。實際拼出的指令長這樣:sudo /usr/bin/oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis --report /home/audit/tmp.Ab12Cd.html /usr/share/xml/scap/ssg/content/ssg-ubuntu2204-ds.xml(值裡沒有特殊字元時,shlex.quote 不會加引號)。profile='-x' 時,--profile 一定要帶一個值,所以 -x 會被吃成 profile 名稱,不會變成選項。加固點: content_path 是最後一個參數,前面沒有 -- 分隔,也沒有檢查 - 開頭。如果填 --results=/某路徑/x.xml,結尾是 .xml 會直接採用,oscap 可能把它解析成 --results 選項(以 root 身分寫結果檔)。我推論這種情況因為少了 content 參數,oscap 會先報錯,所以沒有實際寫檔,但沒有實機驗證。只有派工的人(主系統,或有本機掃描權限的主控台使用者)填得到這個值。建議拒絕 - 開頭的 content_path/profile,或在 content 前面加 --。 |
| 2 | :19 sudo 怎麼決定、密碼怎麼送;_read_remote_os_release、_detect_content_os_mismatch 跑什麼指令 |
不成立 | use_sudo 來自工具帳密設定(:163-164),預設 True。不送 sudo 密碼,靠受檢主機 sudoers 設 NOPASSWD: /usr/bin/oscap。_read_remote_os_release 只跑寫死的 cat /etc/os-release;_detect_content_os_mismatch(:253)只在 Agent 本機比對檔名,沒有另外跑遠端指令,也不吃使用者值。 |
| 3 | :174-197 私鑰暫存檔、_connect、BadHostKeyException |
不成立 | 暫存檔優先放 /dev/shm,用 mkstemp 建立再 chmod 0600,finally 一定刪除,client.connect 被 context manager 包住(:213-214),連線丟例外也會刪。look_for_keys=False, allow_agent=False(:208)。主機指紋走 new_verified_client(:204),:356/:578 不符就拒絕(C3 已掃過的首次信任策略)。 |
| 4 | :445 rm -f、:589 command -v;remote_report 能不能被預先放 symlink |
不成立 | remote_report 由遠端 mktemp -p "$HOME"(:369)建立,檔名隨機、檔案已存在且屬登入者;家目錄一般其他使用者寫不進去,搶不到 symlink。rm -f 和 command -v 都有 quote,值來自 mktemp 輸出或常數。:602 test -s 也有 quote。 |
| 5 | openvas.py:125-148 hosts/port_list_id/scan_config_id/scanner_id 有沒有 XML 跳脫;挑任意 scan config 合理嗎 |
不成立 | python-gvm 用 lxml 組 XML(gvm/xml.py:101-102 SubElement + .text =、:112 .set()),lxml 會自動跳脫 </&,不能注入元素。挑 scan config 是功能本身(掃多深是派工者的選擇),派工者本來就能決定掃哪些目標。 |
| 6 | :101 _task_uid 進任務名稱;OpenVAS 帳密來源與 verify;報告上傳前遮不遮 |
前兩項:見 F1;遮罩:不成立 | _task_uid 截前 8 碼拼名稱,經 lxml 跳脫,無害。帳密來自心跳下發的解密 credentials,TLS 不驗=F1。弱點報告就是要交的稽核證據,不遮是設計如此。 |
| 7 | 兩支的 timeout_sec 上限;Agent 逾時放棄後 OpenVAS 那邊的任務會不會一直跑 |
**成立(可靠性,不算資安漏洞) | 兩支都直接 int(params["timeout_sec"]),沒有上限**(openscap :528、openvas :132)。OpenVAS 逾時時(:258)只 raise TimeoutError,沒呼叫 stop_task(只有取消路徑 :242 會停),所以 OpenVAS 那邊會繼續掃,佔客戶資源,target/task 也不會刪。建議逾時時也 stop_task,並對 timeout_sec 設上限(例如不超過主體 4 小時)。 |
verification.status = verified。1 個候選、3 票全投、3:0 通過,研究員 1/1 回來。content_path 夾帶 oscap 選項」是推論,沒有實機驗證。| 項目 | 值 |
|---|---|
| run ID | wf_0d847800-830 |
| 掃描版本 | 6d1e6ca(branch ws-CM-3507-CM-3529,tree 乾淨) |
| effort | low(1 位研究員+三視角面板) |
| 研究員 | 派出 1、回來 1,沒有被砍(journal failed 0) |
| verification | verified;candidates 1、panel_votes 3、panel_quorum_findings 1、reason 無 |
| 耗時 | 約 17 分鐘(1,037 秒) |
| 歸檔 | runs/CLAUDE-SECURITY-20261106-035701-C5/(原目錄名與 C7 同秒撞名,加 -C5 後綴區分) |
所有結論都是讀程式碼得出的,沒有執行程式碼,也沒有實際打過攻擊。