返回所有文章
發佈於 · 作者 Renaud Deraison

那份字典檔早就知道你的家目錄

F5 Labs 在一個月內記錄到針對暴露的 Vite 開發伺服器的 807 次攻擊與 32,000 筆事件,用的是一個查詢字串小把戲,直接從 Vite 自己的 deny-list 前面走過去。掃描器要找的不是你的應用程式。它們用絕對路徑索取 /home/ubuntu/.aws/credentials,索取 /proc/self/environ,索取 terraform.tfstate。在 Bromure Agentic Coding 的工作區裡,那個連接埠面對的是一台私有交換器,而字典檔想要的那個檔案裡沒有任何金鑰。

這個攻擊者不寄套件給你,也不寄壓縮檔。是你自己的開發伺服器在聽,而一個陌生人 直接點名向它要檔案。由哪一台機器回答,決定了對方拿到什麼。

你叫代理把前端跑起來,好讓你看一眼。它敲下 npm run dev,綁好連接埠,載入頁面, 你則回頭去看那份 diff。在那個循環的某處,在某個 docker-compose.yml 裡,或在你 幾週前為了讓手機也能看預覽而加上的 --host 旗標裡,伺服器綁上了每一張介面,而不是 只綁 loopback。十二分鐘後,某個自稱 Googlebot 的東西向你的開發伺服器索取 /@fs/../.env?raw??,並拿到一個 HTTP 200。

F5 Labs 在 9 月 11 日公布了數字。 在 2026 年 8 月這單單一個月的感測器資料裡,他們的蜜罐記錄到 807 次以工作階段分組的 攻擊,以及大約 32,000 筆針對暴露的 Vite 開發伺服器的原始事件,而前三個月的基準值是 1,732 筆。也就是說,為了一個臭蟲,流量在一個月之間變成十八倍。BleepingComputer 在 9 月 14 日做了報導

一段從 deny-list 旁邊走過去的查詢字串

Vite 透過一條叫做 /@fs/ 的內部路由,把主機檔案系統上的檔案送出去;開發伺服器就是 靠它,把位於專案根目錄之外的模組交給你的編輯器。正因為這條路由哪裡都到得了,Vite 隨附了一份 deny-list,也就是 server.fs.deny,用來擋掉那些顯而易見的目標:.env 檔案、憑證、私有原始碼。

4 月 7 日公布的 CVE-2026-39364,讓攻擊者只要在請求上綴一段查詢字串,就能跳過那份 deny-list。F5 這樣描述它的機制:

伺服器處理該請求,正規化路徑,並在存取驗證期間去除或錯誤解讀查詢字串,因而未能 觸發 server.fs.deny 檢查。

伺服器未能落實 deny-list 過濾,並以 HTTP 200 回應送出目標檔案。

這個臭蟲影響 Vite 7.1.0 到 7.3.2,以及 8.0.5 之前的 8.x。F5 在實戰中攔到這些請求 形狀,每一條都短到一行讀得完:GET /@fs/.env?raw??GET /@fs/../.env?raw??GET /@fs/..%252f..%252f..%252f..%252froot/.env?raw??,以及 GET /@fs/..%252f..%252f..%252f..%252fproc/self/environ?raw??。同一套工具裡的其他 變體則用 ?import&raw?import&url&inline?inline&import?raw?import

那些參數把戲各自帶著自己的 CVE 編號:CVE-2025-30208、CVE-2025-31125 和 CVE-2024-45811,是同一條路由上三個更早的繞道,至今仍裝在同一批掃描器裡。經營這支 掃描艦隊的人並沒有為了一個新臭蟲重新打造工具。他只是在一份早就有的檔案上,再加了 一行。

一個請求,一個檔案掃描器GET /@fs/../.env?raw??User-Agent: Googlebot/2.1vite 開發伺服器路徑為送出而正規化/@fs/ → 讀取磁碟server.fs.deny查詢字串被誤處理,檢查遭跳過讀出檔案並送回沒有認證、沒有工作階段、沒有日誌行HTTP 200檔案的內容,就放在回應主體裡同一批掃描器早就裝著CVE-2025-30208 · ?raw??CVE-2025-31125 · ?inline&importCVE-2024-45811 · ?import&raw
一次往返就結束的繞道。Vite 的 /@fs/ 路由本來是要送出專案檔案,而 server.fs.deny 本來是要讓它離秘密遠一點。在後面接一段查詢字串,常見形狀是 ?raw??,路徑會被正規化以供送出,卻在存取檢查時被錯誤處理,於是 deny-list 保持沉默,檔案帶著 200 回來。同一個請求裡的路徑穿越會伸出專案之外,前端開發伺服器就是這樣讀到了 /proc/self/environ。

字典檔是一張開發者機器的地圖

讀一讀掃描器索取的東西,並留意其中有多少其實跟你的應用程式無關。

它們索取 .env.env.local.env.production.env.development.env.staging。索取 terraform.tfstateterraform.tfvars.terraform/terraform.tfstateserverless.yml.serverless/serverless-state.json。索取 .azure/credentials.azure/accessTokens.json。索取 /etc/passwd/proc/1/environ/proc/self/cwd/.env/proc/self/environ。最後那條路徑裝的是開發伺服器行程 自己的環境區塊,也就是你的 shell 匯出的 API_KEY 最後落腳的地方。

接著它們會沿著開發者行程可能用到的家目錄清單,一個一個索取 AWS 憑證:

/root/.aws/credentials/home/ec2-user/.aws/credentials/home/ubuntu/.aws/credentials/home/node/.aws/credentials/home/www-data/.aws/credentials/home/admin/.aws/credentials/home/debian/.aws/credentials/var/www/.aws/credentials/usr/src/app/.aws/credentials/app/.aws/credentials。然後是 .aws/config.aws/credentials.backup.aws/credentials.bak.aws/sso/cache/rootkey.csvaws-exports.jsamplifyconfiguration.json

那些路徑清點的是一台開發者機器,按使用者名稱逐一列舉,而 Vite 只是那道門。臭蟲本身 是附帶的;同一條路由上三個更舊的臭蟲,就搭在同一批請求裡一起來。經營者賭的是:一個 在可路由位址上聆聽的行程,跑在一個家目錄裡放著真金鑰的使用者底下。

這些流量打扮得足以撐過對日誌的隨意一瞥。F5 記錄到偽造的 User-Agent 標頭,在 Googlebot/2.1ClaudeBot/1.0GPTBot/1.4PerplexityBot/1.0OAI-SearchBot/1.3Amazonbot/0.1 之間輪替,還有偽造的 X-Forwarded-ForX-Real-IP 值,用來繞過 IP 允許清單。來源落在 Google Cloud 的 34.x 和 35.x 網段, 分布於好幾個區域,以美國的 17,297 筆事件居首,其次是比利時的 4,407 和荷蘭的 4,011。 這個月,你存取日誌裡自稱是 AI 爬蟲的那一行,並不足以證明真有爬蟲送出過它。

F5 的建議裡有兩條是架構問題

F5 以五條建議收尾。其中三條平常而正確:升級到 7.3.2 或 8.0.5、在邊緣過濾 /@fs/、 用反向 DNS 驗證爬蟲而不是相信標頭。另外兩條講的不是一件你做得完的事,而是一種你得 一直維持住的姿態。

確保開發伺服器不綁定到外部介面。稽核 Docker compose 設定、Kubernetes ingress 規則以及雲端安全群組。

輪替已暴露的秘密:若一台未修補的 Vite 開發伺服器在 2026 年 8 月期間可由外部網路 觸及,請將本機的 .env 變數、AWS 憑證、Azure 存取權杖與 Terraform 狀態檔視為可能 已遭洩漏。

第一條要你對每一個連接埠、在每一份 compose 檔案裡、跨越每一條分支、在專案活著的每 一天都守住一個承諾;而此刻敲下 npm run dev 的,往往是代理而不是你。第二條問的是 事後你要做什麼,並自己給了答案:把那台機器看得到的東西全部輪替。

如果連接埠是在別的地方打開,而字典檔點名的那些檔案裡沒有任何值得輪替的東西,這兩件 事都會輕鬆許多。

Bromure 的工作區把連接埠放在哪裡

Bromure Agentic Coding 讓編碼代理跑在你 Mac 上一台硬體虛擬化的 Linux VM 裡,安全 控制則放在那道邊界的主機側。其中有兩項回應了這場行動。

開發伺服器綁在一台私有交換器的內側。 在預設的 NAT 模式下,每一台工作區 VM 都接上 同一台行程層級的軟體 L2 交換器,多工在單一 vmnet 介面上,位於一個私有子網路。除非你 自己的區域網路已經用掉,否則那個子網路就是 192.168.64.0/24;Bromure 在上面跑自己的 DHCP 伺服器,每個工作區都保有固定的 MAC 與穩定的租約。手冊寫下了它的後果:你的 Mac 到得了那些 VM,你的實體區域網路看不到它們,而除非你自己發布某個服務,否則從別處進來的 連線根本不可能。在那台 VM 裡綁上 0.0.0.0 的代理,綁的是它自己擁有的每一張介面,而 每一張都面對一台從你的筆電開始、也在你的筆電結束的交換器。你沒有安全群組要稽核,因為 你沒有一條進來的路要守。

你仍然看得到什麼在聽,而那是一份清單,不是一場稽核。 工作區儀表板上有一張 Listening Ports 卡片,把訪客端每一個可從外部觸及的通訊端,以 <VM 位址>:<連接埠> 的端點形式列出,還能一鍵複製。這把 F5 的第 2 條建議,從一場你得排進行事曆的稽核, 變成一張你順手瞄一眼的卡片,而且每一秒半就從訪客端更新一次。當你真的想讓全世界看到 預覽時,一個看起來像 HTTP 的服務會多出一顆地球按鈕,在一次性的同意對話框之後,透過 Cloudflare 快速通道發布那一個服務。你是在主機上,一個服務一個服務地點擊來公開它, 而不是靠一份 compose 檔案裡活著的旗標。

你當初伸手去拿 --host 的理由,仍然保得住。那台用完即丟的 Chromium 側車跟工作區 VM 共用同一個 L2 網段,所以內嵌瀏覽器是用 VM 的位址去載入代理的開發伺服器。不是 localhost,因為瀏覽器跑在另一台機器上。

如果真有其他機器需要連到這台 VM,Bridged 模式會把它接上你的實體區域網路。你在主機上的 工作區編輯器裡逐個工作區設定這件事,而且啟動時若介面不可用,它會退回 NAT。這個選擇是在 一個面板裡做的,不是在一份代理改得動的設定檔裡。

一台暴露的開發機可路由位址、真實使用者、真實檔案GET /@fs/../.env?raw??從 34.x 抵達,200 OK/home/ubuntu/.aws/credentials一組存取金鑰 ID 和一把有效密鑰/proc/self/environshell 匯出的每一枚權杖事後輪替金鑰、權杖、狀態檔,再去猜那段時間有多長在工作區裡一台私有交換器,一個滿是假貨的家目錄沒有入站路徑可以抵達vmnet NAT · 192.168.64.0/24Listening Ports 卡片每一個開啟的通訊端,以及一顆發布用地球字典檔落在假貨上sk-ant-api03-brm-… · ghp_… · credential_process事後想重置磁碟就重置,而且什麼都不必輪替
同一次掃描,打在兩台機器上。在一台擁有可路由位址的開發機上,請求打到一個以真實使用者身分執行的行程,而字典檔裡的每一條路徑都解析到一個真實檔案:裝著有效金鑰的 .env、裝著密鑰的 ~/.aws/credentials、列著 shell 匯出權杖的 /proc/self/environ。在 Bromure Agentic Coding 的工作區裡,連接埠面對的是一台沒有任何入站路徑的私有 vmnet 交換器。就算你刻意發布那個服務,字典檔落腳的仍然是假貨,以及一份指向輔助程式而不是藏著金鑰的 ~/.aws/config。

那如果你是刻意發布這個服務呢

有時候你確實想把預覽放到網際網路上,給客戶或同事看。那就點下地球、打開通道,讓掃描器 找上門。順著字典檔一條一條往下走,看看回來的是什麼。

.env/proc/self/environ 回傳的是工作區的環境,而裡面每一份憑證都是假的。 Bromure 用真實值加上每次安裝專屬的 32 位元組鹽,經 HKDF-SHA256 導出每一個替身,並保留 用戶端驗證器期待的形狀:Anthropic 的金鑰讀起來是 sk-ant-api03-brm-…,GitHub 的權杖是 ghp_ 加 36 個字元,GitLab 則是 glpat- 加 20 個。Bromure 把那些假貨寫進環境變數,也 寫進 ~/.git-credentials~/.docker/config.json~/.kube/config 以及各個 MCP 設定。 真實值從不進入那台 VM;它們加密留在 Mac 上,由主機側的代理在最後一刻把每一個換到線路上, 而且只限它所屬的那個目的地主機。

/home/ubuntu/.aws/credentials 是最鋒利的一條,因為 /home/ubuntu 正是 Bromure 工作區 VM 的家目錄。掃描器猜對了是哪個使用者在跑那台伺服器。檔案卻依然不在那裡。 Bromure 的 AWS 設定根本不寫憑證檔;它寫的是 ~/.aws/config,裡面一行 credential_process 指向一個輔助程式,那個程式透過主機通訊端交出你真正的存取金鑰 ID, 配上一把四十個字元的密鑰,並且省略工作階段權杖。AWS 的各種 SDK、aws CLI、 terraform 和 boto3 都會自己去接。掃描器讀到那份設定檔,拿到的是一個它呼叫不了的輔助 程式路徑。

就算你還是把那把假密鑰拿走,拿走的也是一件死物。訪客端用那個假貨為請求簽章,主機在 出去的路上剝掉簽章,再用真金鑰重新簽一次。任何循其他途徑抵達 AWS 的請求,都會以 InvalidSignatureException 失敗。F5 的最後一條建議,是把每一份本機秘密都當成已洩漏 並加以輪替。Bromure 自家文件裡對應的那一行,讀起來正好相反:因為外洩的只有假貨,真正的 憑證永遠不需要輪替。

字典檔裡有一條點名的檔案真的值錢:terraform.tfstate。狀態檔跟著儲存庫走,所以決定 它暴露程度的是你的共用資料夾清單。主機資料夾以 virtiofs 掛載的形式接進工作區,掛在 /home/ubuntu/<basename>,每個工作區上限八個,而那也是 VM 唯一搆得到的 Mac 檔案系統 部分。只共用代理正在處理的那個儲存庫,其他都不共用,那麼路徑穿越找到的就是一個目錄, 而不是一整顆磁碟。

今年大多數開發者安全故事描述的都是某樣東西抵達:一個套件、一份壓縮檔、一份文件檔案。 這裡什麼都沒有抵達。取而代之的是,每個月 32,000 個請求要求一個你親手啟動的行程,把 檔案唸給一個陌生人聽;而結局取決於是哪一台機器在跑那個行程,以及它的家目錄裡放著 什麼。

把代理的開發伺服器放到一台只有你的 Mac 在上頭的交換器,並且把它的家目錄填滿假貨。 安裝 Bromure Agentic Coding,然後就讓掃描器去問吧。