Sshole: Musl Linux上のOpenSSHにおけるパスワード認証の脆弱性
要約
Musl libcを使用するLinuxディストリビューション(Alpine Linuxなど)のOpenSSHには、2016年から存在する脆弱性があります。これは、パスワード認証が無効化されているはずのrootアカウントやキー認証専用ユーザーなどが、256文字を超える任意のパスワードを受け入れてしまうというものです。この脆弱性は、Musl libcのcrypt関数の挙動とOpenSSHのパスワードチェック処理の組み合わせに起因しており、特定の条件下で意図しないログインを許可する可能性があります。
全文翻訳
*sshole
muslベースのLinux(Alpine, Openwrt, Buildroot, Yocto、および多くのDockerイメージ)で動作するOpenSSHには、2016年から存在する小さな問題があります。
パスワード認証を一切受け付けるべきではないアカウント(root、キー認証のみのデプロイユーザー、LDAPユーザー、AlpineのAWSおよびAzureイメージのデフォルトユーザーなど)が、256文字を超える任意のパスワードを受け付けてしまいます。
そう、任意のパスワードです。はい、知っています。はい。
1980
最初のスター
昔のUNIXでは、パスワードハッシュはユーザー名の隣、/etc/passwdにそのまま保存されていました。誰でも読めるファイルです。
1980年、ビル・ジョイはdaemonというアカウントのためにそこに*を置きました。誰もそれをログインに使用するはずはありません。
*はハッシュではないため、どのパスワードも一致しません。UNIXは今日でもこれを実行しています。
3bsd · /etc/passwd · 1980
Addressgithub.com/dspinellis/unix-history-repo/blob/BSD-3/etc/passwd
[画像: daemon:*:1:1:The devil himself:/:]
次のフィールドは実際の名前です。彼は「悪魔そのもの」と書きました。
1990年代
sshdがパスワードをチェックする方法
さて、最近ではハッシュは/etc/shadowに保存され、$6$abc$k2P9…のように見えます。$6$はsha512を意味し、abcはソルト、残りは実際のハッシュです。
パスワードを入力すると、sshdはその同じソルトでハッシュ化し、一致するかどうかを確認します。一致すればログインできます。それだけです。
そして、フィールドが単に*の場合はどうなるでしょうか?一致するものはありません。何を試しても、ログインできません。
1990年代
細則
もう一つルールがあります。crypt()がその仕事をできない場合、NULLを返します。例えば、ソルトがシステムにないアルゴリズムを要求した場合、計算するものがなく、ハッシュの代わりにNULLが返されます。
the single unix spec · crypt() · 1997
Addresspubs.opengroup.org/onlinepubs/7908799/xsh/crypt.html
[画像: RETURN VALUE: Upon successful completion, crypt() returns a pointer to the encoded string. Otherwise it returns a null pointer and sets errno to indicate the error.]
非常に明確です。
ただし、多くのプログラムはNULLをチェックしません。単にcrypt()が返すものと保存されているフィールドを比較し、NULLにヒットしてクラッシュします。
2012年
cryptがNULLを返さなくなる
そこでmusl(Alpineの基盤となるlibc)は、crypt()がNULLを返さなくなることにしました。失敗した場合、代わりに1文字を返します。その文字は実際のパスワードと一致することはありません。そのため、プログラムはクラッシュせず、間違ったパスワードとして扱われます。
さて、そのためにどの文字を選んだと思いますか?
""
そうです、スターです。再び。
musl · b3c4cc12 · aug 2012
Addressgit.musl-libc.org/cgit/musl/commit/?id=b3c4cc12
[画像: make crypt return an unmatchable hash rather than NULL on failure. always "succeeding" but giving an unmatchable hash is reportedly a better behavior than failing on error.]
「報告によると。」
musl · b3c4cc12 · the diff
Addressgit.musl-libc.org/cgit/musl/commit/?id=b3c4cc12
[画像: diff of crypt_des.c: removed the comment 'Should not happen.' and 'return NULL;', added: return (setting[0]=='') ? "x" : "";]
「起こるはずがない」は削除され、NULLも削除され、*が追加されました。
2012年
3週間後
その後muslはsha512 cryptを追加しました。sha512cryptはパスワードが長いほど遅くなるため、誰かが巨大なパスワードを送信してサーバーを長時間占有する可能性があります。そのため、muslはそれを256バイトに制限し、それ以上のものは拒否します。
musl · 88bf5a8a · aug 29 2012
Addressgit.musl-libc.org/cgit/musl/commit/?id=88bf5a8a
[画像: add sha256/sha512 crypt. Rich Felker, 2012-08-29.]
ちなみに、*コミットの20日後です。
musl · crypt_sha512.c @ 88bf5a8a
Addressgit.musl-libc.org/cgit/musl/tree/src/misc/crypt_sha512.c?id=88bf5a8a
[画像: key limit is not part of the original design, added for DoS protection. #define KEY_MAX 256. if (i > KEY_MAX) return 0;]
したがって、256バイトを超えるとcryptは失敗します。cryptが失敗すると…*を返します。
musl · crypt_sha512.c
Addressgit.musl-libc.org/cgit/musl/tree/src/crypt/crypt_sha512.c
[画像: on error "*" is returned.]
文字通り、コメントにはずっとそう書かれています。
2016年
sshdがボブのソルトを借りる
CVE-2016-6210。存在しないユーザーについてsshdに問い合わせると、ハッシュするものがないため、すぐに「いいえ」と返されます。実在するユーザーは数ミリ秒長くかかります。そのため、時間を計測するだけで誰が存在するかを知ることができます。
その修正はかなり面白いです。実際のマッチするハッシュがない?sshdは他のアカウントのソルトを取得し、パスワードをハッシュ化して、同じ時間だけ無駄にします。それでも「いいえ」ですが、遅い「いいえ」です。
openssh-commits · jul 2016
Addresslists.mindrot.org/pipermail/openssh-commits/2016-July/005428.html
[画像: Determine appropriate salt for invalid users.]
「無効なユーザーのため。」そして、実在する*を持つユーザーのためにも、判明しました。
2019年 → 2022年
修正のための修正
2019年。 AlpineのDockerイメージにはrootパスワードが全く設定されていなかったことが判明しました。CVE-2019-5021。Alpineはrootを!でロックすることで修正しました。
しかし、PAMなしでは、!はSSHキーも無効にしてしまいます。そして、クラウドイメージはキーでログインします。
2022年。 Alpine 3.16はrootを*に変更しました。依然としてロックされていますが、キーでのログインは再び可能になりました。
alpine aports · fbe926af · shipped in 3.16, 2022
Addressgitlab.alpinelinux.org/alpine/aports/-/commit/fbe926af.patch
[画像: this still forbids password login but allows logins via ssh keys with pam disabled. # make sure root login is disabled. root:! becomes root:*]
ナレーター:それはしませんでした。
2016年 → 2026年
10年間の静寂
なぜ誰も気づかなかったのでしょうか?まあ、誰かが気づいたのかもしれません。誰かがパスワードプロンプトに間違ったものを貼り付けたのかもしれません。Slackメッセージの半分やSSHキー全体を貼り付けて、それがログインを許可したのかもしれません。あなたはパスワードプロンプトに間違ったものを貼り付けたことがないのですか?素晴らしい。パスワードが設定されていない新しいボックスは影響を受けません。shadowの*は、まず間違った試行が必要です。opensshはOpenBSDとglibcでテストされ、muslはsshdをテストしません。そしてほとんどのディストリビューションはPAMを経由しますが、PAMは*に対してcryptを呼び出すことさえありません。
そのため、知る限り、誰も気づきませんでした。知る限り。
しかし、いくつかの人々は本当に近づきました。
libc-alpha · debian #714219 · 2013
Addresssourceware.org/legacy-ml/libc-alpha/2013-07/msg00046.html
[画像: The scheme of returning * or x, which are never valid matches, comes from dalias from musl, and I think it is the best compromise/solution.]
Debianの開発者がglibcにmuslの*をコピーするように依頼します。6年後、Debianはlibxcryptに移行し、*0と表示してバグを閉じます。あと一文字でした。
musl mailing list · dec 2024
Addressopenwall.com/lists/musl/2024/12/31/2
[画像: Sure there's no reason it couldn't have been 257 or 300 or whatever, but either way you'd get things like this when software has bugs in handling the condition.]
このスレッドは、busybox passwdが257文字のパスワードで壊れることについてです。同じ*、同じ制限。誰も「sshd」とは言いません。
openssh-commits · 30 oct 2026
Addresslists.mindrot.org/pipermail/openssh-commits/2026-October/013271.html
[画像: fix fake password salt generation for bcrypt hashes]
セキュリティレビューがpick_salt()を修正します。バグはその4行下にあります。私が発見する4日前でした。
2026年11月
どうやって遭遇したか
私はOpenSSH、主にlibcと通信するような退屈な部分、パスワードコードなどをいじっていました。人々は暗号化の方に注目し、これらの部分にはあまり目を向けません。そのため、考えられるあらゆる方法(glibc, musl, pam, no pam, shadow, no shadow)でsshdをビルドし、17,600件の奇妙なログインを試しました。奇妙なハッシュ、壊れたアカウント、長すぎるパスワード。
ああ、「私」と言いましたか?すみません。私はエージェントの群れを使っていました。たくさんです。それについては触れないでおきましょう。
すべてのビルドがすべてのログインで一致しました。PAMなしのmusl以外は。そちらは、そうすべきでないものに「はい」と言いました。
tldr
[画像: six panel comic: 1980 daemon gets a star; 2012 musl crypt returns star instead of NULL; 2016 openssh borrows bob's salt; 2021 alpine changes root from ! to star, narrator: it did not; 2016 to 2026 a cat sleeps on a server; 2026 me and some agents, 300 A's give uid 0]
試してみる
Debianボックス1台、Alpineボックス1台。何かを入力してみてください。すべてブラウザ内で実行され、何も送信されません。
256
1024
0 bytes
debian · glibc
crypt(pw, bob's salt)
alice's field
alpine · musl
crypt(pw, bob's salt)
alice's field
影響を受けていますか?
- あなたのsshdはmuslで実行されていますか?(Alpine, OpenWrt, Buildroot, Yocto)
- PAMを使用していませんか?(Alpineの
openssh-serverはデフォルトでは使用しません。あるいはUsePAM noを設定したかもしれません) - パスワードログインはまだ有効ですか?(デフォルトでは有効です)
- パスワードが単なる
*のアカウントがありますか?(キー認証のみのアカウント、LDAPユーザー、Alpineのrootなど) - そして、別の実パスワードを持つアカウントがありますか?
- その場合、その
*アカウントは257文字から1024文字までの任意のパスワードを受け入れます。
Alpineの公式AWSおよびAzureイメージでは、デフォルトのalpineユーザーがパスワードなしのdoasを持っています。つまり、それはrootです。
Debian、Ubuntu、Fedora、またはglibcを使用するその他のOSで、PAMを経由していますか?Dropbear?OpenBSD?あなたは安全です。
今後どうするか
あなた
# アップデート。openssh 10.6p2 で修正されます。
$ apk upgrade openssh
# まだアップデートできない場合?パスワードログインを無効にしてください。
$ echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/00-no-passwords.conf
$ rc-service sshd restart
もう一つ
ハッキングされましたか?確認してください。
root@your-box
そのような行を見ましたか?パスワードのないアカウントがパスワードを受け入れた。
もしそうなら…
残念です。
あなたはハッキングされました。
*ssholeによって