
Copy Fail Linux LPE (CVE-2026-31431) のクロスプラットフォーム C 移植。2026-04-29 に Theori / Xint によって公開。
English (en) ∙ 日本語 (ja) ∙ 简体中文 (zh-cn) ∙ 한국어 (ko) ∙ Русский (ru)
Copy Fail Linux LPE (CVE-2026-31431) のクロスプラットフォームC言語による再実装です。この脆弱性はTheori / Xintによって2026年4月29日に公開されました。脆弱性の詳細、タイムライン、Theoriによる発見プロセスについては、copy.fail の公式レポートを参照してください。
公開されているProof-of-Concept (PoC) は732バイトのPythonスクリプトです。このC言語への移植版は、プロジェクト独自のソース内にアーキテクチャごとの16進数バイナリやインラインアセンブリを含めることなく、nolibcがサポートする任意のアーキテクチャにコンパイル可能なポータブルなC言語として同じエクスプロイトを表現できることを実証しています。
本移植版の作成者: Tony Gies [email protected] 発見および当初の公開: Theori / Xint
copy-fail-c/
├── exploit.c ドロッパー (バイナリ書き換えバリアント)
├── exploit-passwd.c ドロッパー (/etc/passwd UID書き換えバリアント)
├── vulnerable.c 非破壊的な脆弱性チェッカー
├── payload.c ドロップされる本体 (setgid+setuid+execve sh)
├── utils.c, utils.h AF_ALG/splice ページキャッシュ書き換えプリミティブ (共有)
├── Makefile ビルドのオーケストレーション
├── nolibc/ torvalds/linux の tools/include/nolibc からのベンダーコード
└── README.md このファイル
make 後:
├── payload ドロッパーにバイト列として埋め込まれる小さな静的ELF
├── payload.o `ld -r -b binary` によって再配置可能な .o としてラップされたペイロード
├── exploit ドロッパー、バイナリ書き換えバリアント
├── exploit-passwd ドロッパー、/etc/passwd UID書き換えバリアント
└── vulnerable 非破壊的な脆弱性チェッカー
exploit.c は対象バイナリを読み取り専用で開き、埋め込まれたペイロードの4バイトのウィンドウごとにAF_ALGを介してダミーのAEAD復号を1回実行します。このときの暗号文入力は、ターゲットのページキャッシュページからsplice()を介して提供されます。authencesnテンプレートのインプレース最適化により、spliceされたソースページは暗号文入力と平文出力先のアドレスの両方として扱われるため、認証検証がリクエストを拒否する頃には、(失敗する)復号によってすでにページキャッシュページが上書きされています。4 * N回の反復後、ターゲットのキャッシュされたイメージはペイロードとバイト単位で完全に置き換えられます。ターゲットをexecve()すると、変異したページがロードされます。ディスク上のinodeは依然としてsetuid rootであるため、カーネルはroot権限を付与し、ペイロードを実行します。
payload.c は純粋なポータブルC言語です: setgid(0); setuid(0); execve("/bin/sh", ...)。nolibcは _start、システムコール機構、およびアーキテクチャごとのレジスタ操作を提供します。
2番目のバリアントである exploit-passwd.c は、setuidバイナリのイメージの代わりに /etc/passwd のページキャッシュの4バイトを書き換えます。埋め込みペイロードを必要とせず、バイナリ書き換えのルートがブロックされているシステムでも機能しますが、キャッシュアウトの(権限昇格を達成する)サーフェスははるかに狭くなります。
vulnerable.c はエクスプロイトではありません。文字列 init を含むローカルの testfile を作成し、そのファイル自身のページキャッシュに対して同じ patch_chunk() プリミティブを実行して、バイトを vulnerable に上書きしようとします。読み戻したファイル内容が一致すれば、実行中のカーネルは CVE-2026-31431 の影響範囲内です。ディスク上のinodeは変更されません。testfile は終了時に削除され、ページキャッシュの書き換えもそれと共に消えます。特権なしで実行できます。終了コードは、脆弱な場合は100、プリミティブは実行できたが書き換えが起きなかった場合は0、AF_ALGソケットファミリまたはauthencesnテンプレートが利用できずパッチ状態を判定できない場合は2、その他の実行時エラーでは1です。
デフォルト (ホストアーキテクチャ向けネイティブ):
make
aarch64 へのクロスコンパイル (またはクロスツールチェーンがインストールされている他のLinuxアーキテクチャ):
make CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld
ベンダー化されたnolibcがサポートするアーキテクチャ(アップストリームに準拠): x86_64, i386, arm, aarch64, riscv32/64, mips, ppc, s390x, loongarch, m68k, sh, sparc。nolibcはコンパイラのアーキテクチャマクロに基づいてディスパッチするため、適切な CC/LD を選択するだけで十分です。
ビルドの要件:
cc, gcc または任意のクロスバリアント)ld -r -b binary をサポートするリンカー (binutils ld および lld は対応)linux/if_alg.h と <asm/unistd.h> を提供するカーネルUAPIヘッダー (Debian/Ubuntu: linux-libc-dev; クロスバリアント: 通常、クロスツールチェーンパッケージに含まれます)Linux 5.6 より古いヘッダーセットは、ベンダー化されたnolibcが使用する __kernel_old_time_t および struct __kernel_old_timespec よりも前のものです。compat.h (ペイロードのビルドに強制インクルードされます) は、これらが存在しない場合に提供するため、古い linux-libc-dev でも引き続きビルドできます。5.6 以降のヘッダーでは何もしません。
外部ライブラリへの依存関係はありません。ペイロードはnolibcに対してフリースタンディングとしてビルドされます。ドロッパーは fprintf と perror のためだけにホストのlibcとリンクします。
ソースのポータビリティを維持し、ペイロードを小さく保つために、ツールチェーンのいくつかの小さな機能が大きな役割を果たしています。
nolibc/ はカーネルの小さなヘッダーオンリーなlibcの代替であり、torvalds/linux の tools/include/nolibc/ から取り込まれたものです。これは _start、ポータブルな syscall() マクロ、およびインラインシステムコールラッパーを提供し、アーキテクチャごとのレジスタの規則は nolibc/arch-*.h にエンコードされています。-nostdlib -static -ffreestanding -Inolibc でペイロードをビルドすると、glibcのスタートアップ、TLSの初期化、またはスタックカナリアの仕組みを組み込むことなく、直接カーネルを呼び出す小さな静的ELFが生成されます。結果: パックおよびセクションヘッダーの削除後 (いずれも後述)、x86_64で約720バイト、aarch64で約1.2 KBになります。同じ payload.c をmusl-staticでリンクすると約17 KB、glibc-staticでは約700 KBになります。
ld -r -b binaryMakefileは ld -r -b binary -o payload.o payload を介して、ビルドされた payload ELFを payload.o に変換します。リンカーは入力バイトを再配置可能なオブジェクトファイルのデータセクションとしてそのまま出力し、入力ファイル名から3つのシンボルを合成します。
_binary_payload_start 最初のペイロードバイトのアドレス
_binary_payload_end 最後のペイロードバイトの1つ後のアドレス
_binary_payload_size 絶対シンボル(値はバイト単位のサイズ)
exploit.c は最初の2つを extern const unsigned char[] として宣言し、サイズを _binary_payload_end - _binary_payload_start として計算します。
-Wl,-N と厳しい max-page-sizeペイロードは -Wl,-N -Wl,-z,max-page-size=0x10 で静的リンクされます。これにより、カーネルのページ境界に合わせたセグメントあたり4 KBのデフォルトの代わりに、16バイトのファイルアラインメントで .text/.rodata/.data が単一のLOADセグメントにまとめられます。これにより ld から「RWX permissions」の警告が出ますが、これは情報提供のみです。ペイロードの実行時メモリ保護は、この単一目的のプログラムにとって重要ではありません。このフラグがない場合、同じコードはx86_64で約13 KB(大部分はセグメント間のゼロパディング)にリンクされますが、これを使用すると、後述のセクションヘッダー削除前で約1.3 KBになります。
リンク後、objcopy --strip-section-headers はペイロードのセクションヘッダーテーブルと .shstrtab を削除します。カーネルのELFローダーはプログラムをそのプログラムヘッダーのみからマップするため、これらのバイトは実行時にはロードされませんが、payload はそのまま埋め込まれるため、ドロップと patch_chunk の反復回数を増大させます。これらを削除すると、x86_64のペイロードは約1.3 KBから720バイトになります(322回の4バイト反復が180回に減少)。2つのリンク時フラグが残りを削ります: -Wl,--build-id=none はbuild-idノートを削除し、-fcf-protection=none はコンパイラがサポートしている場合にx86のCETノートを削除します。
この削除には binutils >= 2.40 が必要です。クロスビルドでは OBJCOPY= を介してターゲットのobjcopyを渡します (例: OBJCOPY=aarch64-linux-gnu-objcopy)。objcopyが削除を実行できない場合、ビルドは注意メッセージを表示し、有効でより大きなペイロードを保持します。
このリポジトリは、AF_ALG/splice ページキャッシュの書き換えプリミティブを共有しつつ、異なる方法でroot実行にキャッシュアウト(権限昇格を達成)する2つのエクスプロイトバリアントを提供しています。それらの信頼性プロファイルは同等ではなく、現実世界の脅威モデルを推論する際にこの違いが重要になります。
exploit)ターゲットのsetuidバイナリのページキャッシュを埋め込まれたペイロードバイトで書き換え、バイナリをexecします。カーネルは、バイナリのディスク上の変更されていないsetuidビットからroot権限を付与し、メモリ上の破損したイメージをロードして、ペイロードを実行します。
攻撃者がシステム上の任意のroot-setuidバイナリに対して open(target, O_RDONLY) できる場所であればどこでも機能します。制限された読み取りディレクトリの背後にあるsetuidバイナリの環境や、setuidを使用しないシステム設計によっておおむね防御されます。
exploit-passwd)/etc/passwd のページキャッシュの4バイトを書き換えて、実行中のユーザーのUIDフィールドを "0000" に設定します。/etc/passwd はすべての標準的なLinuxシステムで誰でも読み取り可能であるため、書き換え 自体は普遍的です。これをroot実行に変換するには、root側のプロセスが getpwnam/getpwuid を介してユーザーを解決し、クロスチェックを行わずに解決されたuidに基づいて行動することに依存します。そのようなコンシューマーは多く存在しますが、その多くは呼び出し元のuidに対するカーネルのビューや、ディスク上のファイルの所有権と防衛的なクロスチェックを行うため、キャッシュアウトが機能しなくなります。
su 失敗後のピボットexploit-passwd は書き換え後に最も単純なキャッシュアウトとして su <user> をexecします。これは util-linux の su に対しては機能しますが、shadow-utils の su に対しては "Cannot determine your user name." で失敗します。この時点でもページキャッシュの書き換えは有効であり、他のキャッシュアウト(例: クロスチェックなしで getpwnam を介してユーザーを解決するデーモンを使用する)へのピボットは可能です。
テスト完了後に破損したページキャッシュをクリアするには、rootとして echo 3 > /proc/sys/vm/drop_caches を実行してください。
下限: torvalds/linux 72548b093ee3 2017年8月, v4.14
(AEADスキャッターリストへのspliceを介した
ファイルページ書き込みプリミティブを
導入したAF_ALG iov_iterの再構築)
上限: torvalds/linux a664bf3d603d 2026年4月, メインライン
(2017年のalgif_aeadインプレース最適化を元に戻す;
ソースと宛先のスキャッターリストを分離し、
ページキャッシュページが書き込み可能な
暗号化の宛先にならないようにした)
この間の期間: 修正をバックポートしなかったすべての主要なディストリビューションカーネル。
Ubuntu、RHEL、SUSE、Amazon Linux、およびDebianはすべて、情報公開時にストッククラウドイメージカーネルで脆弱性が確認されていました。ディストリビューションレベルのバックポートは、一般公開と並行して2026年4月29日頃から展開され始めました。ターゲットカーネルが該当期間内にあるかどうかを確認するには、カーネルのgitログまたはディストリビューションのチェンジログに a664bf3d603d (またはそのディストリビューション固有のバックポート) が存在するかどうかを確認してください。
有償のセキュリティレビュー、カスタム移植、非公開のセキュリティアドバイザリについては、作成者のコンサルティング会社である Crash United, LLC を通じてお問い合わせください。
連絡先: [email protected] · https://crashunited.com
GitHub: @tgies · X/Twitter: @me_irl
CVE-2026-31431の発見と当初の公開: Theori / Xint 公式レポート: https://copy.fail/
本C移植版: Tony Gies [email protected]
nolibc/: Linuxカーネルツリーからのベンダーコード、デュアルライセンス
LGPL-2.1-or-later または MIT (nolibc/nolibc.h および各ファイルのSPDXヘッダーを参照)。
このリポジトリのドロッパーおよびペイロードのソースは、依存するnolibcツリーと同じLGPL-2.1-or-later または MIT のデュアルライセンス条項の下でリリースされています。これは、このディレクトリ全体を自分の作業環境に取り込むユーザーに対してライセンスの互換性を容易に維持するためです。
エクスプロイトとペイロードは、セキュリティ研究および防衛的な検出の目的で公開されています。所有していない、または明示的なテストの許可を得ていないシステムに対する使用は、作成者ではなくあなた自身の責任です。
| キャッシュアウト | 事前のroot設定の必要性 | 備考 |
|---|
| WSL2 セッションのスポーン | なし | WSLのセッションごとの setuid(getpwnam(default_user)->pw_uid) は検証を行いません。クリーンに動作します。 |
util-linux su | なし | 呼び出し元の識別処理が寛容です。 |
shadow-utils su | あり | getpwuid(getuid()) 呼び出し元の識別チェックは、書き換えによって実際のuidがマッピングされなくなるため失敗します。 |
sshd (デフォルト StrictModes yes) | あり (StrictModes の無効化) | StrictModes はホームディレクトリが root または pw->pw_uid に所有されていることを要求します。書き換えにより pw_uid=0 になりますが、ディスク上の所有者は元のuidのままです。不一致のため認証が拒否されます。 |
| MTA ローカル配信 (postfix, exim など) | 変動あり | MDAのホーム権限検証に依存します。MTAごとのテストが必要です。 |