
GNU IFUNC は CVE-2024-3094 の真の原因です
[オレンジ色のウェブサイト][hn]のジョーカーどもが俺を困らせているようだな。以下にいくつか厳選した返答を用意したが、まずは挑戦状を叩きつけておく:ifuncを使わずに この攻撃を実証できた最初の人物に、自腹で500ドルを送る。純粋に興味があるし、啓蒙のためなら喜んで金を払う。このリポジトリをフォークして、動作するPoCをPRで送ってくれ。勝者には個別に郵送先を聞く。では返答だ:
これは見当違いも甚だしい。
坊や、俺は見当違いの中に住んでいるんだよ、誰にでも吠えられる。
エクスプロイトに必須ではなかった。
お前こそエクスプロイトに必須じゃねえよ!
rootとして実行される任意のコードに対する保護を追加したいなら、selinuxが常にある。
一度ロードされれば、この攻撃はそれ以上syscall境界を越える必要がなかった。だからそう、我々は「おまけ」のrootセッションを制限できたかもしれないが、それでもマシンには招かれざる客がいただろう!
なんだこれは、ビルボの誕生日会か??パーティー用の用事以外では入れないのか!!
- IFUNCはmainの前にコードを実行する唯一の方法では決してない。
しかし、メモリ保護が設定される前にコードを実行する不必要な方法ではある。
- 彼らが提示する代替案は、プロセスの生存期間中ずっと関数ポインタが書き込み可能なままになるため、おそらく安全性は低い。
mprotectで improvisation できる!LD_PRELOADの変更サブセクションの上の最後の文を見てくれ。
ええ、このブログは誤導されている。
失礼、このブログは誘導されていない。これらの悪戯は全部自分でやったんだ!誰にもこんなバカになるよう騙されていない。
IFUNCは[クライアント]ソフトウェア自体で実装されるべきだ、
@CountWSS 💯 その通りだぜ
Githubメンテナーによる一連の露骨なプロセス失敗から...
これが唯一真剣に返答する点だ:
これを彼の側のミスから始まったと考えるのは、xz-utilsのメンテナーに対して極めて不公平であり、コミュニティにとってかなり危険だと思う。それは誰もこのプロジェクトの維持を手伝おうとしなかったことから始まった。攻撃者はifuncを技術的脆弱性として、そして我々のxz-utilsに対する集団的怠慢を社会的脆弱性として利用した。Collin氏の行動を、英雄的で何年にもわたるコミュニティサービスへの献身以外の何かと見るのは恥ずべきことだと思う。
あとBruce Schneierも俺に同意しているから...残念だったな、お前の議論は終わりだ。
言葉は必要以上に厳しかったかもしれない
友達が最初にどれだけこれをマイルドにさせたか、信じられないだろうな。
Linuxディストリビューションは、OpenBSDが自分たちの混乱に適合し適応することを期待するほど、自分たちを高く評価すべきではない
@debazel!!! Me gusta。
なんてこった、完全にデタラメだ。
ああ、その部分は正確だ。
xz-utilsを[CVE-2024-3094][nvd]のせいにするのをやめるべき理由。あと私のETSA Talkもチェックしてね!

CVE-2024-3094、一般には「xz-utilsバックドア」として知られるこれは、世界のサイバーセキュリティにとって危機一髪だった。[Andres Freund][freund]が間一髪で発見していなければ、地球上のほとんどのSSHサーバーが、この攻撃の背後にいる者にrootアクセスを許可し始めていただろう。
残念ながら、あまりにも多くの分析が[悪意あるコード][JiaT75]がどのようにxz-utilsリポジトリに入り込んだかに焦点を当てている。代わりに私は、重要なオープンソースソフトウェアにおける2つの長年の設計上の決定、すなわち[OpenSSHをSystemDにリンクすること][biebl]と、[GNU IFUNC][sourceware]の存在が、この攻撃を可能にしたと主張したい。
始める前に:この議論の多くはLinuxにおける動的リンクの複雑さを扱っている。復習が必要なら、dynamic_linking.mdをチェックしてほしい。
xz-utilsバックドアの高レベルな詳細を概説した優れた記事は山ほどある。例えばDan Goodinの[What we know about the xz Utils backdoor that almost infected the world][goodin1]や、Sam Jamesの[FAQ on the xz-utils backdoor (CVE-2024-3094)][thesamesam] gistなどだ。ここでそれをすべて蒸し返す必要はないので、この記事の目的のために、非常に粗い振り返りを以下に示す:
## なぜLinuxディストリビューションはOpenSSHを改変するのか?
短い答えは、彼らがそうせざるを得ないからです。OpenSSHは
OpenBSDコミュニティによって、OpenBSDコミュニティのために開発されており、彼らは
Linuxなど気にも留めていません。[Portable OpenSSH][mindrot]プロジェクトは、
OpenBSD固有のコンポーネントを汎用POSIXコンポーネントに置き換え、該当する場合は
プラットフォーム固有のコードを加える、ベストエフォートのパッチ集です。
SSHのソフトウェアサプライチェーンは、実際には次のようなものになります:```mermaid
flowchart TD
subgraph OpenBSD Folks
A[OpenBSD]
B[OpenSSH]
H[improvements]
end
B-->A
A-->H
H-->B
B-->C
C[Portable OpenSSH]
subgraph Debian Folks
D[Debian SSH]
G[improvements]
end
C-->D
D-->G
G-->C
subgraph Fedora Folks
J[Fedora SSH]
K[improvements]
end
C-->J
J-->K
K-->C
OpenBSD版のOpenSSHは他のすべての上流に位置し、その改善のほとんどはOpenBSDコミュニティ内からもたらされます。これらの変更は下流のPortable OpenSSHプロジェクトに流れ込み、OpenBSDに固有ではない方法で新機能を再実装しようとします。これにより、SSHはLinux、macOS、FreeBSD、さらにはWindowsといったプラットフォームで動作できるようになります。
しかし、それだけにとどまりません。一部のオペレーティングシステムは、Portable OpenSSHが提供する以上のカスタマイズをさらに適用しています。たとえば、AppleはmacOSのパスワードマネージャーと統合するために、ssh-addに[--apple-use-keychain][keith]フラグを追加しています。
CVE-2024-3094の場合、FedoraとDebianはOpenSSHのフォークに対して独自の[SystemDパッチ][biebl]を維持し、[sshdの再起動時の競合状態][schmidt]を修正しました。そのため、SSHの実際のサプライチェーンは次のようになり始めました:```mermaid
flowchart TD
A[OpenSSH]
B[Portable OpenSSH]
C[Debian SSH]
D[Fedora SSH]
A-->B
B-->C
B-->D
C<-->|SystemD Patches|D
これらのパッチがPortable OpenSSHに取り込まれることはなかった。Portable OpenSSHの人々は[「libsystemdへの依存を受け入れることに興味がなかった」][djmdjm]からだ。そして上流のOpenSSHにも取り込まれることはなかった。OpenBSDにはSystemDをサポートする必要がまったくなかったからだ。
### 「関心の分離」に関する懸念
これは一見無害に見えるが、オープンソース、特にLinuxにおけるはるかに大きな問題の一例である。オペレーティングシステムの重要なコンポーネントが、互いを知らず、互いに話をしない人々によって開発されているのだ。
* SystemD向けにOpenSSHへパッチを当てた人々は、libsystemdがxz-utilsに依存していることを知っていた(あるいは気にかけていた)だろうか?
* SystemDの人々は、xz-utilsがifuncを使い始めたことを知っていた(あるいは気にかけていた)だろうか?
* OpenSSHの人々は、ifuncというものが存在することを知っていた(あるいは気にかけていた)だろうか?OpenBSDでは間違いなく存在しないものだ。
ある意味で、このコミュニケーションの断絶はオープンソースの特徴である。私はあなたにわずらわされることなく、あなたの成果を自分のニーズに合わせて適応させることができる。しかしそれはまた、重要な設計上の前提(従来の動的リンクのプロセスなど)が維持されることを妨げる、ある種の間接性をもたらすこともある。
[コンウェイの法則][conway]の明らかな系として、あなたが自組織の組織図を出荷しているなら、組織図の隙間に潜むバグもまた出荷しているということになる。ここで実際に間違いを犯した個人やチームは存在しないが、後知恵で見れば、攻撃者たちがDebian/FedoraのSSHの左手がxz-utilsの右手が何をしているかを知らないと認識していたことは明らかだ。
## GNU IFUNCは*本来*何をするものなのか?
これは、ある関数のどのバージョンを使いたいかを実行時に決定できるようにするものである。これは、リンカがシンボルを解決する方法に影響を与えるために**任意のコード**を実行する機会を与えることで実現される。

### CPU機能の検出
幅広い種類のx86 CPUで動作しなければならないアプリケーションがあるとしよう。現在のCPUの特定の機能に応じて、同じタスクに対して異なるアルゴリズムを使いたいかもしれない。IFUNCの背後にある元々のアイデアは、関数が最初に呼び出されたときにCPU機能をチェックし、その後はそのCPUに最も適した実装を使えるようにすることだった。
[`cpu_demo.c`](https://github.com/robertdfrench/ifuncd-up/blob/trunk/code/cpu_demo.c)を見てみよう:```c
void print_cpu_info() __attribute__((ifunc ("resolve_cpu_info")));
void print_avx2() { printf("AVX2 is present.\n"); }
void print_nope() { printf("AVX2 is missing.\n"); }
static void* resolve_cpu_info(void) {
__builtin_cpu_init();
if (__builtin_cpu_supports("avx2")) {
return print_avx2;
} else {
return print_nope;
}
}
int main() {
print_cpu_info();
return 0;
}
このプログラムは、IFUNCの最も一般的な使用方法を示しています。CPUが特定の機能をサポートしているかどうかを問い合わせ、サポートされている機能に応じて関数の異なる実装を提供します。この場合、私たちの関数 print_cpu_info は、あなたのCPUがどれほど古いかに応じて、「AVX2 is present」または「AVX2 is missing」のいずれかを出力することになります。
IFUNCはCPU機能のプロービングを目的としていますが、リゾルバ内でより複雑なコードを実行することを妨げるものは何もありません。例えば、tty_demo.c は、STDOUTがファイルであるかターミナルであるかに応じて、異なる関数実装をロードする方法を示しています:```c
// Print Green text to the Terminal
void print_to_tty(const char *message) {
const char *green_start = "\033[32m";
const char *color_reset = "\033[0m";
printf("%sTTY: %s%s\n", green_start, message, color_reset);
}
// Print plain text to a file void print_to_file(const char *message) { printf("FILE: %s\n", message); }
void print_message(const char *message)
attribute((ifunc("resolve_print_function")));
void (*resolve_print_function(void))(const char *) { struct termios term;
// Ask the kernel whether stdout is a file or a tty
int result = ioctl(STDOUT_FILENO, TCGETS, &term);
if (result == 0) {
// stdout is a terminal
return print_to_tty;
} else {
// stdout is not a terminal
return print_to_file;
}
}
int main() { print_message("Hello, World!"); return 0; }
これは実際にはIFUNCの意図された使用方法ではありませんが、何が可能かを示しています。宣言したIFUNCを使用する任意のプログラムで、`main`の前に任意のコードを実行できます。
## IFUNCはおそらく悪いアイデアである

GNU IFUNCは実装が難しく、正しく使用するのが困難であり、(性能ツールと謳われているわりに)代替手段よりもそれほど高速ではありません。CVE-2024-3094で見られたように、ソフトウェアサプライチェーン攻撃のための非常に強力なツールでもあります。