Alpine LinuxのOpenSSHパスワード認証バイパス『*sshole』― crypt()の『*』が一致した理由と対処
投稿日:2026.11.06
CATEGORY
- Web開発
Alpine Linux で動く OpenSSH の sshd に、パスワードを設定していないはずのアカウントへログインできてしまうバグが見つかりました。Alpine Linux 公式と OpenSSH 公式は2026年11月5日、このバグの詳細と修正版を同時に公開しています。発見者はこのバグに「sshole」という通称を付けました。原因は、crypt() という関数が「パスワードが一致しない」ことを示すために返す記号「」を、sshd が別の意味の「*」と取り違えてしまう組み合わせです。CVE番号は本稿執筆時点ではまだ割り当てられていません。
影響を受けるのは、PAM(Pluggable Authentication Modules、Linux の認証処理を差し替え可能にする仕組み)を使わずに sshd を動かしている Alpine Linux の一部です。Docker コンテナ自体で sshd を動かしていなければ対象外で、UsePAM yes の設定でも対象外です。
*sshole、パスワードなしアカウントへのログインが成立する
Alpine Linux の sshd は、PAM を使わない設定(openssh-server そのもの、または UsePAM no で動かす openssh-server-pam)では、パスワードの一致判定に crypt() という関数を使っています。発見者の Özgün Kültekin 氏は、ある条件がそろうと、鍵認証専用のはずのアカウント(パスワードを設定していないアカウント)に、特定の長さの文字列を送るだけでログインできることを確認しました。
対象になる Alpine Linux のリリースは 3.21 から 3.24・edge、さらにサポートが終了済みの 3.16 から 3.20 まで広く及びます。tiny-cloud 向けのイメージ(qcow2・VHD・AMI、3.18.11 から 3.24.3)も対象です。これらのイメージでは、クラウド上に標準で用意される alpine ユーザーのパスワード欄がもともと「*」になっており、doas をパスワードなしで実行できる構成のため、影響がそのまま現実のリスクになります。
crypt()の『*』二重発生 ― なぜ起きたかの仕組み
原因は、crypt() が全く別の2つの場面で、同じ記号「*」を返すことです。この2つが重なったアカウントの組み合わせで、sshd はパスワードが一致したと判定してしまいます。
「」をログイン不能の印として使う慣習は、1980年の BSD に由来します。パスワード欄に「」が入っていれば、crypt() がどんな入力を計算してもその値と一致しないため、誰もログインできません。この慣習自体は、musl 環境でも Alpine Linux でも長く守られてきました。

ここに2つの変更が重なります。1つ目は、musl の crypt() が2012年8月に、計算が失敗したときの戻り値を「」に変更したことです。もともと不一致を示す記号だった「」が、今度は「エラー」を示す記号としても使われるようになりました。2つ目は、musl の crypt() が256バイトを超える入力パスワードを拒否し、同じ「」を返す仕様も持っていることです。つまり、攻撃者が257〜1024バイトの長いパスワードを送ると、crypt() は正規の計算をせずに「」を返します。
もう1つの要因が、2016年の CVE-2016-6210 対応です。この対応で OpenSSH は、保存されているパスワードハッシュの salt(ハッシュ化の際に加える追加データ)が短すぎるアカウントでは、別のローカルアカウントの salt を借用する実装になりました。この実装のもとでは、パスワード未設定アカウント(保存値「」)への長いパスワードでのログイン試行が、$5$ または $6$ ハッシュを持つ別のアカウントの salt を借用した計算を経由し、最終的に crypt() が返す「」(長すぎる入力へのエラー応答)と、保存されている「*」(パスワード未設定の印)が文字列として一致します。sshd はこの一致を「パスワードが合っている」と解釈し、ログインを許可します。
対象になる条件、対象外になる条件
次の3つがすべてそろったときに成立します。
- パスワード欄が「*」のアカウントが存在する(鍵認証専用ユーザー・adduser で作った無パスワードユーザー・ディレクトリサービス経由のユーザー・tiny-cloud の alpine ユーザーなど)
- $5$ または $6$ ハッシュを持つ別のローカルアカウントが1つ以上存在する
- 攻撃者が257〜1024バイトの長いパスワードを送信する
対象外になるのは次のケースです。
- sshd を動かしていない Docker コンテナ
- PasswordAuthentication no のシステム
- UsePAM yes で動かしている openssh-server-pam
- パスワード欄が「!」でロックされたアカウント
- cloud-init で PasswordAuthentication no を設定済みのクラウドイメージ
発見者は、同じ musl libc を使う OpenWRT・Buildroot・Yocto にも同種の影響が及ぶ可能性を挙げていますが、公開されているページでは Alpine ほど具体的な検証結果を示していません。これらの環境を運用している場合は、可能性として念頭に置きつつ、別途自分で確認する必要があります。
対処 ― 更新とパスワード認証の停止
最優先の対処は、パッケージを更新して sshd を再起動することです。apk upgrade -U -a を実行したあと、rc-service sshd restart で sshd を再起動します。パッケージを更新しただけでは、すでに動作している sshd プロセスは置き換わりません。
修正版のパッケージ番号はブランチごとに異なります。
| ブランチ | 修正後のバージョン |
|---|---|
| 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 |
すぐに更新できない場合は、パスワード認証そのものに南京錠をかける回避策があります。/etc/ssh/sshd_config.d/ に PasswordAuthentication no と書いたファイルを置くと、crypt() の判定自体を通らなくなるため、この脆弱性も含めて無効化されます。ただし、鍵でログインできることを事前に確認してから設定を反映する必要があります。既存の接続を切ってしまうと、鍵が使えない環境ではサーバーに入れなくなります。設定が効いているかどうかは sshd -T を実行し、出力に passwordauthentication no と表示されることで確認できます。

更新または回避策を適用したあとは、すでに侵害されていないかの確認も必要です。/var/log/messages を開き、alpine・root・鍵認証専用ユーザーなど、パスワードを設定していないはずのアカウントに対して「Accepted password for」というログが残っていないかを確認します。doas の使用履歴、見覚えのない新規ユーザーやファイル、authorized_keys の内容も合わせて確認する対象です。
まとめ
今日からできる最初の一歩は、自分が管理している Alpine サーバーで sshd -T | grep -iE "passwordauthentication|usepam" と apk info -v openssh-server を実行し、UsePAM・PasswordAuthentication の設定とインストール済みのバージョンを確認することです。対象の条件(パスワード欄が「*」のアカウントの有無・$5$/$6$ ハッシュを持つアカウントの有無)に当てはまるなら、更新を先に進めます。
この脆弱性の本体は、攻撃コードの精巧さではなく、1980年の BSD の慣習・2012年の musl の仕様変更・2016年の OpenSSH の salt 借用という、互いに無関係な3つの変更が積み重なって、意味の違う2つの「*」が同じ文字列として扱われたことです。
関連
自社サイトの状態が気になったときは、URL を入れるだけで使える無料の診断ツールを公開しています。
出典
※ この記事は AI を使用しています。Leadeas が自社開発した AI エージェント基盤で下書きを作成し、人間のレビューを経て公開しています。AI ネイティブ開発会社として自社の技術をそのまま実演する目的で、この手法を用いています。(詳しくは AI 利用ポリシー)

