
Proof-of-concept exploit per CVE-2026-41651, una escalatione locale dei privilegi TOCTOU in PackageKit, con analisi tecnica, logica di rilevamento e indicazioni di rimedio.
Classificazione: Artefatto di Valutazione Purple Team
Solo uso autorizzato. Questo documento e lo script di test allegato sono destinati esclusivamente alla validazione di sicurezza interna su sistemi per i quali è stata ottenuta autorizzazione scritta.

| Campo | Valore |
|---|---|
| ID CVE | CVE-2026-41651 |
| CWE | CWE-367 — Condizione di Corsa Time-of-check Time-of-use (TOCTOU) |
| Componente | PackageKit (packagekitd, servizio D-Bus) |
| Interessati | PackageKit ≤ 1.3.4 |
| Corretto in | PackageKit 1.3.5 |
| Impatto | Escalation dei Privilegi Locali → root |
| Vettore d'Attacco | Locale / D-Bus (sessione utente non privilegiato) |
| Divulgato | 2026-04-22 (Deutsche Telekom Red Team) |
| Advisory | GHSA-f55j-vvr9-69xv |
| Commit di correzione | 76cfb675fb31acc3ad5595d4380bfff56d2a8697 |
PackageKit è un livello di astrazione D-Bus per la gestione dei pacchetti di sistema, presente per impostazione predefinita sui desktop basati su GNOME in Debian, Ubuntu, Fedora, RHEL, SUSE e Arch Linux. Poiché media operazioni privilegiate sui pacchetti per conto di utenti non privilegiati, un difetto nel suo flusso di autorizzazione ha un impatto root a livello di sistema.
pk-transaction.c (pre-1.3.5) non applicava un controllo di stato sulla re-invocazione dei metodi di azione. Un client D-Bus poteva chiamare InstallFiles (o altri metodi di azione) più volte sullo stesso oggetto transazione dopo che questo era già passato dallo stato PK_TRANSACTION_STATE_NEW.
Attaccante (non privilegiato)
│
├─① CreateTransaction() → PackageKit restituisce il percorso dell'oggetto transazione (tid)
│
├─② InstallFiles(tid, FLAG_SIMULATE=4, [dummy.pkg])
│ PackageKit accoda un controllo di autorizzazione polkit per dummy.pkg.
│ Nessuna installazione avviene ancora — SIMULATE significa solo prova a secco.
│
├─③ InstallFiles(tid, FLAG_NONE=0, [payload.pkg]) ← finestra TOCTOU
│ Re-invoca sullo stesso tid prima che l'autorizzazione venga risolta.
│ Le versioni vulnerabili sovrascrivono i parametri accodati con payload.pkg.
│
└─④ polkit concede l'autorizzazione (approvata dall'utente o auto-autorizzata)
packagekitd installa payload.pkg come root.
script postinst/post di payload: install -m 4755 /bin/bash /tmp/.suid_bash
L'attaccante esegue /tmp/.suid_bash -p → shell root.
I passi ② e ③ vengono inviati come chiamate D-Bus asincrone non bloccanti sulla stessa connessione e scaricati in un'unica scrittura. I due messaggi arrivano a packagekitd prima che possa elaborare ② e avanzare la macchina a stati, lasciando aperta la finestra TOCTOU. La correzione in 1.3.5 aggiunge un controllo di stato esplicito che restituisce PK_TRANSACTION_ERROR_INVALID_STATE su qualsiasi re-invocazione dopo PK_TRANSACTION_STATE_NEW.
File: cve-2026-41651-purpleteam.py
Linguaggio: Python 3
Dipendenze: python3-gi (introspezione GObject / binding GLib/Gio)
Dimostra la sfruttabilità di CVE-2026-41651 su un sistema di test predisposto per gli scopi di:
| Fase | Azione |
|---|---|
| Setup | Sonda /proc/mounts per selezionare una directory di drop SUID/exec-capable, imposta umask(022), applica chmod esplicito su tutti gli artefatti di build, quindi crea un pacchetto dummy e uno payload in /tmp |
| Exploit | Apre una connessione D-Bus di sistema, crea una transazione PackageKit, lancia la corsa a due chiamate |
| Payload | Lo script post-install del pacchetto copia /bin/bash in <drop_dir>/.suid_bash con modalità 04755, proprietario root. La directory di drop viene risolta a runtime (vedi Supporto Ambienti Hardened) |
| Escalation | Esegue il polling del binario SUID (timeout 90 s), quindi esegue execl su di esso con -p per una shell root |
| Cleanup | Rimuove i file .deb/.rpm temporanei all'uscita (successo o fallimento) |
I sistemi hardened per impostazione predefinita possono bloccare l'exploit in due punti indipendenti. Lo script gestisce entrambi automaticamente.
027 / 077)Un umask ereditato dal processo di 027 o 077 fa sì che mkdir() produca directory 750 o 700. Sia dpkg-deb che rpmbuild devono attraversare l'intero albero di build — se una qualsiasi directory non è leggibile la build fallisce silenziosamente.
Correzione applicata: os.umask(0o022) viene chiamato all'avvio prima che venga creato qualsiasi file o directory. Inoltre, ogni directory e file nell'albero di build riceve un chmod esplicito immediatamente dopo la creazione (0o755 per directory e script, 0o644 per i file di dati), così i permessi corretti sono garantiti indipendentemente dall'umask ereditato.
nosuid / noexec su /tmp| Flag | Effetto sull'exploit |
|---|---|
nosuid | Il kernel rimuove silenziosamente il bit SUID da qualsiasi file memorizzato su quel file system — la bash copiata non diventa mai root |
noexec | Il binario SUID non può essere eseguito affatto |
Correzione applicata: All'avvio, _find_suid_dir() legge /proc/mounts e testa le directory candidate in ordine di preferenza finché non trova un file system che non ha nessuno dei due flag impostati. Il percorso risolto viene quindi incorporato nello script post-install del payload e utilizzato per il polling e l'esecuzione.