
CVSS 3.x : 7.8 HIGH
CWE : CWE-367 (Time-of-Check Time-of-Use)
Fix : PackageKit 1.3.5+
Affects : PackageKit 1.0.2 – 1.3.4
PackageKit の D-Bus トランザクションハンドラにおける TOCTOU レースコンディションを介したローカル権限昇格。
特権を持たないローカルユーザーは、任意のパッケージを root としてインストールできます — ローカルセッションを持っていること以外の認証は不要です。
ラボ / 許可されたテストのみ (HTB、CTF、書面によるスコープが定められたエンゲージメント)。
python3 exploit.py
これだけです。/tmp/.suid_bash をドロップし、その後 root シェルに exec します。
カスタム SUID パスまたはタイムアウト:
SUID_BIN=/dev/shm/.sh TIMEOUT=90 python3 exploit.py
apt install python3-gi dpkg-dev
このエクスプロイトは実行時に 2 つの .deb パッケージをビルドします — コンパイラは不要です。
src/pk-transaction.c 内の 3 つのバグが連鎖します:
InstallFiles() は 状態チェックなしで cached_transaction_flags と cached_full_paths を上書きします。同じトランザクションへの 2 回目の D-Bus 呼び出しは、トランザクションがすでに実行中であっても、それらを無条件に置き換えます。
pk_transaction_set_state() は、バグ 1 で上書きされたフラグをロールバック せずに、不正な後方遷移 (RUNNING→READY など) を拒否します。破損したフラグは残り続けます。
pk_transaction_run() は、認可時ではなくディスパッチ時 (GLib idle) に cached_transaction_flags を読み取ります。バックエンドは攻撃者のフラグを参照します。
PK_TRANSACTION_FLAG_SIMULATE (0x4) を設定すると、polkit の認可チェックが完全にスキップされます。呼び出し 1 はユーザーの操作なしで認可されます。
Attacker packagekitd
│ │
│ CreateTransaction() │ state = NEW
│─────────────────────────────────►│
│ │
│ InstallFiles(SIMULATE, dummy) │ polkit skipped (SIMULATE)
│─────────────────────────────────►│ state = READY, idle queued
│ │
│ InstallFiles(NONE, payload) │ flags+paths overwritten
│─────────────────────────────────►│ set_state(WAITING_FOR_AUTH) rejected
│ │ state stays READY (BUG 2)
│ │
│ [idle fires]│
│ │ pk_transaction_run() reads NONE (BUG 3)
│ │ → dpkg installs payload as root
│ │ → postinst: chmod +s /tmp/.suid_bash
│ │
│ execv("/tmp/.suid_bash -p") │
│─────────────────────────────────►│ euid=0(root)
両方の呼び出しは GLib idle が発火する前に到達します — レースに勝つ必要はなく、決定的です。
Ubuntu Desktop 18.04–26.04、Debian Trixie、Fedora 43、RockyLinux 10.1、およびデーモンが有効な状態で PackageKit 1.0.2–1.3.4 を同梱するすべてのディストリビューション。
systemctl mask packagekittransaction->state != PK_TRANSACTION_STATE_NEW → 拒否