Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
page_inject — CVE-2026-31431-killed ページキャッシュエクスプロイト — 同じイメージレイヤーを共有するコンテナへのコード実行 | Kitploit
ツール/GitHubGitHub/sgkdev/page_inject
脆弱性分析エクスプロイトポストエクスプロイトペネトレーションテストレッドチーミングコンテナエスケープバイナリエクスプロイト
GitHubsgkdev/page_inject

page_inject

CVE-2026-31431-killed ページキャッシュエクスプロイト — 同じイメージレイヤーを共有するコンテナへのコード実行

リポジトリを見る
74143ヶ月前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

page_inject - AF_ALG aead クロスコンテナエスケープ

AF_ALG aead 脆弱性を利用したクロスコンテナエクスプロイト -- 侵害済みの1つのコンテナから、同じ libc.so.6 イメージレイヤーを共有するすべての兄弟コンテナへピボットします。

これはエスケーププリミティブです。攻撃者がすでに侵害した非特権コンテナの内部から実行され、AF_ALG authencesn の ESN ローテーションによる4バイト任意書き込みバグ(CVE-2026-31431)を利用して、libc.so.6 のページキャッシュページに永続的な read() フックを仕込みます。Docker / containerd は overlayfs の下層レイヤーファイルを共有 inode で裏打ちしているため、これらのページは同じイメージから生成されたすべての兄弟コンテナから見えます。フックはそれらのプロセスでも発動し、攻撃者は各コンテナ内でコマンド実行を得ます。

脅威モデル

  • 攻撃者は、victim と同じイメージから他のコンテナ(siblings)を実行しているホスト上の単一のコンテナ(victim と呼ぶ)へのシェルアクセスを持っています。
  • victim はデフォルトの Docker/k8s 構成で実行されます:コンテナのユーザー名前空間内の非特権 uid、デフォルトの seccomp プロファイル、デフォルトの AppArmor プロファイル、特別なケーパビリティなし、ホストのバインドマウントなし。
  • victim が持つのは以下のみ:
    • 自身の libc への読み取りアクセス(/usr/lib/x86_64-linux-gnu/libc.so.6 またはディストリビューションがインストールした場所)
    • 標準の socket(AF_ALG, ...) システムコール群
    • 標準の splice / vmsplice システムコール
    • chmod +x できるディレクトリへの書き込みアクセス(例:/tmp)
  • カーネルが CVE-2026-31431 に対して脆弱である必要があります(アップストリームの revert 修正より前の algif_aead + authencesn ビルド)。

以上です。特別な CAP_* も、ホストのファイルシステムへのアクセスも不要です。攻撃者は自己完結型の静的リンクバイナリをコンテナ内に配置して実行するだけで、ページキャッシュの破壊 -- つまりフック -- がすべての兄弟コンテナから見えるようになります。

エクスプロイトの連鎖の仕組み

  1. ページキャッシュページの同一性。 overlayfs コンテナ内では、/usr/lib/.../libc.so.6 は下層イメージレイヤーの ext4 inode によって提供されます。同じイメージから起動されたすべてのコンテナはそのバッキング inode を共有し、カーネルのページキャッシュは基盤となる inode をキーとして管理されます -- overlay や名前空間ではなく。したがって、ページキャッシュページへの単一の4バイト書き込みは、そのページを mmap しているすべての兄弟コンテナのプロセスから見えます。

  2. AF_ALG aead の脆弱性は、そのような1回の書き込みを多数の書き込みに変えます。 algif_aead はユーザー RX iovec を、splice された TX SGL の末尾の authsize バイトと連結します。そして authencesn の ESN ローテーションは、AAD の seq_high フィールドの4バイトを dst[assoclen + cryptlen] に置きます -- これは連結された外部テールの最初のバイトです。splice されたページは、攻撃者が読み取りアクセスしか持たないファイルのページキャッシュページですが、暗号はダーティ管理なしでそこにバイトをコピーします。(基礎となる仕組みについては crypto/algif_aead.c と crypto/authencesn.c を参照。)

  3. 呼び出し可能なプリミティブのブートストラップ。 page_inject が最初に行うのはゾーン A のブートストラップです -- libc の .text ケーブ内に配置される、同じ AF_ALG の操作手順を asm で再実装したもの(write_cache.asm)。これにより、4バイト書き込みは将来の任意のフックペイロード内部からの通常の となり、呼び出しごとのソケットセットアップは不要になります。

ビルド

インジェクタは victim コンテナの外部 -- 通常は攻撃者自身の開発マシン -- でビルドされます。ほとんどの本番コンテナイメージにはコンパイラが含まれていないためです。gcc(-static リンクサポート付き)と nasm がある標準的な Linux x86_64 開発環境で十分です。

root@kitploit:~
make            # assembles .asm sources via gen_arrays.sh, links static page_inject
make shellcode  # also produces inspectable .bin flat binaries
make clean      # removes generated files and the binary

出力は、最新の x86_64 Linux カーネルで動作する単一の静的リンク ELF(./page_inject)です。

配布と使用方法(victim コンテナから)

攻撃者が victim でシェルを取得したら、バイナリを書き込み可能なディレクトリ(通常は /tmp)にアップロードします:

root@kitploit:~
# inside the compromised container, attacker session
victim$ ./page_inject

引数なしの場合、page_inject は /usr/lib/x86_64-linux-gnu/libc.so.6(統合後の Debian/Ubuntu の場所)をデフォルトとします。他のディストリビューションでは libc は別のパスにあります。明示的に指定するか、--root / を使用してコンテナのルートから組み込みの検索テーブルをスキャンしてください:

root@kitploit:~
# Fedora / Rocky / CentOS
victim$ ./page_inject /usr/lib64/libc.so.6

# Arch
victim$ ./page_inject /usr/lib/libc.so.6

# Auto-detect, regardless of distro:
victim$ ./page_inject --root /

どちらの呼び出しも同じことを行います:コンテナ内の libc を ELF パースし、そのページキャッシュにフックをインストールし、兄弟コンテナが登録する間スロットテーブルを約30秒間監視し、健全性チェックとして最初に登録した兄弟コンテナに対して一度だけ id を実行します。

ブートストラップ後、コマンドシェルに入って登録済みの任意の兄弟コンテナを操作できます:

root@kitploit:~
victim$ ./page_inject --shell --no-bootstrap
=== page-cache shell ===
Containers (3):
  [0] 0x0018598d  <- target
  [1] 0x001859ab
  [2] 0x001859cd

inject:0018598d> exec id
uid=0(root) gid=0(root) groups=0(root)
inject:0018598d> target 0x001859ab
inject:001859ab> exec hostname
1ccd66abee9d
inject:001859ab> exec cat /etc/shadow
root:$6$.....
inject:001859ab> unhook
... read() prologue restored, slot table zeroed ...

unhook はすべての兄弟コンテナからフックを一括で除去し、フック子プロセスを自己終了させます。

root@kitploit:~
Usage: page_inject [OPTIONS] [LIBC_PATH]

Options:
  --root <prefix>   Auto-resolve libc.so.6 under <prefix> using the
                    built-in fixed-path lookup table. Inside the
                    victim container that's normally --root / .
  --shell [0xKEY]   Drop into interactive command shell after
                    injection. Optional KEY pre-selects the target.
  --no-bootstrap    Skip injection (shell-only; hook must already
                    be live in the page cache).
  --timeout SEC     Slot monitoring timeout in --shell mode
                    (default 30 s).
  --help, -h        Show help.

Default libc (when no --root and no LIBC_PATH given):
  /usr/lib/x86_64-linux-gnu/libc.so.6

デュアルインジェクションパス

glibc のビルドによって、実行可能 LOAD セグメントと次の読み取り専用 LOAD の間の .text ケーブ空間の量は異なります。page_inject はインジェクション時に2つのレイアウトを選択します:

  • パス A -- libc のみ(デフォルト)。 ゾーン C とゾーン A の両方が libc の .text ケーブ内に配置されます。スロットテーブル + CMD + OUTPUT 領域は libc の .hash セクション内に配置されます -- ld.so が実行時に読まないレガシーな SysV ハッシュデータです(代わりに .gnu.hash を使用するため)。.hash がない場合(Arch のモダンなツールチェーン)、page_inject は代わりに .eh_frame_hdr の末尾からスロット領域を切り出します。まず fde_count フィールドを縮小して、アンワインダが解放されたバイトを FDE バイナリ検索インデックスの一部と見なさないようにします(アンワインダは、切り詰められた範囲内に FDE があった IP に対して透過的に .eh_frame の線形スキャンにフォールバックします -- LSB で定められた動作)。

  • パス B -- libc トランポリン + ld.so ペイロード。 一部の glibc ビルドでは、libc のケーブが完全なゾーン C + ゾーン A ペイロードに必要なサイズより小さくなります(Ubuntu 24.04 / glibc 2.39 は 711 B のケーブを提供します)。その場合、page_inject は 36 B のトランポリンを libc のケーブに書き込みます -- これは libc 内で高速パスの .bss キーゲートを処理します -- そして低速パスでは、libc の GOT スロットから _rtld_global(すべての glibc がプライベートにインポートする ld.so 側のシンボル)の ld.so の実行時ベースを計算し、ld.so の .text ケーブ内にあるゾーン C のベースレジスタ変種にジャンプします。スロットテーブル + CMD + OUTPUT + キーはすべて libc に残ります。ld.so 側のゾーン C は、トランポリンが をシードした後、 を通じてそれらに到達します。

どちらのレイアウトも適合しない場合、page_inject はディスク上またはページキャッシュ内の libc や ld.so に何も書き込まずに、きれいに拒否します。

read() プロローグ処理

glibc のバージョンによって、read() の先頭シーケンスは異なります。インジェクタはそれぞれを認識し、フックが置き換えるバイトを読み戻して、ゾーン C の高速パスでエミュレートするため、シングルスレッドの read() は read+N で正しく再開されます:

ゾーン C の高速パスエミュレーションスロットは、既知の最長プロローグ(8バイト)に5バイトの rel32 ジャンプを加えたサイズに設定されます。より短いプロローグは、スロット末尾のバイトを NOP フィラーで埋めて、スロットの全長を一定に保ちます。

ファイル構成

root@kitploit:~
page_inject/
  page_inject.c           Main injector: ELF parsing, vuln primitive,
                          dual-path layout selection, inject + unhook.
  zone_c.asm              Path-A hook dispatcher shellcode.
  zone_c_ld.asm           Path-B hook dispatcher (rbp-base variant).
  trampoline.asm          Path-B 36-byte libc-side stub.
  write_cache.asm         Zone A (vuln write primitive shellcode).
  gen_arrays.sh           Assemble .asm -> asm_bytecode.c.
  asm_bytecode.c          [generated] shellcode byte arrays.
  Makefile                Build system.

テスト済み・サポート対象マトリックス

このエクスプロイトは、以下のコンテナディストリビューションでエンドツーエンドで検証されています。各エントリで、1つのコンテナ内部から page_inject が注入され、同じイメージから起動された兄弟コンテナでフックが発動することが確認されました。コマンドはページキャッシュチャネル経由で正しく実行され、unhook は libc のページ状態をきれいに復元しました。

運用上の注意

  • page_inject は意図的に静的リンクされており、攻撃者自身のプロセスは、インストールするフックの影響を受けません。
  • page_inject は「フック済み」の libc(read() のプロローグに E9 + nops がある状態)を認識し、再インジェクションを拒否します。テスト環境でページキャッシュがその状態で固まっている場合は、そのイメージを使用しているすべてのコンテナを停止し、drop_caches でリセットしてください。
ツールをダウンロード
call
  • フックのインストール。 次にインジェクタはゾーン C(zone_c.asm)を libc の .text ケーブに書き込み、read() の最初の7〜12バイトを、そこへの E9 disp32 ジャンプでパッチします。プロローグの退避されたバイトはゾーン C の高速パスで忠実にエミュレートされます(3種類の glibc プロローグが認識されます -- 以下の「プロローグ処理」を参照)。これでフックは libc のページキャッシュ内で有効になります。

  • フックの伝播。 すべての兄弟コンテナは read() を常時呼び出すプロセスを実行しています(ロギングデーモン、ヘルスチェック、cat /etc/hostname、その他何でも)。兄弟コンテナ内での最初のそのような呼び出しで、乗っ取られたプロローグはゾーン C にジャンプし、そこで以下の処理が行われます:

    • コンテナのルート inode を stat("/") で取得し(名前空間ごとの安定した ID)、それをコンテナのスロットキーとして使用、
    • スロットテーブルをスキャンしてそのキーを持つ既存のエントリを探す、
    • 存在しない場合はキーを登録し、CMD 領域をポーリングして命令を待つ長期稼働のコマンドループ子プロセスを fork() する、
    • read()+N に戻るため、呼び出し元は何も気づかない。 元の兄弟プロセスはそのまま実行を続けます。この時点から、攻撃者はそのコンテナ内にデーモンを持つことになります。
  • コマンドチャネル。 攻撃者は同じ page_inject バイナリを --shell モードで使用して、スロット領域の CMD 領域にコマンドを書き込みます。登録された各兄弟コンテナのフック子プロセスはポーリングし、/bin/sh -c <cmd> を fork して、stdout/stderr を OUTPUT 領域に取り込み、完了を通知して、再びポーリングに戻ります。シェルは出力を表示します。すべての CMD/OUTPUT 書き込みも脆弱性プリミティブを経由するため、特別な権限は不要です。

  • フックの解除。 完了したら、unhook は read() の元のプロローグバイトを復元し、スロットテーブルをゼロで埋めます。フック子プロセスは次の反復で空のスロットを確認し、自己終了します。ページキャッシュの変更自体はクリーンです(カーネルは変更されたページをダーティとマークしないため)。libc を mmap しているすべてのコンテナが停止すれば、drop_caches によってキャッシュは完全に元に戻り -- ディスク上の痕跡は残りません。

  • .bss
    rbp = libc_base
    rbp + offset
    glibc の範囲プロローグ(オプションの endbr64 の後)備考
    2.36 / 2.39cmpb $0x0, __libc_single_threaded(%rip)7 バイト。エミュレートされた cmpb は元の jne .Lthreaded 用に ZF を設定します。
    2.43push rbp; movsxd rdi,edi; xor r9d,r9d7 バイト。バイト単位でエミュレート。
    2.31 / 2.35mov eax, fs:[0x18]8 バイト。バイト単位でエミュレート(FS プレフィックス付き [disp32] は RIP 相対ではなく絶対アドレスなので、バイトコピーは忠実です)。
    イメージglibcインジェクションパスRead() プロローグスロット領域
    debian:bookworm2.36Acmpb.hash
    ubuntu:24.042.39Bcmpb.hash(libc 側、ld.so から rbp 経由でアドレス指定)
    ubuntu:22.042.35ATLS-fs.hash
    fedora:402.39Acmpb.hash
    archlinux:latest2.43Apush-rbp.eh_frame_hdr(末尾切り詰め)