はじめに
最近発見されたLinuxカーネルの権限昇格の脆弱性であるCopy Fail(CVE-2026-31431)、Copy Fail 2、およびDirtyFragは、微妙なページキャッシュの破損バグが、root権限への実用的で確実な経路になり得ることを浮き彫りにしています。これらの問題は、攻撃に正当なカーネルインターフェース、ローカル実行、および短い概念実証コードが関わるため、防御側にとって特に重要である。Copy Failは実際に悪用されたことが報告されており、CISAの既知の悪用された脆弱性カタログに追加されました。
これらの脅威を軽減するために、Elastic Security Labsは、特定の概念実証実装にのみ一致するのではなく、これらの脆弱性を取り巻く悪用パターンに焦点を当てた検出ロジックを開発しました。
コピー失敗
コピー失敗は、Linuxカーネルのauthencesn暗号化テンプレートにおける論理バグです。脆弱性はAF_ALGとsplice()連鎖させ、読み取り可能なファイルのページキャッシュに制御された4バイトの書き込みを作成します。実際には、これは/usr/bin/suような setuid バイナリのメモリ内ビューを破損させ、ディスク上のファイルを変更せずに権限を昇格させます。公開されている脆弱性を悪用するコードは732バイトのPythonスクリプトで、Ubuntu、Amazon Linux、RHEL、SUSEといった複数のOSで動作します。
ダーティフラグ
DirtyFragは、同じバグクラスをネットワークスタックに拡張し、2つのページキャッシュ書き込みバリアントを追加します。ESP パスは、 AF_NETLINKを介して XFRM セキュリティ関連付けを使用して、スプライスされたページに対してインプレース暗号化操作を実行し、 /usr/bin/su最小限のルートシェル ELF で上書きします。RxRPC フォールバック パスは、 AF_RXRPCとpcbc(fcrypt)を使用して/etc/passwd破損させ、root のパスワード フィールドをクリアします。どちらの経路でも、ページキャッシュへの書き込みをトリガーする前に、 unshare(CLONE_NEWUSER | CLONE_NEWNET)名前空間機能を取得する必要があります。
DirtyFragはalgif_aeadモジュールに依存しないため、コピー失敗対策のみを適用したシステムでは、依然として脆弱性が存在する可能性があります。
検知
これらの脆弱性については、特定の攻撃手法の実装だけでなく、その根底にある基本要素や動作を検出することに重点を置きました。その違いは重要だ。Copy Failは既に複数の公開再実装版(Python、Go、Rust、C、Metasploit)が存在し、DirtyFragは公開されたC言語の概念実証版として提供されている。特定の概念実証(PoC)のみを検出しようとすると、防御側は一歩遅れをとることになる。
システムコールレベルのプリミティブ(監査済み)
Copy FailとDirtyFragはどちらも、カーネルの暗号化サブシステムにアクセスするためにsocket(AF_ALG)に依存し、インプレース暗号化操作によってページキャッシュが破損するネットワークバッファに読み取り専用ファイルページを挿入するためにsplice()に依存しています。DirtyFragは、 AF_ALGが利用できない場合の代替手段としてsocket(AF_RXRPC)も使用します。これらのプリミティブは、auditd システムコール監査socketを通じてa0 16 進数値26 ( AF_ALG ) または21 ( AF_RXRPC ) で、非ルート プロセスからのsplice呼び出しで確認できます。これらを初期段階のシグナルとして使用し、EQLシーケンスを介して、非ルート呼び出し元から実効uid 0 を取得する最終的な権限昇格ステップと関連付けます。
sequence with maxspan=60s
[any where host.os.type == "linux" and
(
(event.category == "process" and auditd.data.syscall == "socket" and auditd.data.a0 in ("26", "21")) or
(event.category == "process" and auditd.data.syscall == "splice") or
(event.category == "network" and event.action == "bound-socket" and data_stream.dataset == "auditd_manager.auditd" and ?auditd.data.socket.family == "38")
)
and user.id != "0"] by process.pid, host.id, user.id with runs=10
[process where host.os.type == "linux" and event.action == "executed" and
(
(user.effective.id == "0" and user.id != "0") or
(process.name in ("bash", "sh", "zsh", "dash", "fish", "ksh", "busybox") and
process.args in ("-c", "--command", "-ic", "-ci", "-cl", "-lc", "-bash", "-sh", "-zsh", "-dash", "-fish", "-ksh"))
)] by process.parent.pid, host.id, user.id
Example of matches :
名前空間の作成(DirtyFrag固有)
DirtyFragのエクスプロイトチェーンは、名前空間機能を取得するためにunshare(CLONE_NEWUSER | CLONE_NEWNET)にも依存しています。このイベントは、ルートプロセスの実行、またはその直後に発生したsetuid(0)システムコールと関連付けられます。
sequence by host.id, process.parent.pid with maxspan=30s
[process where host.os.type == "linux" and
(
(auditd.data.syscall == "unshare" and auditd.data.class == "namespace" and auditd.data.a0 in ("10000000", "50000000", "70000000", "10020000", "50020000", "70020000")) or
(process.name == "unshare" and
(process.args in ("--user", "--map-root-user", "--map-current-user") or process.args like ("-*U*", "-*r*")))
) and user.id != "0" and user.id != null]
[process where host.os.type == "linux" and
user.id == "0" and user.id != null and
(
process.name in ("su", "sudo", "pkexec", "passwd", "chsh", "newgrp", "doas", "run0", "sg", "dash", "sh", "bash", "zsh", "fish",
"ksh", "csh", "tcsh", "ash", "mksh", "busybox", "rbash", "rzsh", "rksh", "tmux", "screen", "node") or
process.name like ("python*", "perl*", "ruby*", "php*", "lua*")
)]
汎用SUIDバイナリ悪用(プロセス実行イベント)
また、プロセス実行イベントのみを使用した検出オプションについても評価しました。これは、auditdのシステムコール監査よりも多くの環境で有効になっている傾向があるためです。両方のエクスプロイトに共通する最終ステップは、 su 、 sudo 、 pkexec 、 passwd 、 chsh 、またはnewgrpなどのSUIDバイナリのメモリ内実行を破損または影響させ、攻撃者が制御するコードをrootとして実行させることです。
検出機能は、プロセスが実効UID 0で実行され、実際のユーザーがroot以外であり、親プロセスもroot以外であり、SUIDバイナリが最小限の引数で起動され、親プロセスがスクリプトランタイム、シェルワンライナー、またはユーザーが書き込み可能なパスからの実行ファイルである場合に、疑わしい実行を検出します。
process where event.type == "start" and event.action == "exec" and (
(process.user.id == 0 and process.real_user.id != 0) or
(process.group.id == 0 and process.real_group.id != 0)
) and (
(process.name == "su" and process.args_count <= 2) or
(process.name == "sudo" and process.args_count == 1) or
(process.name == "pkexec" and process.args_count == 1) or
(process.name == "passwd" and process.args_count <= 2)
) and
(
process.parent.name like (".*", "python*", "perl*", "ruby*", "lua*", "php*", "node", "deno", "bun", "java") or
process.parent.executable like ("./*", "/tmp/*", "/var/tmp/*", "/dev/shm/*", "/run/user/*", "/var/run/user/*", "/home/*/*") or
(
process.parent.name in ("bash", "dash", "sh", "tcsh", "csh", "zsh", "ksh", "fish", "mksh") and
process.parent.args in ("-c", "-cl", "-lc", "--command", "-ic", "-ci", "-bash", "-sh", "-zsh", "-dash", "-fish", "-ksh") and
process.parent.args_count <= 4
)
)
子プロセスが生成されるのを待つことなく、ES|QL を使用して攻撃活動を積極的に検出することもできます。Copy FailとDirtyFragはどちらも、同じプロセスからsocket(AF_ALG)とsplice()システムコールが交互に発生する特徴的なバーストを生成します。Copy Fail は 48 回繰り返して 192 バイトを書き込み、DirtyFrag は ESP と RxRPC パス全体で同様のパターンに従います。
次のクエリは、これらのシステムコールをプロセスごとに集約し、 AF_ALGまたはAF_RXRPCソケットとspliceコールをボリュームで組み合わせた非ルートプロセスを表示します。
FROM logs-auditd_manager.auditd-default*
| WHERE host.os.type == "linux" AND user.id != "0" AND
(
(event.category == "process" AND auditd.data.syscall == "socket" AND auditd.data.a0 IN ("26", "21")) OR
(event.category == "process" AND auditd.data.syscall == "splice") OR
(event.category == "network" AND event.action == "bound-socket" AND auditd.data.socket.family == "38")
)
| EVAL
is_af_alg = CASE(auditd.data.syscall == "socket" AND auditd.data.a0 == "26", 1, 0),
is_af_rxrpc = CASE(auditd.data.syscall == "socket" AND auditd.data.a0 == "21", 1, 0),
is_splice = CASE(auditd.data.syscall == "splice", 1, 0),
is_bind_alg = CASE(event.action == "bound-socket" AND auditd.data.socket.family == "38", 1, 0)
| STATS
socket_af_alg = SUM(is_af_alg),
socket_af_rxrpc = SUM(is_af_rxrpc),
splice_count = SUM(is_splice),
bind_af_alg = SUM(is_bind_alg),
total_calls = COUNT(*),
first_seen = MIN(@timestamp),
last_seen = MAX(@timestamp)
BY host.name, user.name, process.executable, process.pid
| EVAL
duration_seconds = DATE_DIFF("seconds", first_seen, last_seen),
distinct_syscalls = CASE(
socket_af_alg > 0 AND splice_count > 0 AND bind_af_alg > 0, "af_alg+splice+bind",
socket_af_alg > 0 AND splice_count > 0, "af_alg+splice",
socket_af_rxrpc > 0 AND splice_count > 0, "af_rxrpc+splice",
socket_af_alg > 0, "af_alg_only",
socket_af_rxrpc > 0, "af_rxrpc_only",
splice_count > 0, "splice_only",
"other"
)
| WHERE total_calls >= 10 AND
(socket_af_alg > 0 OR socket_af_rxrpc > 0) AND
splice_count > 0
| SORT total_calls DESC
| LIMIT 50
Auditdのルール:
以下のルールをAuditd統合設定に追加することで、これらのエクスプロイトプリミティブの可視性を確保できます。
-a always,exit -F arch=b64 -S socket -k socket_syscall
-a always,exit -F arch=b32 -S socketcall -k socket_syscall
-a always,exit -F arch=b64 -S splice -k splice-syscall
-a always,exit -F arch=b32 -S splice -k splice-syscall
-a always,exit -F arch=b64 -S bind -k socket_bound
-a always,exit -F arch=b32 -S bind -k socket_bound
検出ルール:
- AF_ALGソケットを介したコピー失敗の可能性(CVE-2026-31431)の悪用
- 不審なSUIDバイナリ実行
- 疑わしいカーネル機能アクティビティルール
- Unshare を使用した名前空間操作
- Privilege Escalation via SUID/SGID
緩和
検出は、セキュリティ強化とパッチ適用と併せて実施されるべきである。これらの脆弱性に対する主な対策は、ディストリビューションのパッチが利用可能になった時点でLinuxカーネルを更新することです。
即時のパッチ適用が不可能な場合は、対象を絞ったモジュールのブロックによって攻撃対象領域を縮小できる。コピー失敗の場合、 algif_aeadモジュールを無効にすることで、エクスプロイトで使用されるAF_ALG AEADパスを防止できます。
echo "install algif_aead /bin/false" > /etc/modprobe.d/copyfail.conf
rmmod algif_aead 2>/dev/null
DirtyFragの場合、影響を受けるネットワークモジュールを無効にすることで、ESPとRxRPCの両方のエクスプロイト経路をブロックできます。
printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf
rmmod esp4 esp6 rxrpc 2>/dev/null
いずれかの緩和策を適用した後、ページキャッシュを削除することで、以前に破損したメモリ内のページがすべて破棄されることが保証されます。
echo 3 > /proc/sys/vm/drop_caches
これらの対策は、カーネルモジュールを無効にすると、影響を受けるサブシステムによっては、IPsec VPN、暗号化アプリケーション、またはその他のサービスに影響を与える可能性があるため、本番環境に展開する前にステージング環境でテストする必要があります。ページキャッシュを削除すると、一時的にI/O負荷が急増するため、ピーク負荷時には避けるべきです。
権限のないユーザーの名前空間の作成を制限することで、DirtyFragや同様の脆弱性に対する耐性も強化されます。
sysctl -w kernel.unprivileged_userns_clone=0
RHEL/Fedoraでは、代わりにuser.max_user_namespaces=0を使用してください。この設定は、特定のコンテナランタイムやブラウザサンドボックスなど、特権のない名前空間に依存するアプリケーションに影響を与える可能性があります。適用前に互換性を評価してください。
参照資料:
- https://copyfail/
- https://xint.io/blog/copy-fail-linux-distributions
- https://github.com/V4bel/dirtyfrag/tree/master
- https://github.com/0xdeadbeefnetwork/Copy_Fail2-Electric_Boogaloo/
- https://access.redhat.com/security/vulnerabilities/RHSB-2026-003
- https://ubuntu.com/blog/copy-fail-vulnerability-fixes-available
- https://aws.amazon.com/security/security-bulletins/rss/2026-027-aws/
- https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a664bf3d603d