HN 日本語サマリー

← 一覧へ戻る
セキュリティ

Sshole: Musl Linux上のOpenSSHにおけるパスワード認証の脆弱性

Sshole (sshhole.com)

621 pointsby josemrb173 コメント

要約

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によって