Share
## https://sploitus.com/exploit?id=8F870A9B-727B-5C66-B186-F473AFA58ADC
# CVE-2026-41651
```
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
```
Local privilege escalation via TOCTOU race condition in PackageKit's D-Bus transaction handler.
Any unprivileged local user can install arbitrary packages as root β no authentication beyond having a local session.
> **Lab / authorized testing only** (HTB, CTF, engagement with written scope).
---
## Usage
```bash
python3 exploit.py
```
That's it. Drops `/tmp/.suid_bash`, then `exec` into a root shell.
Custom SUID path or timeout:
```bash
SUID_BIN=/dev/shm/.sh TIMEOUT=90 python3 exploit.py
```
---
## Requirements
```bash
apt install python3-gi dpkg-dev
```
The exploit builds two `.deb` packages at runtime β no compiler needed.
---
## Root cause
Three bugs in `src/pk-transaction.c` chain together:
### Bug 1 β Unconditional flag overwrite
`InstallFiles()` overwrites `cached_transaction_flags` and `cached_full_paths` with **no state check**. A second D-Bus call on the same transaction blindly replaces them β even while the transaction is already running.
### Bug 2 β Silent state-machine guard
`pk_transaction_set_state()` rejects illegal backward transitions (RUNNINGβREADY, etc.) **without** rolling back the overwritten flags from Bug 1. The corrupted flags persist.
### Bug 3 β Late flag read
`pk_transaction_run()` reads `cached_transaction_flags` at dispatch time (GLib idle), not at authorization time. The backend sees the attacker's flags.
### Bonus β SIMULATE bypasses polkit
Setting `PK_TRANSACTION_FLAG_SIMULATE` (0x4) skips the polkit authorization check entirely. Call 1 gets authorized with no user interaction.
---
## Attack chain
```
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)
```
Both calls arrive before the GLib idle fires β no race to win, it's deterministic.
---
## Affected systems
Ubuntu Desktop 18.04β26.04, Debian Trixie, Fedora 43, RockyLinux 10.1, and any distro shipping PackageKit 1.0.2β1.3.4 with the daemon active.
---
## Remediation
1. Upgrade to **PackageKit 1.3.5+**
2. Mask the service if upgrade isn't possible: `systemctl mask packagekit`
3. The fix adds a state guard: `transaction->state != PK_TRANSACTION_STATE_NEW` β reject
---
## References
- [NVD β CVE-2026-41651](https://nvd.nist.gov/vuln/detail/CVE-2026-41651)
- [GitHub Advisory β GHSA-f55j-vvr9-69xv](https://github.com/PackageKit/PackageKit/security/advisories/GHSA-f55j-vvr9-69xv)
- [Fix commit](https://github.com/PackageKit/PackageKit/commit/76cfb675fb31acc3ad5595d4380bfff56d2a8697)