
Локальное повышение привилегий через состояние гонки TOCTOU в PackageKit.
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
Локальное повышение привилегий через гонку TOCTOU в D-Bus обработчике транзакций PackageKit.
Любой непривилегированный локальный пользователь может установить произвольные пакеты от имени root — аутентификация не требуется, достаточно наличия локальной сессии.
Только для лабораторных / авторизованных тестов (HTB, CTF, работы по письменному согласованию).
python3 exploit.py
Вот и всё. Создаёт /tmp/.suid_bash, затем выполняет exec в root-оболочку.
Свой путь для SUID или таймаут:
SUID_BIN=/dev/shm/.sh TIMEOUT=90 python3 exploit.py
apt install python3-gi dpkg-dev
Эксплойт собирает два .deb-пакета во время выполнения — компилятор не нужен.
Три ошибки в src/pk-transaction.c, объединяющиеся в цепочку:
InstallFiles() перезаписывает cached_transaction_flags и cached_full_paths без проверки состояния. Второй D-Bus-вызов в той же транзакции вслепую заменяет их — даже когда транзакция уже выполняется.
pk_transaction_set_state() отклоняет недопустимые обратные переходы (RUNNING→READY и т.д.) без отката перезаписанных флагов из ошибки 1. Повреждённые флаги сохраняются.
pk_transaction_run() читает cached_transaction_flags в момент диспетчеризации (idle в GLib), а не в момент авторизации. Бэкенд видит флаги атакующего.
Установка 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)
Оба вызова поступают до срабатывания idle в GLib — гонку выигрывать не нужно, всё детерминировано.
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 → отказ