Xz: Linux Landlock を無力化したたった一文字
(git.tukaani.org)- xz.git の CMakeLists.txt 変更は、Landlock サンドボックス検出をヘッダーの存在確認から実際のコンパイルテストへ切り替える過程で、テストコード内に単独の
.文字が入り込んだ事例である - 新しい検査は、
linux/landlock.hが存在していても必要な syscall 定義 がない可能性のあるシステムを除外するために追加された - コンパイルテストは
<linux/landlock.h>、<sys/syscall.h>、<sys/prctl.h>をインクルードし、prctl、SYS_landlock_create_ruleset、SYS_landlock_restrict_self、LANDLOCK_CREATE_RULESET_VERSIONを参照する - 従来の基準だった
HAVE_LINUX_LANDLOCK_Hは HAVE_LINUX_LANDLOCK に変更され、SANDBOX_COMPILE_DEFINITIONとSANDBOX_FOUNDの設定もこの結果に従う - このコミットは Autotools と CMake ビルドの Linux Landlock feature test 修正であり、提示された diff は CMake 側の変更を示している
CMake における Landlock 検出方式の変更
- 対象ファイルは xz.git の CMakeLists.txt で、変更箇所は
# Sandboxing: Landlockブロックである - 従来の CMake ロジックは、
ENABLE_SANDBOXがONまたはlandlockで、かつSANDBOX_FOUNDでない場合にcheck_include_file(linux/landlock.h HAVE_LINUX_LANDLOCK_H)でヘッダーの存在だけを確認していた - 変更後は
check_c_source_compilesにより、小さな C コードを実際にコンパイルして Landlock の利用可否を確認する- 一部のシステムでは、
linux/landlock.hがあっても Linux Landlock に必要な syscall 定義が存在しない場合がある - テストコードは
<linux/landlock.h>、<sys/syscall.h>、<sys/prctl.h>をインクルードする my_sandbox()内でprctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)、SYS_landlock_create_ruleset、SYS_landlock_restrict_self、LANDLOCK_CREATE_RULESET_VERSIONを参照する
- 一部のシステムでは、
単独文字と変数名の変更
- 挿入されたコンパイルテストコードの include ブロックの次に、単独の
.行がある - 結果変数は
HAVE_LINUX_LANDLOCK_Hから HAVE_LINUX_LANDLOCK に変更された - 条件文も
if(HAVE_LINUX_LANDLOCK_H)からif(HAVE_LINUX_LANDLOCK)に変更された SANDBOX_COMPILE_DEFINITIONの値は"HAVE_LINUX_LANDLOCK_H"ではなく"HAVE_LINUX_LANDLOCK"を使う- その後の
SANDBOX_FOUND ON設定は維持されている
1件のコメント
Hacker News の意見
答え: https://git.tukaani.org/?p=xz.git;a=commitdiff;h=f9cf4c05edd...
Linux の Landlock アクセス制御システムをよく知らないなら、説明はここにある: https://docs.kernel.org/userspace-api/landlock.html
xz 公式(おそらく……)のインシデント対応ページ: https://tukaani.org/xz-backdoor/
ところが diff の左端付近にくっついていて見分けにくい小さな ピリオド1つのせいで、C コードが常に無効になり、その結果 Landlock が常にオフになっていた、と?
卑劣なほど見事で、見事なほど卑劣だ。最初に読んだときは自分も見落とした
隠すなら Unicode の類似文字を置き換える、もっと良い方法もある。フォントによってはピクセル単位で同じに見えるものもある
自分が流れを見落としているのかもしれないが、ここで故意だと確定したのか?
注意深くざっと見ていたら、明らかにあってはいけない場所にピリオドが見えたので、「思ったほど難しくないな」と思った
画面を軽く触ったら、そのピリオドが動いた
くそったれの モニターのホコリ
システムのセキュリティが、こんなにゆるい 正しさの連鎖に依存しているなんて信じられない。防げるポイントはいくらでもあった
+ # A compile check is done here because some systems have+ # linux/landlock.h, but do not have the syscalls defined+ # in order to actually use Linux Landlock.そういうシステムのヘッダーを修正して、明示的に除外されるようにすべきだ。ヘッダーが自分の機能を宣言しないなら、何の意味があるのか?
それに、バイナリ blob が作られた後(オープンソースからコンパイルされたとしても)に、セキュリティが保たれているか確認するテストが一つもない理由も分からない
Web サイトの決済機能だって、主要機能に対するエンドツーエンドテストなしにはデプロイしないはずだ。機能追加が安定性より優先されるという 優先順位の不一致があるように見える
修正が単に
.を削除するだけでないことを願う。HN の別の投稿で、.の削除を示しているのを今見たところだヘッダーの修正や追加テストでは防げなかっただろう。そもそもヘッダーが壊れていた兆候はなく、追加テストも別の方法で損なわれたり、リリース tarball で無視されたりし得たからだ
[1] https://git.tukaani.org/?p=xz.git;a=commit;h=328c52da8a2bbb8...
ユーザーが GCC 9.4.0 でコンパイルしようとする可能性があり、そのヘッダーに依存する機能はアプリケーションにとって中核的ではなかったため、ビルドがヘッダーの破損を検出した場合は、その機能を単にオフにして警告を出した
xz に戻ると、セキュリティが最優先事項でないなら、オプション機能の失敗を無視して続行するのも合理的に見える。もちろん事後的には非難しやすいが、その文脈を除けば、完全に筋が通らない判断というわけではない
うわ、彼の最後のコミットは セキュリティ報告をさらに悪化させている: https://git.tukaani.org/?p=xz.git;a=commitdiff;h=af071ef7702...
Landlock はどこで/どのように使われる想定だったのだろう?
圧縮/展開のような汎用ライブラリでは、実際には使いにくそう。ライブラリはプログラムが何をすべきか、何を制限すべきかを知り得ない
プログラムならもっと明確かもしれない
sshd への攻撃では liblzma をライブラリとして使っていた。なら Landlock を無効にすることは無関係に見えるのでは? まだ見つかっていない悪いコードがさらにあるか、後で入れる計画だったという合図なのだろうか?
Landlock を取り除けば、デーモンが自分自身をロックしなくなり、悪用段階に到達したときにペイロードの実行が容易になる
この2つが無関係だとは思わない。すでにペイロードを念頭に置いていて、これが障害になると気づいていたように見える
この件は非常に混乱している。信じがたいほど巧妙な部分もあれば、かなり粗く見える部分もある
アイデアとしては、ライブラリが複雑な処理を別スレッドで開始し、ユーザーコードが呼び出した API の中でそのスレッドを待つ、というもの。スレッドは自分で Landlock を適用してから処理を始める。何か問題が起きればコードは隔離される
もちろんこの場合は、サンドボックスが攻撃者に制御されるので機能しない。攻撃者が単に無効化するか、本来より弱くできるからだ。それでも別の方法で行うことはできる
いったいここで何を狙っていたのだろう? 後からさらに多くのバックドア、よりもっともらしく否認できるバックドアを入れようとしていたのか? 私には oss-fuzz もこの変更も、見つかったバックドアを実際に発見することはなかったように思える
それなのに、なぜバックドアの卵を1つのかご、つまり1つのライブラリに集中させたのだろう?
さらに、ライブラリ自体をバックドア化するのではなく、それを使うツールを狙うやり方だ。「Reflections on trusting trust」風だ
失敗するまでは完璧な計画に聞こえる
xz で Landlock が有効になるのを妨げることが、なぜ有用だったのか気になる。後の段階で xz アーカイブに悪意ある内容を注入しようとしていたのだろうか? もしそうなら、なぜ xz 自体に悪意ある動作をそのまま入れなかったのか?
メンテナンスしている C プロジェクトにバッファオーバーフローや use-after-free をこっそり入れることは、見つからずにできるかもしれない。実際のトロイの木馬を配布するのははるかに難しく、xz-to-sshd バックドアでそれが露呈した
これは意外なほど目立つ。機能を無効にする手法は賢いし、コミットもかなりもっともらしい。なのに、なぜ単なるスペルミスではなく、明白な 構文エラーを選んだのだろう? たとえばミスが
PR_SET_NO_NEW_PRIVだったら気づけただろうか?そのほうが否認可能性も高かったはずだ
それはさておき、この マルウェアチームは、セキュリティ問題を識別するよう AI を訓練するための素晴らしいデータセットを作ってくれた。すべてのコミットにセキュリティ問題があり、オープンソースコミュニティが1つずつ調べて見つけていくのだから
整理作業をしているメンテナたちに感謝する。きっと楽しい仕事ではないはずだ
だから、自動で機能を有効/無効にする configure 風のビルドシステムは本当に好きではない。何かが欲しければ明示的に有効にしたい。機能をデフォルトにする十分な理由があるなら、代わりに明示的に無効化できるようにすべきだ
単に標準機能セットを用意し、必要に応じて
--enable-a --disable-bを許可すればよいチェック処理のバグを黙って飲み込んでしまうというのは、また別の問題だ