OpenSSH 在 musl libc(Alpine 預設)且關閉 PAM 的環境,有一個潛伏十年的密碼驗證繞過:只要機器上有一個密碼欄位為 * 的帳號,任何人送出 257 到 1024 字元的任意密碼就能登入。修法兩個:升級含修補的 openssh 並重啟 sshd,或直接關掉密碼登入。
這個漏洞在 2026 年 11 月 5 日由 OpenSSH 以 10.6p2 緊急修掉,CVE-2026-123158 於同日稍晚指派,CVSS 3.1 分數 8.1(High)。發現者是 Wakefield 的 Özgün Kültekin,公開頁面就叫做 sshhole.com,名字來自那個「星號」與那個「洞」。
時間軸值得先看一眼:OpenSSH 在得知漏洞的隔天就發版(維護者 Damien Miller 於 11 月 4 日晚間提交修正,11 月 5 日釋出),Alpine 同日補齊 3.16 到 edge 的套件。而 Previdian 的感測器在 CVE 公開後不到九小時就錄到利用嘗試,並在其中一台 Alpine 蜜罐上取得 root。
一句話版本:你的機器有沒有中?
受影響的不是「所有 Alpine」,而是同時踩到五個條件的那批。任一條不成立就不會被這招打進來:
| 條件 | 說明 | 怎麼查 |
|---|---|---|
| sshd 跑在 musl 上且沒用 PAM | Alpine 預設的 openssh-server 套件就是這樣;openssh-server-pam 若設 UsePAM no 也算 | ldd --version 出現 musl;apk info | grep openssh |
PasswordAuthentication 是 yes | OpenSSH 預設值,沒改過就是開的 | sshd -T | grep -i passwordauthentication |
機器上有帳號的密碼欄位是 * | key-only 使用者、Alpine 的 root(3.16 起就是 root:*)、tiny-cloud 映像的 alpine 使用者、透過 musl-nscd 的目錄服務使用者 | awk -F: '$2=="*"{print $1}' /etc/shadow |
另有至少一個「啟用中」的帳號存著真的 $5$ 或 $6$ 雜湊 | sshd 會「借」那個帳號的 salt 來算假雜湊 | awk -F: '$2 ~ /^\$[56]\$/{print $1}' /etc/shadow |
攻擊者知道那個 * 帳號的名字 | 帳號名不是秘密,alpine、root、deploy 都是常見猜測 | — |
第四條是 Check Point 在公開公告之外自己補的測試結論:只有雜湊不夠,那個帳號必須是「活的」(未被鎖定)。少了這個捐 salt 的帳號,攻擊直接失敗。
反過來說,沒事的情況很明確:沒跑 sshd 的 Docker 容器、把 PasswordAuthentication 設成 no 的主機、使用 openssh-server-pam 且 UsePAM yes 的系統、密碼欄位是 !(鎖定帳號)而非 * 的機器、以及完全沒有 $ 開頭雜湊的主機。Debian、Ubuntu、Fedora 這類 glibc + PAM 的發行版不在影響範圍內,OpenBSD 與 Dropbear 也不受影響。
還有一個常被搞混的點:root 本身不一定最危險。OpenSSH 預設 PermitRootLogin prohibit-password,root 走密碼這條路是被擋住的;真正致命的是「任何可達、且有 doas/sudo 權限的 * 帳號」——打進去之後再提權。
這條洞的原始碼證據鏈:1980 到 2026
漏洞不是某一行寫錯,是四個各自合理的決定在十年間接起來。發現者把每一個環節的 commit 都列在公開頁面上,可以逐條核對:
| 時間 | 事件 | 當時為什麼是對的 |
|---|---|---|
| 1980 | BSD-3 的 /etc/passwd 用 * 標記 daemon 帳號,註解寫「The devil himself」 | * 不是合法雜湊,任何密碼都對不上,等於永久關閉密碼登入 |
| 1997 | 單一 UNIX 規格明訂 crypt() 失敗時回傳 NULL | 呼叫端可以明確區分「雜湊失敗」與「雜湊不匹配」 |
| 2012-08 | musl commit b3c4cc12:crypt() 失敗時不再回傳 NULL,改回傳 *(diff 是 return (setting[0]=='*') ? "x" : "*";) | 避免沒檢查 NULL 的程式直接 segfault——「總是成功、但給一個無法匹配的值」被認為比較友善 |
| 2012-08-29 | musl commit 88bf5a8a 加入 sha256/sha512 crypt,並設 KEY_MAX 256 | 註解寫明「key limit 不是原始設計的一部分,是為了防 DoS 才加的」——超長密碼會讓 sha512crypt 愈算愈慢 |
| 2016 | OpenSSH 為了修 CVE-2016-6210(用時間差列舉帳號),開始對沒有真實雜湊的帳號「借用別的帳號的 salt」硬算一次 | 讓「帳號不存在」與「帳號存在但沒密碼」的回應時間一致 |
| 2019 | glibc 幾乎照抄了 musl 的 * 回傳,最後變成 *0(Debian #714219) | 差一個字元,恰好躲過同一個坑 |
| 2022 | Alpine 把 root 的密碼欄位從 ! 改成 *(! 在無 PAM 時連金鑰登入都擋掉) | 讓 root 可以用金鑰登入、又不能用密碼 |
| 2024-12 | musl 郵件列表有人回報:257 字元的密碼會讓 passwd 崩潰,原因同樣是那個 * 與 256 上限 | 當時沒有人把它連到 sshd |
| 2026-10-30 | OpenSSH commit 84f8f4666a 做安全審查、修了 pick_salt() | 漏洞就在那個函式往下四行,四天後才被發現 |
| 2026-11-04 | OpenSSH 團隊得知漏洞;Miller 於 21:18 提交修正 90f9c2f7330b | — |
| 2026-11-05 | 10.6p2 釋出、Alpine 同日發套件與公告、CVE-2026-123158 指派 | — |
把上面幾條接起來就是:sshd 想驗一個 * 帳號的密碼 → 它沒有 salt,去借別人帳號的 $6$ salt → 送進來的密碼超過 256 bytes → musl 的 crypt() 回傳 * → sshd 拿 * 跟 /etc/shadow 裡的 * 一比,字串相同 → 登入成功。
Alpine 安全公告的描述很直白:「一次連線、每次都成功、不需要重試。」(one connection, works every time, and needs no retries)
至於長度,官方說明與 CVE 記錄之間有個位元組級的小歧義:musl 的界線是 KEY_MAX 256(超過 256 就失敗),所以實際可利用區間是 257 到 1024 bytes(1024 是 sshd 自己的 MAX_PASSWORD_LEN);但 CVE 記錄的行文是「256 到 1024 個字元」。寫報告時採 257 比較精確,兩者的分界都在同一個地方。
修正本身只有十幾行
修正有多小,看 commit 就知道。核心只有一個巨集與一個條件:
#define MIN_PASSWORD_HASH_LEN 4
...
ret = encrypted_password != NULL &&
strlen(pw_password) >= MIN_PASSWORD_HASH_LEN &&
strcmp(encrypted_password, pw_password) == 0;
邏輯是「真的雜湊一定很長,所以短於四個字元的欄位不必比」。* 只有一個字元,永遠過不了這道門檻,*0、!、!! 同理。同一個 commit 也順手把 pw_password 改成 xstrdup 複本、結尾用 freezero 抹掉,避免把借來的 salt 留在記憶體裡。
Miller 在 commit message 裡寫得很清楚:
「This bypass affects some configurations and runtime configurations. We do not consider the root cause to be within OpenSSH but we are well equipped to avoid it.」
要注意的是,這是在 sshd 這一側的繞道(workaround),不是修掉 musl 的行為。任何其他「把 crypt() 回傳值直接跟 /etc/shadow 比對」的程式,只要跑在 musl 上,仍然有同樣的邏輯瑕疵。
誰的錯:兩個專案都不認帳
10.6p2 的發布說明寫「我們認為根源在 sshd 之外」,musl 那邊也不認為自己有問題。這場爭論在 openssh-unix-dev 郵件列表上直接攤開來。
Andy Lutomirski 指出,不能全說是 musl 的錯:musl 其實預期有人會用 crypt("garbage", "*") 這種呼叫,並準備了對應處理。Miller 的回覆則很不客氣:
「IMO returning a character that has been used for decades to identify accounts with disabled passwords regardless of input is absolutely a bug in musl. This behaviour is not AFAIK documented anywhere and diverges from other crypt() implementations, which have safer failure return values. There is a perfectly good return value on failure: NULL.」
他也順帶批評了 2012 年那個「不要回傳 NULL 以免呼叫端崩潰」的決定,認為在 C 標準函式庫裡替沒做好錯誤處理的程式擦屁股,本身就是荒謬的。
musl 維護者 Rich Felker 的公開立場則是「這不是 musl 的 bug,是呼叫端用錯了」,但也表示這個回傳值「本來就打算改」,很可能在下一個週期調整。
對維運的人來說,這場筆戰的實務結論只有一句:不必選邊,兩邊都升。OpenSSH 這邊升到 10.6p2(或發行版修補版),musl 若之後改變行為,也跟著收。真正該記住的教訓是——一個函式庫的錯誤回傳值,如果恰好等於另一個系統裡的「安全標記」,那麼中間那層膠水就會變成攻擊面。
已經有人打進來了
漏洞修得快,不代表沒人動手。威脅情報公司 Previdian 的創辦人 Ryan Dewhurst 對 Help Net Security 表示:
- 第一次看到的利用嘗試,出現在 CVE 公開後不到九小時
- 至少有一台跑著真實 Alpine Linux + OpenSSH 的蜜罐被取得 root
- 攻擊者用超長密碼繞過驗證後,透過非互動 SSH 執行
id、uname、hostname做偵察 - 初期記錄是來自單一日本位址
89.116.89[.]18的 8 次嘗試;Previdian 的公開頁面到 11 月 7 日已累計 19 次嘗試、12 個攻擊來源 IP、8 個國家
必須把話講準:這些是對蜜罐的嘗試與一次成功的入侵,不等於有大規模受害者。截至 2026 年 11 月 7 日,CVE 並未列入 CISA 的 KEV(已知被利用漏洞)清單,CISA 的 SSVC 評估當時仍記為「有公開 PoC」而非「已確認遭利用」。兩種說法可以同時成立,因為「蜜罐被打穿」與「確認的受害企業」是不同的證據門檻。實務上請當作已被利用處理。
另外,GitHub 上已出現多個 PoC。Previdian 與 OX Security 都提醒:不要亂跑來源不明的 exploit 或掃描腳本,它們本身可能就是惡意程式。要自查,用發現者公開的 oz9un/sshole,或發行版官方的檢查方式。
修補:升級之外,一定要重啟 sshd
Alpine 在 3.16 到 3.24 與 edge 全都發了修補套件,連已經停止一般支援的 3.16 到 3.20 都補了;3.21 在 2026-11-01 才結束支援,同樣有修補版。
| Alpine 分支 | 修補後的 openssh 版本 |
|---|---|
| edge | 10.6_p2-r0 |
| 3.24 | 10.3_p1-r2 |
| 3.23 | 10.2_p1-r1 |
| 3.22 | 10.0_p1-r11 |
| 3.21 | 9.9_p2-r1 |
| 3.20 | 9.7_p1-r6 |
| 3.19 | 9.6_p1-r3 |
| 3.18 | 9.3_p2-r4 |
| 3.17 | 9.1_p1-r7 |
| 3.16 | 9.0_p1-r6 |
指令只有兩行,但第二行才是關鍵——套件升級不會重啟正在跑的 daemon:
apk upgrade -U -a
rc-service sshd restart
如果暫時不能升級,就先關掉密碼登入。以下是 Alpine 公告給的原始指令(會先備份原設定,再寫一份 drop-in):
mkdir -p /etc/ssh/sshd_config.d
echo 'PasswordAuthentication no' > /etc/ssh/sshd_config.d/00-no-passwords.conf
sed -i.bak -e '1i PasswordAuthentication no' \
-e 's/^\(\s*PasswordAuthentication[^y#]*\)yes/\1no/i' \
/etc/ssh/sshd_config
sshd -t && rc-service sshd restart
sshd -T | grep -i passwordauthentication
最後一行要印出 passwordauthentication no 才算生效。動手前先確認自己的金鑰登入是通的,而且不要關掉目前開著的 session,等新連線測試通過再登出。
還有一種「修了但看起來沒修」的陷阱:修補後的套件版本號還是舊的。Alpine 3.24 修好之後是 10.3_p1-r2,橫看豎看都不像「有修」。同樣地,這個漏洞在公開當天並沒有 CVE 編號(CVE-2026-123158 是 11 月 5 日稍晚才指派),所以當天依 CVE 清單跑的掃描器會直接漏掉它。判斷有沒有修,看套件 revision(-r 後面那個數字),不要看主版本號。
從外面看得到什麼
Check Point 補了一件對防守方很有用的事:登入前就能看到的兩個訊號。
| 外部可見 | 怎麼看 | 意義 |
|---|---|---|
| SSH banner 的版本字串 | nc host 22 或 ssh -o PreferredAuthentications=none host 的回應 | 知道對方是不是 OpenSSH 10.6p2 之前 |
| 允許的驗證方法清單 | 同上,會列出 publickey,password 等 | 沒開密碼登入的伺服器根本不在風險內 |
也就是說,做外部攻擊面盤點時,可以先篩掉「不接受密碼驗證」的主機,剩下的再用主機內部檢查(musl、PAM、* 帳號、是否有活著的 $6$ 帳號)確認。反過來看,攻擊者用的也是同一組訊號——這也是為什麼「關掉密碼登入」是當天最有效的緊急處置。
已經被打進來了嗎?去日誌找這一種句子
被這招登入的帳號「本來就不該用密碼登入」,所以事件日誌會留下一個很矛盾的特徵:一個沒有密碼的帳號出現了密碼登入成功。
grep "Accepted password for" /var/log/messages
如果看到 Accepted password for alpine、Accepted password for root,或任何 key-only 使用者的密碼登入紀錄,而那個帳號在 /etc/shadow 裡的欄位是 *,就當作已遭入侵處理:檢查 doas 使用紀錄、最近新增的使用者、~/.ssh/authorized_keys 的變動,以及對外連線。
關於金鑰要不要輪替,原則很簡單:只在有無法解釋的登入紀錄時才需要。一旦確認被登入,就假設該主機能碰到的每一把金鑰與憑證都已外洩,重建後全部換掉;如果日誌乾淨,升級加重啟 sshd 就夠了。
實測:官方 Docker 映像現在給的是修補版
把這件事落到最多人踩到的情境——Docker。2026-11-07 在乾淨環境拉兩個官方映像查套件庫版本:
| 映像 | /etc/alpine-release | apk policy openssh | 判定 |
|---|---|---|---|
alpine:3.22 | 3.22.8 | 10.0_p1-r11 | 已達公告的修補版 |
alpine:3.24 | 3.24.4 | 10.3_p1-r2 | 已達公告的修補版 |
同一次檢查也確認了另一件事:映像裡 root 的 /etc/shadow 欄位仍然是 root:*,ldd --version 也確實是 musl。也就是說,「三個條件」在最新映像裡依然成立,只是把關的那一環(sshd 的行為)被補起來了——這正是為什麼「升級後一定要重啟 sshd」不能用『映像已經是新版』來取代。跑在容器裡的舊 sshd 不會因為你重拉 image 而自動換掉。
對絕大多數人來說還有一個好消息:Alpine 公告明確寫著「容器內沒有跑 sshd 的 Docker 使用者不受影響」。真正要緊張的是這幾種人:
| 情境 | 風險 |
|---|---|
| Alpine 主機開著 SSH 對外 | 高,直接符合全部條件 |
| 容器內刻意跑 sshd(自建 jump host、CI runner、NAS 的 sidecar) | 高,等於把主機的洞搬進容器 |
| Alpine 的 tiny-cloud 雲端映像(3.18.11–3.24.3) | 高,alpine 使用者是 * 且有免密碼 doas,等於直接 root |
| 只用 Alpine 當 base image、沒有 sshd | 無 |
| Debian/Ubuntu(glibc + PAM) | 無 |
| OpenWrt 預設(Dropbear) | 無;但自行改裝 openssh 就有風險 |
至於 buildroot 與 yocto,發現者說受影響,但目前沒有獨立確認;OpenWrt 也只是「可自行安裝 openssh」而非預設。這類嵌入式裝置的麻煩在於通常不能升級,關掉密碼登入往往是唯一能做的事。
10.6p2 除了這個洞,還改了什麼
因為是緊急版,多數報導只提安全修正,但它的發布說明其實還有幾項值得順手看的變更:
| 類別 | 內容 |
|---|---|
| 安全 | sshd 避開本次的密碼驗證繞過(本次主角) |
| 功能 | 把後量子混合簽章 ssh-mldsa44-ed25519 列為允許的 CA 簽章演算法 |
| 修正 | 使用 ChannelTimeout 時的不到 1 秒 CPU 空轉 |
| 修正 | 認證前重設 GSSAPIAuthentication,避免前一次嘗試殘留 |
| 修正 | seccomp 沙箱允許 restart_syscall(2)(bz4036) |
| 修正 | 亞塞拜然語系偵測、沒有 openat(2) 平台的 agent socket 建立 |
| 棄用預告 | scp -R(遠端對遠端複製)在 10.6 起會印出棄用警告,未來版本將忽略 |
| 棄用預告 | 不支援 file descriptor passing、又需要 root 才能配置 PTY 的平台(SCO OpenServer 5、QNX 6)將停止支援 |
另外有一段與這次事件直接相關的聲明:OpenSSH 團隊說近期收到大量由 AI 模型或 AI 輔助產出的安全報告,他們歡迎這類回報,但更歡迎附上人工分析與測試案例。更關鍵的是這個觀察——
「我們看到一些案例,是 AI 工具找到的 bug 之後被另一位研究者獨立發現。這意味著不回報的安全研究人員,大概也找得到同樣的洞。」
因此團隊改變發布節奏:不再把修正累積到下次計畫版本,修好就發。2026 年至今出了五個版本,其中四個在七月之後。對維運的人來說,這代表「三個月看一次 OpenSSH」的習慣,會讓你停在有洞的版本上很久。
為什麼一個十年老問題現在才被找到
發現者的方法很土但很有效:把 sshd 用 glibc/musl/PAM/無 PAM/有 shadow/無 shadow 的各種組合編譯起來(12 種建置),每一種丟進約 17,600 次刻意奇怪的登入,比對回應行為。結果每一組都如預期拒絕,只有「musl + 無 PAM」那一組對不該答應的請求說了 yes。
SANS Internet Storm Center 的 Johannes Ullrich 在 11 月 6 日的 Stormcast 裡,把這件事講得最精簡:問題其實不在 SSH,在 musl;並提醒聽眾優先把 Alpine 找出來,同時確認三件事——sshd 有沒有跑、允不允許密碼登入、有沒有編進 libpam(很多容器映像根本沒有)。他也認為 sshhole.com 的逐步解說比播客適合講細節。
OX Security 的評論則點出這件事的荒謬處:真兇不在精巧的密碼學,而在「OpenSSH 與 libc 之間那段沒人想讀的無聊膠水」;而找到它的方法,只是問了一句很樸素的問題——「如果密碼超長會怎樣?」他們的預測是這類老問題還很多,因為 AI 讓「用奇怪方式測試老軟體」的成本大幅下降。
現在該做的三件事
- 找出所有 musl + 無 PAM 又開著 SSH 的主機(Alpine 是最常見的那個),
apk upgrade完 重啟 sshd。外部盤點可以先用「是否接受密碼驗證」把範圍砍一半。 - 把
PasswordAuthentication no當成預設值。金鑰登入成熟之後,密碼登入只是多開一扇門;這次的漏洞正是「一扇本來不該開的門」被證明可以推開。 - 把
grep "Accepted password for"加進例行檢查。這種漏洞最陰險的地方是成功登入的日誌長得跟正常登入一樣,只有當你知道那個帳號根本沒有密碼,才會發現不合理。
站內延伸閱讀
- CrowdSec 完整教學 2026:1.5 萬星開源 IDS/IPS+WAF 安裝實戰——SSH 這類暴力與掃描行為,用 CrowdSec 的行為偵測補上「事後封鎖」那一層。
- Authelia 完整教學 2026:自架 SSO 單點登入與兩步驗證——把對外入口收斂到單一驗證層,比逐台主機開 SSH 好管。
- Nginx 反向代理完整教學 2026:proxy_pass、WebSocket、gzip 到 HTTPS——只開 80/443、SSH 走內網或跳板,是同類事件最有效的結構性防禦。
參考來源
- OpenSSH 10.6p2 發布說明(2026-11-05)
- Damien Miller:urgent security release: 10.6p2(openssh-unix-dev,2026-11-05/06)
- OpenSSH commit 90f9c2f7330b:avoid password authn bypass(含 auth-passwd.c diff)
- Özgün Kültekin:*sshole 漏洞說明與時間軸
- OX Security:OpenSSH Password Authentication Bypass on Alpine Linux(2026-11-05)
- Check Point:A Critical SSH Password Bypass Hits musl-Based Linux(2026-11-05)
- SANS Internet Storm Center Stormcast(2026-11-06,Johannes Ullrich)
- Help Net Security:OpenSSH flaw exploited in the wild just hours after disclosure(2026-11-06)
- Alpine Linux 安全公告:openssh-server password authentication bypass(2026-11-05)
- CVE-2026-123158(MITRE CVE Services 記錄,CVSS 3.1 8.1)
(本文無聯盟連結。套件版本與 CVE 細節以 Alpine 公告、OpenSSH 發布說明與 MITRE 記錄當下內容為準;Docker 映像版本為 2026-11-07 在乾淨環境實測取得;野外利用數據來自 Previdian 的公開頁面與 Help Net Security 報導,截至 2026-11-07 該 CVE 未列入 CISA KEV。)
