一個班邏輯漏洞讓 257 個 A 登入

ssh:musl + sshd fake-auth 組合出 257 到 1024 字元任意密碼登入

懶人包:https://static.blog.derekhsu.net/ssh-test-hn.html

2026年11月,安全研究員伊茲岡庫爾特金(Ozgun Kultekin)公開一個簡單得不像漏洞的 SSH 登入繞過。他的網站 sshhole.com 用一連串歷史事件拼出因果鏈,標題的後綴故意把 moth 打成 hole,原文是「*sshole」。HN 討論在 2669 分的情況下,這段文字仍在往下漲,代表社群仍在揉它。以下把他講的來龍去脈翻譯過來,同時把 HN 網友在現場踩到的坑補進來,所以讀者不用再往回翻 635 則留言。

攻擊長什麼樣

問題的觸發條件很苛刻,少一條都不行。備援情境是:

  • sshd 由 musl libc 編譯 — glibc 上不會出事。
  • sshd 沒有走 PAM 驗證 — 有 PAM 的環境直接免疫。
  • 密碼登入開啟 — 關閉就沒事。
  • 有一個 “*” 帳號 — 也就是密碼欄只放一個星號(例如只用 SSH key 的使用者),本來就不打算讓人用密碼登入。
  • 同時存在另一個有真密碼的帳號 — 用來提供雜湊鹽(salt)給假驗證借用。

五個條件一碰上,”*”-帳號會接受長度在 257 到 1024 個字元之間的任意密碼。實測網友發現,第一次輸入長密碼會被拒,第二次開始才放行。

修補方已在 OpenSSH 10.6p2 上線。HN 上有人報出編號 CVE-2026-123158,Alpine 官方已發布對 3.16 到 3.24 與 edge 的安全更新。

第一顆星號(1980年)

最早的 Unix 把密碼雜湊直接放在 /etc/passwd,人人可讀。Bill Joy(比爾·喬伊)在一個叫 daemon 的帳號上填了個星號,密碼欄後面的真實姓名欄寫著 “The devil himself”(道士本士),玩笑的意思是:這個帳號任何人都不該登入。星號不是雜湊,任何輸入都不可能比對上。即使是今天,Unix 仍保留這個慣例。

[3BSD · /etc/passwd · 1980](https://github.com/dspinellis/unix-history-repo/blob/BSD-3/etc/passwd)

musl 為了避免 crash,選了最糟的哨兵字元(2012年)

雜湊搬到 /etc/shadow 後,crypt() 的規格寫著「失敗回 NULL」。但許多程式沒檢查 NULL,直接拿回傳值去比對,結果 segfault(分段錯誤、整行程式崩潰)。musl 決定「不會再回 NULL」,失敗就回一個永遠不可能命中真實密碼的字元。

它選的字元是 "*" — 又是星號。作者在原始程式碼裡留了註「Should not happen」(不應發生),後來連註都一起刪掉,錯誤訊號從 NULL 變成 "*"。

2012年8月 · musl · b3c4cc12

三週後,musl 又加了數字攔截。因為 sha512crypt 算得越長越慢,怕被 DoS(阻斷服務攻擊)用長密碼榨乾,crypt() 決定超過 256 bytes 直接拒絕。關鍵的坑是:拒絕發生時,它仍然回傳 "*"。後面的註解白紙黑字寫「on error “” is returned」(出錯回 ““)。

[musl · crypt_sha512.c](https://git.musl-libc.org/cgit/musl/tree/src/crypt/crypt_sha512.c#n8)

OpenSSH 的假驗證埋單(2016年)

CVE-2016-6210:問一個不存在的使用者,sshd 立刻回「no」,但真實帳號要多花幾毫秒算雜湊,時間差就洩漏使用者是否存在。OpenSSH 的修法是:沒有真實雜湊可查時,借別的帳號的 salt 假裝算一次,時間拉平,還是回「no」。

借 salt 時 sshd 不記得自己在假驗證。模糊比對以後又碰到 musl 長密碼回 "*" 的問題,sshd 就拿那個 "*" 去和 “*” 帳號比對 — 一致,放行。

OpenSSH · 2016年7月

Alpine 三年間的鎖法(2019→2022年)

Alpine 的容器映像原本 root 沒有密碼,CVE-2019-5021 逼它改用 ! 上鎖。結果不走 PAM 時,! 連 SSH key 也一併擋掉。2022年 Alpine 換成 * — 密碼仍鎖,key 恢復。原作者以為這下安全了,結果把真正的漏洞露了出來。

alpine · fbe926af,3.16 起

glibc 差了一字(2013→2024年)

Debian 的開發者 2013 年建議 glibc 照抄 musl 的 "*",六年後 Debian 切到 libxcrypt,用了 "*0" 並關閉此事。差一個字元,就差一個漏洞。2024年12月,musl 郵件串又有人碰到同款問題:busybox passwd 對 257 字元密碼崩潰,沒有人提及 sshd,特徵的 "*" 與 256 上限雷同。

libc-alpha · Debian #714219

2026年10月30日,OpenSSH 的安全審查修好 pick_salt() 的問題,bug 在它下方四行沒人動;四天後 ss hh o le 找到它。

OpenSSH · 2026年10月

個人經驗與-agent swarms

作者說他大部分時間在啃低階的密碼程式碼,卻是用 agent swarms 丟出約 17,600 次怪異登入測出來的。把 glibc、musl、PAM、不走 PAM 有或無、全雜湊、各式壞帳號、密碼過長到過短的組合全測過一輪,多數 build 一致,只有「musl 不要 PAM」那組不該說 yes 卻說 yes。

網站上那六格簡報,合起來補上時間軸。HN 網友大多認為這些重述有價值,反方認為把簡單事花了太多的 scroll 動畫。

為什麼沒人發現(十年盲點)

三個條件疊加讓這件事失去了觀測機會。第一,乾淨的 box 還沒設定任何密碼可以先排除;第二,* 帳號要剛中攻擊前先經過一次錯誤嘗試,最終看起來像偽陽性;第三,OpenSSH 在 OpenBSD 與 glibc 測,musl 測 sshd 不歸 musl 測,多數 distribution 走 PAM,而 PAM 對 * 根本不會呼叫 crypt。所以偵測面上是盲的。

實際踩到的玩家也證明不容易中:兩個人都從官方 Alpine 最新映像直接重現不出來,當天已更新;有人改用降級 OpenSSH 才重現。在 Alpine 3.15 還必須手動把 ! 改成 * 才觸發。Forgejo 實測也要同時開密碼登入、加上一個有真密碼的其他帳號,預設才安全。

自查與緩解

受影響的那一類:musl 系統例如 Alpine、OpenWrt、Buildroot、Yocto;sshd 不走 PAM(Alpine 的 openssh-server 預設就是);密碼登入開啟;影子檔裡有一個只填 * 的帳號;同時又有另一個有真密碼的帳號。


# 升級即可 — OpenSSH 10.6p2 已修
apk upgrade openssh

# 暫時擋不了:先關密碼登入
echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/00-no-passwords.conf
rc-service sshd restart

是否已中獎:github.com/oz9un/sshole —— 若像「no password 帳號接受了密碼登入」這樣的紀錄出現,你這邊可能已中。不管怎麼說,這件事提醒的不是哪個函式壞掉,而是 musl 一邊修「沒處理 NULL 就 crash」的時候,預先就把永久的迴聲送到了安全邊界。一個本來用來告訴「這裡沒東西」的記號,沒想在十年內會變成鑰匙。

後續:OpenSSH 10.6p2 官方修補與 HN 討論整理

2026年11月5日,OpenSSH 發布 10.6p2。官方發行說明的安全條目,與本文描述的情境直接對應:在 PAM 停用、使用 musl libc 的 Linux 系統上,sshd 密碼驗證可被繞過;作者認為根因在 sshd 之外,但可在 sshd 內避開。回報者為 Özgün Kültekin(Wakefield)。

同版另有三項修正:允許後量子混合簽章 ssh-mldsa44-ed25519 作為 CA 簽章演算法、修正 ChannelTimeout 下的 CPU 空轉、修正數個舊平台的編譯與 agent socket 問題。

HN 上實質討論的 story 不是 openssh.org 那則(249 分、5 則留言),而是 Damien Miller 的 Mastodon 貼文(180 分、223 則留言,抓取時 222 則)。熱串依子樹大小排序,主要分成三組:

  • 語言之爭:Rust 的 Result 能否防這類錯誤。多數人認為這是邏輯錯誤,Rust 也擋不住,但 sentinel 字串在 Rust 中相對少見。
  • 根因設計:musl 原本回傳 NULL,後改為 *,目的在避免程式未檢查 NULL 就 crash。網友 @pdw 表示僅 OpenBSD 與 Fedora 的 crypt 仍回 NULL(據其所述,未經查證)。
  • 實務建議:PasswordAuthentication no 是最快的緩解方式。爭議在於遠端恢復與是否改以 WireGuard 內網隔離。

完整翻譯與數字覆蓋範圍見懶人包:

懶人包:https://static.blog.derekhsu.net/openssh-10-6p2-hn-v2.html

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *