Mailpit WebSocket 來源驗證再度被繞過,百分號編碼路徑讓 CORS 檢查形同虛設
GitHub Security Advisory · 2026-08-20
漏洞機制
Mailpit 是常見的本機 SMTP/HTTP 郵件測試工具,其 WebSocket handler 會檢查 Origin 前的請求路徑是否落在 /api/ 這個受保護前綴下,藉此擋掉惡意網頁發起的跨站連線。問題出在驗證用的是尚未解碼的原始路徑字串,而 Go 標準庫的 ServeMux 實際路由時卻會先做 percent-decoding 再比對。攻擊者只要把路徑寫成 /%61pi/events(%61 解碼後即字母 a),驗證端 strings.HasPrefix 比對 /api/ 會判定不符而整段跳過檢查,但 Go 路由仍把它解成 /api/events 送進 handler。此洞是 CVE-2026-22689 修補的迴歸(regression),本質是驗證層與路由層對同一字串做了不同的正規化。
// 驗證邏輯示意
if !strings.HasPrefix(r.URL.Path, "/api/") {
// /%61pi/events 判定為 false,檢查被跳過
return
}
// 但 ServeMux 已將路徑解碼為 /api/events 送入 handler
受影響版本
- Mailpit
1.29.0至1.30.5(含)受影響 1.30.6已修補(fix commitfbe5e00)
此洞 CVSS 評為 6.5(中等)。實務影響是:本機執行 Mailpit 時,使用者只要瀏覽一個惡意網頁,該網頁的 JS 就能在背景建立未授權 WebSocket 連線,即時取得收件匣中的寄件者、收件者、主旨與內文片段,完全不需登入憑證。
修補與緩解
1.30.6 把 origin 檢查移到路徑正規化之後執行,確保驗證與路由使用同一份已解碼字串。
- 將 Mailpit 升級至
1.30.6或以上版本 - 避免在瀏覽器仍開著不受信任分頁時,讓 Mailpit 對外網段監聽
- 若暫時無法升級,可在反向代理層統一做一次路徑正規化再轉發
原始來源:GHSA-8r62-w5wh-fc5m
Mailpit SMTP DATA 讀取先塞爆記憶體才檢查大小上限,未認證攻擊者可觸發資源耗盡
GitHub Security Advisory · 2026-08-20
漏洞機制
SMTP 的 DATA 指令沒有固定長度,伺服器要逐行讀取、遇到單獨一行「.」才視為訊息結束(即 dot-stuffing 機制)。Mailpit 逐行讀取時呼叫 bufio.Reader.ReadBytes('\n'),這個呼叫會持續配置記憶體直到遇到換行字元為止,而訊息大小上限的檢查卻是等整行讀完才進行。只要送出一行超長且刻意不放換行符的資料,伺服器就會在拒絕前先把這一整行吃進記憶體。
// 概念示意:大小檢查發生在整行讀完之後
line, err := reader.ReadBytes('\n') // 持續配置記憶體、無中途上限
if len(line) > maxMessageSize {
return "552 5.3.4 message too large"
}
受影響版本
- Mailpit
>= 1.30.0、< 1.30.5受影響 1.30.5已修補(fix commit 8720c6b)
CVSS 3.1 評為 5.3(中等),向量為 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L,對應 CWE-770(資源配置未設限)。即使設定 50 MiB 訊息上限,攻擊者送一行 64 MiB、不給換行符的資料仍能先撐爆記憶體再被拒絕;多條連線同時攻擊會疊加成明顯的服務可用性壓力,且不需任何 SMTP 認證。
修補與緩解
修補版本改成邊讀邊檢查長度,一旦超過上限立即中斷連線,同時正確處理 dot-stuffing 與 CRLF 結尾,不再讓大小檢查依附於單一 ReadBytes 呼叫之後才觸發。
- 升級 Mailpit 至
1.30.5或以上 - 不要將 Mailpit 的 SMTP 監聽埠直接暴露在不受信任網路
- 容器部署可額外加上記憶體上限,降低單一惡意連線的影響範圍
原始來源:GHSA-r553-m4fv-5v97
gettext-converter 翻譯字串轉換函式存在原型污染,惡意 key 可污染整個 JS Runtime
GitHub Security Advisory · 2026-08-20
漏洞機制
npm 套件 gettext-converter 的 js2i18next() 會把巢狀翻譯 key 用分隔符(預設 ##)切開,再把每個片段依序當成動態屬性名稱組出巢狀物件。原型污染(prototype pollution)的成因在於:JavaScript 物件的 __proto__ 其實是指向該物件原型的參照,程式若沒過濾就直接拿使用者輸入的字串當 key 賦值,寫入 __proto__ 等同改寫 Object.prototype,效果是全域性的,會影響同一個 process 裡所有物件,而不只是當前這筆翻譯資料。
// 概念示意
const segments = key.split("##"); // 例如 "__proto__##polluted"
let node = target;
for (const seg of segments) {
node = node[seg] ??= {}; // seg 為 __proto__ 時直接污染原型鏈
}
受影響版本
gettext-converter< 1.3.3受影響1.3.3已修補
CVSS v4 評為 6.9(中等)。任何會把來源不受信任的翻譯檔(例如第三方貢獻的 locale 檔或使用者上傳字串)丟進 js2i18next() 轉換的應用都在受影響範圍內,被污染的原型可能導致後續邏輯判斷錯誤,成為阻斷服務(DoS)的跳板。
修補與緩解
修補版本在把每個 key 片段當成物件屬性寫入前,先擋掉 __proto__、constructor、prototype 三個保留字,符合的片段直接拒絕轉換。
- 升級至
gettext-converter 1.3.3或以上 - 暫時無法升級時,呼叫
js2i18next()前自行過濾含__proto__/constructor/prototype的 key - 轉換結果改用
Object.create(null)或Map承接,降低原型污染的實際影響面
原始來源:GHSA-f4jp-rw7w-ccwg