パスワードを設定していないアカウントは、本来ログインできません。それが前提です。
ところが、Alpine Linuxなどのmusl環境で動くOpenSSHのsshdには、その前提が崩れる穴がありました。パスワード欄が「*」のアカウントに、257バイト以上のパスワードを1回送るだけでログインできてしまいます。
通称は「*sshole」、CVE-2026-123158、CVSS 3.1は8.1。2026年11月5日、OpenSSH 10.6p2とAlpineの修正版が同時に公開されました。本記事では、この穴がどうやって生まれたのかを1980年まで遡って整理し、影響範囲と今すぐ取るべき対策をまとめます。
何が起きたのか——「パスワードなし」のアカウントが、長いパスワードで開く
まず、現象を正確に押さえます。派手な攻撃ではなく、地味な一致の問題です。
対象は、muslというCライブラリの上で、PAMを使わずにパスワード認証をしているsshdです。Alpine Linuxの標準パッケージが、そのまま当てはまります。
攻撃者は、パスワード欄が「*」になっているアカウント名を知っていればよいだけです。257〜1024バイトの長いパスワードを1回送れば、認証が通ります。
総当たりも、辞書攻撃も要りません。1回の接続で、毎回成功します。Alpineの勧告はそう書いています。
仕組み——「*」と「*」が一致してしまうまで
なぜ、そんなことが起きるのでしょうか。鍵は、3つの実装の重なりにあります。
パスワード照合の、ごく普通の流れ
sshdは受け取ったパスワードをcrypt()でハッシュ化し、/etc/shadowに保存された値と文字列で比べます。一致すればログイン成功です。
パスワード欄が「*」のアカウントには、ハッシュがありません。何を送っても一致しないはずです。
他人のソルトを借りる、という親切
ハッシュの計算にはソルトが要ります。ふつうは、保存されたハッシュの中に埋まっています。
ソルトがないアカウントでは、sshdは別アカウントのソルトを借りて計算します。2016年のCVE-2016-6210への対策として入った挙動です。存在するユーザーを応答時間から見分けられないようにするための工夫でした。
256バイトの壁と、失敗の返し方
muslのcrypt()は、パスワードが256バイトを超えるとエラーを返します。そのとき返す値が「*」です。
保存欄も「*」ですから、比較は一致します。sshdは「パスワードが合った」と判断します。
結果として、パスワードを持たないアカウントが、どんなパスワードでも受け入れる入り口に変わります。「失敗の印」と「ログイン不可の印」が、同じ1文字になったことが起点です。
4つの条件がすべて揃ったときだけ、穴は開く
ただし、どの環境でも起きるわけではありません。Alpineの勧告は、条件を4つに整理しています。
| 条件 | 内容 | 手元での確認 |
|---|---|---|
| 1. sshdがPAMを使っていない | openssh-serverはPAMなしでビルド。openssh-server-pamでもUsePAM noなら同じ | sshd -T にusepamの行が出ない |
| 2. PasswordAuthenticationがyes | OpenSSHの既定値。未設定ならyesになる | sshd -T でpasswordauthenticationを確認 |
| 3. パスワード欄が「*」のアカウントがある | 鍵専用ユーザー、usermod -p ”、musl-nscd経由のユーザーなど | shadowの2列目が「*」の行を探す |
| 4. $5$か$6$のハッシュを持つアカウントが1つある | パスワードを持つユーザーが1人いれば足りる | shadowの2列目が$5$または$6$で始まる行を探す |
4つが揃って初めて影響を受けます。1つでも欠ければ対象外です。
見落としやすいのが4番目です。「自分はパスワードを設定しているから大丈夫」とはなりません。誰か1人がパスワードを持っていれば、パスワードなしのアカウントすべてに届きます。
40年の連鎖——1980年の1文字から、2016年の親切まで
この穴は、単独のバグではありません。4つの妥当な判断が、40年かけて積み上がった結果です。
| 時期 | 出来事 | 何が起きたか |
|---|---|---|
| 1980年 | BSDの/etc/passwdに「*」 | Bill Joyがdaemonアカウントの欄に「*」を置く。ハッシュではないので、どのパスワードも一致しない |
| 1997年 | Single UNIX Specification | crypt()は失敗時にNULLを返すと規定。呼び出し側のNULLチェックが前提になる |
| 2012年8月 | muslがNULLをやめる | 失敗時は「*」を返すよう変更。プログラムが落ちないようにするための配慮だった |
| 2012年8月末 | SHA-512 cryptと256バイト上限 | 20日後の追加。長いパスワードで計算に時間を取られないよう打ち切り、超過時は失敗つまり「*」を返す |
| 2016年7月 | CVE-2016-6210の対策 | ユーザー存在の推測を防ぐため、ソルトがない場合は他アカウントのソルトを借りて計算する |
| 2019年 | AlpineのDockerイメージ | rootにパスワードが無い状態が判明(CVE-2019-5021)。「!」でロックすると、PAMなしでは鍵認証まで壊れた |
| 2022年 | Alpine 3.16 | rootを「*」に変更。鍵は通り、パスワードは通らない。意図どおりに見えた |
| 2026年10月30日 | OpenSSHのレビュー | pick_salt()周辺を修正。問題の4行下には、この時点でも気づかれていない |
| 2026年11月5日 | OpenSSH 10.6p2とAlpine修正版 | 保存欄が4文字未満なら一致しても通さない、という形で修正された |
どこにも「間違い」はありません。それぞれの時点で、理由のある変更でした。
ところが、最終的に「失敗の印」と「ログイン不可の印」が同じ1文字になりました。片方はハッシュの代わり、もう片方は照合の結果です。意味が違うのに、文字列としては同じです。
私はこの年表を読んだとき、設計の正しさと、組み合わせの危うさは別物だと感じました。
影響する環境、しない環境
対象かどうかは、libcとPAMの2点でほぼ決まります。表で整理します。
| 環境 | 影響 | 理由 |
|---|---|---|
| Alpine Linux 3.16〜3.24・edge | 受ける | musl+PAMなしのsshdが標準構成 |
| Alpineの公式クラウドイメージ(tiny-cloud・3.18.11〜3.24.3) | 受ける | alpineユーザーが「*」で、パスワードなしのdoasを持つ |
| OpenWrt・Buildroot・Yocto | 条件次第 | sshdを動かしていれば同じ条件。OpenWrt標準のDropbearは対象外 |
| musl系ベースのDockerイメージ | 条件次第 | コンテナ内でsshdを動かしている場合のみ |
| Debian・Ubuntu・Fedora・RHEL | 受けない | glibc+PAM経由で、crypt()が「*」を返さない |
| FreeBSD・OpenBSD・macOS | 受けない | libcの実装が異なる |
| Windows版OpenSSH Server | 受けない(別件あり) | この穴とは無関係。ただし別の脆弱性が10月13日に修正済み |
コンテナの中でsshdを動かしていなければ、影響はありません。多くの用途は該当しないはずです。
一方、Alpineのクラウドイメージは要注意です。alpineユーザーがパスワードなしでdoasを使えるため、条件が揃うとrootまで到達します。
なお、rootは既定でPermitRootLogin prohibit-passwordです。パスワードでの直接ログインを許していなければ、rootそのものは入口になりません。
今日やること——更新、再起動、そして痕跡
該当しそうなら、対応は3つです。順番に進めてください。
1. まずバージョンを確認する
openssh-serverの版を確認し、自分のブランチの修正版以上にします。Alpine 3.24なら10.3_p1-r2が目安です。
修正入りのポイントリリースは3.21.10、3.22.8、3.23.8、3.24.4です。サポートが終わった3.16〜3.20にも、修正版が用意されました。
2. 更新したら、sshdを再起動する
パッケージを更新しても、動いているデーモンは古いままです。rc-service sshd restartまで実行してください。
すぐに更新できない場合は、PasswordAuthentication noで塞げます。3.19以降なら、/etc/ssh/sshd_config.d/に1行置くだけです。作業前に鍵でログインできることを確かめ、現在のセッションは閉じないでください。
3. 侵害の痕跡を探す
Alpineは/var/log/messagesで「Accepted password for」を探すよう案内しています。パスワードを持たないアカウント名があれば、侵入されたものとして扱います。
あわせてdoasの使用履歴、最近作られたユーザー、authorized_keysも確認してください。
見つけたのは、エージェントの群れだった
発見の経緯も、この穴らしいものでした。
報告者はWakefieldのÖzgün Kültekin氏です。同氏はパスワード処理のような「地味な部分」を、libcとの境界まで含めて調べました。
glibc、musl、PAMあり、PAMなし、shadowあり、なし。考えられる構成でsshdをビルドし、約17,600通りの変なログインを投げ込みました。
同氏の解説では、この総当たりを支えたのはエージェントの群れでした。すべてのビルドが同じ答えを返すなか、muslかつPAMなしの1つだけが「通すべきでないログイン」を受け入れました。
2026年10月30日には、OpenSSHのレビューがpick_salt()を修正しています。問題の4行下には、その時点でも気づかれていませんでした。
責任はsshdにあるのか、libcにあるのか
修正は出ましたが、根本原因の所在は見解が分かれています。
OpenSSH側の立場は、原因はsshdの外にあるというものです。ただしsshd経由で悪用でき、sshd側でも避けられる、としています。
muslのRich Felker氏は「muslのバグではなく、呼び出し側の誤用にすぎない」と述べています。一方で、この仕組みは次のリリースサイクルで変える可能性が高いとも書きました。
運用する側にとっては、sshdの更新で塞がると分かれば十分です。ただ、境界にまたがる不具合は、どちらのテストにも入りにくいという教訓が残ります。
なぜ10年間、誰も気づかなかったのか
2016年から2026年まで、この穴は表に出ていません。理由は4つ。
1つ目は、テストの配置です。OpenSSHはOpenBSDとglibcでテストし、muslはsshdをテストしていません。境界の両側を、それぞれ別の人が見ていました。
2つ目は、PAMという安全網です。多くのディストリビューションはPAM経由で認証するため、「*」のアカウントではcrypt()自体が呼ばれません。
3つ目は、条件の多さです。4つ揃わなければ再現しません。作りたてのサーバーでは、まず起きません。
4つ目は、偶然の近さです。2013年には、Debianの開発者がglibcにmuslの「*」を採り入れるよう提案していました。2019年にDebianがlibxcryptへ移行したとき、返す値は「*0」になりました。1文字違いで、同じ穴を回避していたことになります。
2024年12月のmuslのメーリングリストには、257文字のパスワードでbusybox passwdが壊れる、という投稿があります。同じ「*」、同じ上限です。それでも、誰もsshdの名前を出していません。
これは特別な話ではない——AIが40年分の負債を暴く時代
今回の件は、単独の事故として片づけられません。
2026年10月下旬には、PHPの重大な脆弱性「PHP2Shell」が公表されました。元になったバグは2024年に報告されていましたが、「通常は落ちるだけ」として後回しにされていました。
OpenAIの支援でPHPを監査した企業の研究者が、AIツールを使って2週間で任意コード実行の方法を見つけた、とIEEE Spectrumが伝えています。
40年かけて書かれたレガシーコードが、AIによって次々に暴かれています。今回のssholeも、人間が1つずつ確かめていたら、膨大な時間がかかったはずです。
同時期には、Windows版OpenSSHのCVE-2026-97816(CVSS 9.8)や、IPv4のTCP処理に関するカーネルのCVE-2026-98432も動いています。サーバーの待ち受けまわりは、いま静かに再点検の時期に入っています。
【FAQ】OpenSSHの「*sshole」に関するよくある質問
最後に、問い合わせの多い点をまとめました。
| 質問 | 回答 |
|---|---|
| UbuntuやDebianのサーバーも危ないのですか? | この穴の影響は受けません。glibc+PAM経由で認証するため、crypt()が「*」を返す経路を通りません。ただし同時期の別の脆弱性は別途確認が必要です。 |
| 「*」と「!」は何が違うのですか? | どちらもログイン不可を示す記号ですが、扱いが異なります。PAMなしの環境では「!」が鍵認証まで止めてしまうため、Alpineは2022年から「*」を使っています。今回の穴は、その「*」が起点です。 |
| コンテナを使っていても影響を受けますか? | コンテナ内でsshdを動かしていなければ影響はありません。動かしている場合は、ベースイメージのlibcとPAMの設定を確認してください。 |
| 修正版に上げれば、設定はそのままで大丈夫ですか? | 更新後にsshdの再起動が必要です。再起動しなければ、古いデーモンが動き続けます。あわせて侵害の痕跡も確認してください。 |
| すでに侵入されていた場合、何を見れば分かりますか? | 「Accepted password for」の記録に、パスワードを持たないアカウント名が出ていないかを見ます。doasの履歴、新しいユーザー、authorized_keysの変更も手がかりになります。 |
| この穴に名前が付いたのはなぜですか? | 「*」とSSHを掛けた通称です。研究者自身の表記は「*sshole」で、CVE番号はCVE-2026-123158です。名前の軽さに反して、CVSSは8.1の高い深刻度です。 |
まとめ——「入れないつもり」のアカウントが、いちばんの入口になる
鍵だけで入るつもりで作ったアカウントが、パスワードを持たないせいで入口になっていました。
私は今回、セキュリティの弱点は「設定ミス」よりも「正しい判断の積み重ね」から生まれるのだと理解しました。4つの設計判断は、どれも当時の理由がはっきりしています。
できることは限られています。libcとPAMの構成を知り、opensshを更新して再起動する。それが済んだら、shadowの「*」と「!」がどう並んでいるかを一度眺めておく。
まずは自分の環境で、sshdの設定とlibcの種類を確認してください。小さな違和感を放置しない運用を、今日から始めましょう。


コメント