Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

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

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

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

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2026-8933 — CVE-2026-8933の概念実証コード(C言語)。cgroupフリーズ、シンボリックリンク競合、udevルールインジェクションを利用してroot権限でコマンドを実行する、snap-confineのローカル権限昇格脆弱性です。 | Kitploit
ツール/GitHubGitHub/yifengchen-cc/cve-2026-8933
特権昇格コンテナセキュリティ脆弱性分析エクスプロイト
GitHubyifengchen-cc/cve-2026-8933

CVE-2026-8933

CVE-2026-8933の概念実証コード(C言語)。cgroupフリーズ、シンボリックリンク競合、udevルールインジェクションを利用してroot権限でコマンドを実行する、snap-confineのローカル権限昇格脆弱性です。

リポジトリを見る
14日前未レビュー

人気

すべて見る →

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

すべてのツールを探索

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

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

CVE-2026-8933 — snap-confine set-capabilities ローカル権限昇格 PoC

Qualys のアドバイザリ「Local Privilege Escalation in set-capabilities versions of snap-confine」(https://cdn2.qualys.com/advisory/2026/07/21/snap-confine-set-capabilities.txt)を再現する。

本機(Ubuntu 25.10 / snapd 2.73+ubuntu25.10.1 / カーネル 6.17)で実測:uid 1000 から出発し、最終的に systemd-udevd が root でコマンドを実行し、/tmp/pwned が uid=0(root) gid=0(root) groups=0(root) を取得した。これは公告の最後のデモで示された効果と一致する。

再現過程でのつまずき記録、および最初の評価でなぜ「悪用不可」と誤判定したのかについては、NOTES.md を参照。

ファイル

ファイル説明
poc.c完全な PoC(単一ファイル)
Makefileビルド(システムにインストール済みの libfuse3.so.3.17.4 をリンク)
include/libfuse3 3.17.4 ヘッダーファイル(システムのランタイムライブラリに一致)
NOTES.mdつまずき記録 + 到達可能性分析の方法論レビュー

脆弱性の原理

cmd/snap-confine/mount-support.c(snapd 2.73):

sc_bootstrap_mount_namespace()      // 第 475 行起
  479:  mkdtemp("/tmp/snap.rootfs_XXXXXX")          <- 目录先归调用者所有
  491:  sc_do_mount(scratch, scratch, NULL, MS_BIND)
  498:  sc_do_mount("none", scratch, NULL, MS_UNBINDABLE)
  503:  sc_do_mount("none", scratch, "tmpfs", ...)
  504:  sc_replicate_base_rootfs(scratch, rootfs_dir, mounts)

sc_replicate_base_rootfs()
  330:  chown(scratch, 0, 0)
  418:  fd = open(full_path, O_CREAT | O_TRUNC, 0644)   <- 缺少 O_NOFOLLOW
  422:  fchown(fd, 0, 0)

行番号は本機の snapd 2.73 のソースコード(cmd/snap-confine/mount-support.c)から取得した。Qualys の公告が引用しているのは同社独自のファイルコピーであり、対応する行番号は 511 / 523 / 530 / 535 / 536 / 362 / 450 / 454 である —— 文は一対一に対応しており、行番号が異なるだけである。

本機の /usr/lib/snapd/snap-confine は mode=755 + file capabilities(setuid-root ではない)であるため、攻撃者の euid で実行されながらほぼ root の権限を保持している。それが作成するディレクトリとファイルはしばらくの間攻撃者に属し、418 行目の open() はシンボリックリンクを辿る。

逆アセンブルで確認済み:本機の open@plt のフラグは 0x240(O_CREAT|O_TRUNC、O_NOFOLLOW なし)である;修正済みの 2.77.1 では同じ箇所が 0x20240 である。

PoC の 6 つのステップ

  1. snap-confine を自身の cgroup に入れる:systemd-run --user --scope --unit=snap.<snap>.<app>。この scope は現在のユーザーに属し、cgroup.freeze が書き込み可能なため、 snap-confine をシステムコール単位で進めたり止めたりできる。
  2. 正しい位置で凍結する:SNAPD_DEBUG=1 のとき snap-confine は associated snap application process ... with device cgroup を出力する。この行は デバイス cgroup の BPF プログラム attach の後、mkdtemp() の前に現れる。これより早く凍結してはならない: freeze trap が存在すると BPF 検証器が EAGAIN を返し、snap-confine はそのまま終了してしまう。 stderr に接続するのは pty(パイプではない):そうでなければ stdio がブロックバッファリングされ、marker はしばしば mkdtemp() と bind mount の後にようやくフラッシュされる。 さらに scope は CPUQuota=1% で起動する:marker から mkdtemp() まではわずか数百マイクロ秒の CPU 時間しかなく、取得プロセスの起床遅延と同じ桁である;スロットリングによりこの区間は実時間でミリ秒級に引き伸ばされ、 凍結を確認したら直ちに cpu.max を 100% に戻す。
  3. scratch ディレクトリを自分たちのディレクトリを指すシンボリックリンクに置き換える:mount(2) は対象 パス内のシンボリックリンクを辿るため、snap-confine の bind/unbindable/tmpfs はすべて <scratch>.dA 上に落ちるが、コピーループは依然として scratch ディレクトリという文字列を使う。
  4. tmpfs がマウントされるのを待ち(ns_mount_count(sc, .dA) >= 2)、シンボリックリンクの向き先を <scratch>.dB に変える。そして dB の中に、base rootfs の最上位の通常ファイルと同じ名前のシンボリックリンクを置き、 /run/udev/rules.d/97-cve-2026-8933-<pid>-<rand>.rules を指すようにする。このときコピーループ のパス解決は dB —— 我々が完全に制御できる通常のディレクトリ —— に落ちる。tmpfs は dA に残され、もはや 邪魔にならない。 注意:.dA 上のマウントが 1 つだけのときは bind mount である(/tmp 自体が tmpfs であり、bind 後も fstype は tmpfs のまま)。2 つ目のマウント(本当の tmpfs)が現れるまで向き先を変えてはならない。そうでなければ snap-confine の mount --make-unbindable がパスがマウントルートでなくなったため EINVAL になる。
  5. open()/fchown() の間のウィンドウを勝ち取る:コピーループは最初の通常ファイルに対して open(O_CREAT|O_TRUNC) を実行し(シンボリックリンクを辿り、root 権限で /run/udev/rules.d の下にファイルを作成する)、続いて fchown(fd,0,0) を行う。ファイルはこの瞬間まだ我々の所有である。PoC は同時に 2 つの手段を使う:
    • 主経路:引き続き freezer を使い、毎回およそ 1 システムコールずつ進める。stat() がファイルを 見つけ、かつ uid がまだ自分自身であれば、直ちに chmod 0666 + udev ルールの書き込みを行う(このとき snap-confine は凍結されており、ウィンドウは「押し広げられて」いる);
    • フォールバック:複数の spinner プロセスが chmod() のホットループで同じウィンドウを奪い合う。spinner が奪った 後、ファイルは root 所有だがモードは 0666 である —— モードビットは fchown(0,0) の後でも 保持されるため、主経路でも同様に書き切ることができる(コードが判定しているのは「書けるかどうか」であり、「所有者かどうか」ではない)。
  6. udev をトリガーする:snap-confine の AppArmor プロファイルは /run/udev/** rw を許可している。 FUSE ファイルシステムをマウント/アンマウントすると、カーネルは bdi サブシステムに対して add uevent を発行し、 systemd-udevd が直ちに root としてルール内の PROGRAM= を実行する。

使い方

make
./poc --diagnose            # 只读自检,不改动任何系统状态
./poc                       #  exploit,默认最多 5 次尝试
./poc --attempts 8          # 增加尝试次数
./poc --file default256.png # 只用 base rootfs 里的这一个普通文件
sudo ./poc --cleanup        # 清理(仅在异常中断留下残留时需要)

パラメータ:

パラメータ説明
--snap NAMEどの snap の rootfs を使うか(デフォルトは firefox、環境変数 CVE8933_SNAP でも指定可能)
--app NAMEその snap の app(デフォルトは meta/snap.yaml の apps: 配下の最初のもの)
--file NAMEbase rootfs 内のこの通常ファイルのみを使用する。一致するものがなければエラーで終了
--attempts N試行回数(デフォルトは 5)
--step US / --step2 US2 つのフェーズの各スライスの CPU 時間(マイクロ秒)、デバッグ用
--keep<scratch>.dA / .dB ディレクトリとシンボリックリンクを保持する(デバッグ用)
--diagnose読み取り専用の自己診断
--cleanup過去の残留物をクリーンアップする(root が必要)

ログは /tmp/cve8933.log に、snap-confine のデバッグ出力は /tmp/cve8933-debug.txt に書き込まれます。

CVE8933_PAYLOAD_CMD='<shell コマンド>' で payload 本体を置き換えられます(他の root コマンドの実行をデモする用途)。この場合、自動クリーンアップは行われません。カスタムコマンドは 引き続き /tmp/pwned に書き込む必要があります。そうでなければ PoC は 今回成功しなかったと判断します。

実測結果

$ ./poc
[+] WIN: uid=0(root) gid=0(root) groups=0(root)
[+] exploit succeeded, evidence in /tmp/pwned

/tmp/cve8933.log における対応する成功プロセス(2026-10-23、変更後の再テスト):

[    0.256] === attempt 1/5 ===
[ 1062.158] marker seen after 750.5 ms
[ 1067.530] scratch dir /tmp/snap.rootfs_oFBraI replaced by a symlink to ...dA
[ 1100.887] bind mount + tmpfs on ...dA after 417 slices
[ 1100.974] scratch directory now points to ...dB with 4 symlinks -> ...rules
[ 1108.782] took the file over while snap-confine was stopped (242 slices)
[ 1108.820] rule written after 797.1 ms
[ 1108.832] target: uid=0 gid=0 mode=0666 size=73 content="ACTION=="add", ..."

(以前の8回試行の実行では2回目で成功した。1回目のフリーズ確認が遅すぎて、snap-confineが同じタイムスライス内でbind mountを実行してしまった。)

/run/udev/rules.d/ に一時的に以下が出現した:

-rw-rw-rw- 1 root root 73 ... 97-cve-2026-8933-85431-485b.rules

内容为 ACTION=="add", SUBSYSTEM=="bdi", PROGRAM="/tmp/cve-2026-8933-payload.sh", udevd ログで読み込み済みであることを確認("marked world-writable ... Proceeding anyway")、payload は root で実行される。

成功率について

ステップ 5 のウィンドウ(open() と fchown() の間)は、システムコール 1~2 個分の幅しかない。メインパス (freezer の単一ステップ + 凍結時の引き継ぎ)はほとんどの試行で成功する。まれに凍結確認が遅れ、snap-confine が同じタイムスライス内に bind mount を追い越してしまい、その試行はそのまま失敗してリトライとなる。デフォルトは 5 回試行; 実測では連続 3 回の実行がそれぞれ 2、1、2 回の試行で成功した。--attempts を大きくすればよい。

クリーンアップと復元

正常に動作した場合、PoC は自己クリーンアップを行う:payload は root 権限で snap-confine が伝播させたすべての /tmp/snap.rootfs_* マウントをアンマウントし、これらのディレクトリを削除し、/run/udev/rules.d/97-cve-2026-8933*.rules および 2 つのスクリプトを削除する。テスト前にベースラインを記録し、実行後に項目ごとに照合したところ、実測では毎回の成功実行後に:

mounts under /tmp/snap.rootfs_*: 0
leftover directories:            0
/run/udev/rules.d:               只有 90-netplan.rules / 99-netplan-ens33.rules
/etc/passwd md5:                 24c3aa8f7b7bdf6fa770b46601abc1a0(未改变)
user scopes:                     0

意図的に残されるのは /tmp/pwned のみです(root 所有、つまり公告のデモにおける証拠ファイル)。これを削除するには root が必要です:sudo ./poc --cleanup。

複数回の試行がすべて失敗した場合(例えばシステム負荷が非常に高い場合)、いくつかの /tmp/snap.rootfs_* マウントとディレクトリが残留する可能性があります。sudo ./poc --cleanup でまとめて削除されます。

安全上の注意

本 PoC は脆弱性の研究と検証のみを目的としています:

  • payload はデフォルトで id > /tmp/pwned を実行して自己クリーンアップするだけで、いかなる認証情報ファイルも変更しません (/etc/passwd、/etc/shadow はいずれも触れておらず、md5 で照合可能);
  • ホスト名、ネットワーク、proxy を変更せず、パッケージのインストール/アンインストールも行わず、再起動もしません;
  • 実行のたびに、自身が生成したマウントを自動的にアンマウントし、一時ファイルを削除します。
ツールをダウンロード