|
| 1 | +# Copy Fail (CVE-2026-31431) - C port |
| 2 | + |
| 3 | +*[English (en)](README.md) ∙ [日本語 (ja)](README.ja.md) ∙ [简体中文 (zh-cn)](README.zh-cn.md)* |
| 4 | + |
| 5 | +Copy Fail Linux LPE (CVE-2026-31431) のクロスプラットフォームC言語による再実装です。この脆弱性はTheori / Xintによって2026年4月29日に公開されました。脆弱性の詳細、タイムライン、Theoriによる発見プロセスについては、[copy.fail](https://copy.fail/) の公式レポートを参照してください。 |
| 6 | + |
| 7 | +公開されているProof-of-Concept (PoC) は732バイトのPythonスクリプトです。このC言語への移植版は、プロジェクト独自のソース内にアーキテクチャごとの16進数バイナリやインラインアセンブリを含めることなく、nolibcがサポートする任意のアーキテクチャにコンパイル可能なポータブルなC言語として同じエクスプロイトを表現できることを実証しています。 |
| 8 | + |
| 9 | +本移植版の作成者: Tony Gies <tony.gies@crashunited.com> |
| 10 | +発見および当初の公開: Theori / Xint |
| 11 | + |
| 12 | +## リポジトリ構成 |
| 13 | + |
| 14 | +``` |
| 15 | +copy-fail-c/ |
| 16 | +├── exploit.c ドロッパー (バイナリ書き換えバリアント) |
| 17 | +├── exploit-passwd.c ドロッパー (/etc/passwd UID書き換えバリアント) |
| 18 | +├── payload.c ドロップされる本体 (setgid+setuid+execve sh) |
| 19 | +├── Makefile ビルドのオーケストレーション |
| 20 | +├── nolibc/ torvalds/linux の tools/include/nolibc からのベンダーコード |
| 21 | +└── README.md このファイル |
| 22 | +``` |
| 23 | + |
| 24 | +`make` 後: |
| 25 | + |
| 26 | +``` |
| 27 | +├── payload ドロッパーにバイト列として埋め込まれる小さな静的ELF |
| 28 | +├── payload.o `ld -r -b binary` によって再配置可能な .o としてラップされたペイロード |
| 29 | +├── exploit ドロッパー、バイナリ書き換えバリアント |
| 30 | +└── exploit-passwd ドロッパー、/etc/passwd UID書き換えバリアント |
| 31 | +``` |
| 32 | + |
| 33 | +`exploit.c` は対象バイナリを読み取り専用で開き、埋め込まれたペイロードの4バイトのウィンドウごとにAF_ALGを介してダミーのAEAD復号を1回実行します。このときの暗号文入力は、ターゲットのページキャッシュページからsplice()を介して提供されます。authencesnテンプレートのインプレース最適化により、spliceされたソースページは暗号文入力と平文出力先のアドレスの両方として扱われるため、認証検証がリクエストを拒否する頃には、(失敗する)復号によってすでにページキャッシュページが上書きされています。4 * N回の反復後、ターゲットのキャッシュされたイメージはペイロードとバイト単位で完全に置き換えられます。ターゲットをexecve()すると、変異したページがロードされます。ディスク上のinodeは依然としてsetuid rootであるため、カーネルはroot権限を付与し、ペイロードを実行します。 |
| 34 | + |
| 35 | +`payload.c` は純粋なポータブルC言語です: `setgid(0); setuid(0); execve("/bin/sh", ...)`。nolibcは `_start`、システムコール機構、およびアーキテクチャごとのレジスタ操作を提供します。 |
| 36 | + |
| 37 | +2番目のバリアントである `exploit-passwd.c` は、setuidバイナリのイメージの代わりに /etc/passwd のページキャッシュの4バイトを書き換えます。埋め込みペイロードを必要とせず、バイナリ書き換えのルートがブロックされているシステムでも機能しますが、キャッシュアウトの(権限昇格を達成する)サーフェスははるかに狭くなります。 |
| 38 | + |
| 39 | +## ビルド |
| 40 | + |
| 41 | +デフォルト (ホストアーキテクチャ向けネイティブ): |
| 42 | + |
| 43 | +```sh |
| 44 | +make |
| 45 | +``` |
| 46 | + |
| 47 | +aarch64 へのクロスコンパイル (またはクロスツールチェーンがインストールされている他のLinuxアーキテクチャ): |
| 48 | + |
| 49 | +```sh |
| 50 | +make CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld |
| 51 | +``` |
| 52 | + |
| 53 | +ベンダー化されたnolibcがサポートするアーキテクチャ(アップストリームに準拠): x86_64, i386, arm, aarch64, riscv32/64, mips, ppc, s390x, loongarch, m68k, sh, sparc。nolibcはコンパイラのアーキテクチャマクロに基づいてディスパッチするため、適切な `CC`/`LD` を選択するだけで十分です。 |
| 54 | + |
| 55 | +ビルドの要件: |
| 56 | + |
| 57 | +* Cコンパイラ (`cc`, `gcc` または任意のクロスバリアント) |
| 58 | +* `ld -r -b binary` をサポートするリンカー (binutils ld および lld は対応) |
| 59 | +* `linux/if_alg.h` と `<asm/unistd.h>` を提供するカーネルUAPIヘッダー (Debian/Ubuntu: `linux-libc-dev`; クロスバリアント: 通常、クロスツールチェーンパッケージに含まれます) |
| 60 | + |
| 61 | +外部ライブラリへの依存関係はありません。ペイロードはnolibcに対してフリースタンディングとしてビルドされます。ドロッパーは `fprintf` と `perror` のためだけにホストのlibcとリンクします。 |
| 62 | + |
| 63 | +## アーキテクチャの選択 |
| 64 | + |
| 65 | +ソースのポータビリティを維持し、ペイロードを小さく保つために、ツールチェーンの3つの小さな機能が大きな役割を果たしています。 |
| 66 | + |
| 67 | +### nolibc |
| 68 | + |
| 69 | +`nolibc/` はカーネルの小さなヘッダーオンリーなlibcの代替であり、torvalds/linux の `tools/include/nolibc/` から取り込まれたものです。これは `_start`、ポータブルな `syscall()` マクロ、およびインラインシステムコールラッパーを提供し、アーキテクチャごとのレジスタの規則は `nolibc/arch-*.h` にエンコードされています。`-nostdlib -static -ffreestanding -Inolibc` でペイロードをビルドすると、glibcのスタートアップ、TLSの初期化、またはスタックカナリアの仕組みを組み込むことなく、直接カーネルを呼び出す小さな静的ELFが生成されます。結果: x86_64で約1.7 KB、aarch64で約2.0 KB。同じ `payload.c` をmusl-staticでリンクすると約17 KB、glibc-staticでは約700 KBになります。 |
| 70 | + |
| 71 | +### 埋め込み用の `ld -r -b binary` |
| 72 | + |
| 73 | +Makefileは `ld -r -b binary -o payload.o payload` を介して、ビルドされた `payload` ELFを `payload.o` に変換します。リンカーは入力バイトを再配置可能なオブジェクトファイルのデータセクションとしてそのまま出力し、入力ファイル名から3つのシンボルを合成します。 |
| 74 | + |
| 75 | +``` |
| 76 | +_binary_payload_start 最初のペイロードバイトのアドレス |
| 77 | +_binary_payload_end 最後のペイロードバイトの1つ後のアドレス |
| 78 | +_binary_payload_size 絶対シンボル(値はバイト単位のサイズ) |
| 79 | +``` |
| 80 | + |
| 81 | +`exploit.c` は最初の2つを `extern const unsigned char[]` として宣言し、サイズを `_binary_payload_end - _binary_payload_start` として計算します。 |
| 82 | + |
| 83 | +### `-Wl,-N` と厳しい `max-page-size` |
| 84 | + |
| 85 | +ペイロードは `-Wl,-N -Wl,-z,max-page-size=0x10` で静的リンクされます。これにより、カーネルのページ境界に合わせたセグメントあたり4 KBのデフォルトの代わりに、16バイトのファイルアラインメントで `.text`/`.rodata`/`.data` が単一のLOADセグメントにまとめられます。これにより `ld` から「RWX permissions」の警告が出ますが、これは情報提供のみです。ペイロードの実行時メモリ保護は、この単一目的のプログラムにとって重要ではありません。このフラグがない場合、同じコードはx86_64で約13 KB(大部分はセグメント間のゼロパディング)にリンクされますが、これを使用すると約1.7 KBになります。 |
| 86 | + |
| 87 | +## バリアントとキャッシュアウトの実現可能性 |
| 88 | + |
| 89 | +このリポジトリは、AF_ALG/splice ページキャッシュの書き換えプリミティブを共有しつつ、異なる方法でroot実行にキャッシュアウト(権限昇格を達成)する2つのエクスプロイトバリアントを提供しています。それらの信頼性プロファイルは同等ではなく、現実世界の脅威モデルを推論する際にこの違いが重要になります。 |
| 90 | + |
| 91 | +### バイナリ書き換えバリアント (`exploit`) |
| 92 | + |
| 93 | +ターゲットのsetuidバイナリのページキャッシュを埋め込まれたペイロードバイトで書き換え、バイナリをexecします。カーネルは、バイナリのディスク上の変更されていないsetuidビットからroot権限を付与し、メモリ上の破損したイメージをロードして、ペイロードを実行します。 |
| 94 | + |
| 95 | +攻撃者がシステム上の任意のroot-setuidバイナリに対して `open(target, O_RDONLY)` できる場所であればどこでも機能します。制限された読み取りディレクトリの背後にあるsetuidバイナリの環境や、setuidを使用しないシステム設計によっておおむね防御されます。 |
| 96 | + |
| 97 | +### /etc/passwd UID書き換えバリアント (`exploit-passwd`) |
| 98 | + |
| 99 | +/etc/passwd のページキャッシュの4バイトを書き換えて、実行中のユーザーのUIDフィールドを "0000" に設定します。/etc/passwd はすべての標準的なLinuxシステムで誰でも読み取り可能であるため、*書き換え* 自体は普遍的です。これをroot実行に変換するには、root側のプロセスが getpwnam/getpwuid を介してユーザーを解決し、クロスチェックを行わずに解決されたuidに基づいて行動することに依存します。そのようなコンシューマーは多く存在しますが、その多くは呼び出し元のuidに対するカーネルのビューや、ディスク上のファイルの所有権と防衛的なクロスチェックを行うため、キャッシュアウトが機能しなくなります。 |
| 100 | + |
| 101 | +#### キャッシュアウト実現可能性マトリックス |
| 102 | + |
| 103 | +| キャッシュアウト | 事前のroot設定の必要性 | 備考 | |
| 104 | +|---|---|---| |
| 105 | +| WSL2 セッションのスポーン | なし | WSLのセッションごとの `setuid(getpwnam(default_user)->pw_uid)` は検証を行いません。クリーンに動作します。 | |
| 106 | +| util-linux `su` | なし | 呼び出し元の識別処理が寛容です。 | |
| 107 | +| shadow-utils `su` | あり | `getpwuid(getuid())` 呼び出し元の識別チェックは、書き換えによって実際のuidがマッピングされなくなるため失敗します。 | |
| 108 | +| sshd (デフォルト `StrictModes yes`) | あり (StrictModes の無効化) | StrictModes はホームディレクトリが root または `pw->pw_uid` に所有されていることを要求します。書き換えにより pw_uid=0 になりますが、ディスク上の所有者は元のuidのままです。不一致のため認証が拒否されます。 | |
| 109 | +| MTA ローカル配信 (postfix, exim など) | 変動あり | MDAのホーム権限検証に依存します。MTAごとのテストが必要です。 | |
| 110 | + |
| 111 | +#### `su` 失敗後のピボット |
| 112 | + |
| 113 | +`exploit-passwd` は書き換え後に最も単純なキャッシュアウトとして `su <user>` をexecします。これは util-linux の `su` に対しては機能しますが、shadow-utils の `su` に対しては "Cannot determine your user name." で失敗します。この時点でもページキャッシュの書き換えは有効であり、他のキャッシュアウト(例: クロスチェックなしで getpwnam を介してユーザーを解決するデーモンを使用する)へのピボットは可能です。 |
| 114 | +テスト完了後に破損したページキャッシュをクリアするには、rootとして `echo 3 > /proc/sys/vm/drop_caches` を実行してください。 |
| 115 | + |
| 116 | +## 影響を受けるカーネル |
| 117 | + |
| 118 | +``` |
| 119 | +下限: torvalds/linux 72548b093ee3 2017年8月, v4.14 |
| 120 | + (AEADスキャッターリストへのspliceを介した |
| 121 | + ファイルページ書き込みプリミティブを |
| 122 | + 導入したAF_ALG iov_iterの再構築) |
| 123 | +
|
| 124 | +上限: torvalds/linux a664bf3d603d 2026年4月, メインライン |
| 125 | + (2017年のalgif_aeadインプレース最適化を元に戻す; |
| 126 | + ソースと宛先のスキャッターリストを分離し、 |
| 127 | + ページキャッシュページが書き込み可能な |
| 128 | + 暗号化の宛先にならないようにした) |
| 129 | +``` |
| 130 | + |
| 131 | +この間の期間: 修正をバックポートしなかったすべての主要なディストリビューションカーネル。 |
| 132 | +Ubuntu、RHEL、SUSE、Amazon Linux、およびDebianはすべて、情報公開時にストッククラウドイメージカーネルで脆弱性が確認されていました。ディストリビューションレベルのバックポートは、一般公開と並行して2026年4月29日頃から展開され始めました。ターゲットカーネルが該当期間内にあるかどうかを確認するには、カーネルのgitログまたはディストリビューションのチェンジログに `a664bf3d603d` (またはそのディストリビューション固有のバックポート) が存在するかどうかを確認してください。 |
| 133 | + |
| 134 | +## ライセンスとクレジット |
| 135 | + |
| 136 | +CVE-2026-31431の発見と当初の公開: Theori / Xint |
| 137 | +公式レポート: <https://copy.fail/> |
| 138 | + |
| 139 | +本C移植版: Tony Gies <tony.gies@crashunited.com> |
| 140 | + |
| 141 | +`nolibc/`: Linuxカーネルツリーからのベンダーコード、デュアルライセンス |
| 142 | +LGPL-2.1-or-later または MIT (`nolibc/nolibc.h` および各ファイルのSPDXヘッダーを参照)。 |
| 143 | + |
| 144 | +このリポジトリのドロッパーおよびペイロードのソースは、依存するnolibcツリーと同じLGPL-2.1-or-later または MIT のデュアルライセンス条項の下でリリースされています。これは、このディレクトリ全体を自分の作業環境に取り込むユーザーに対してライセンスの互換性を容易に維持するためです。 |
| 145 | + |
| 146 | +エクスプロイトとペイロードは、セキュリティ研究および防衛的な検出の目的で公開されています。所有していない、または明示的なテストの許可を得ていないシステムに対する使用は、作成者ではなくあなた自身の責任です。 |
0 commit comments