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)