Featured image of post Capabilities: oltre il root tutto-o-niente

Capabilities: oltre il root tutto-o-niente

Storicamente root poteva tutto e un utente normale quasi niente: nessuna via di mezzo. Le capabilities spezzano i poteri di root in tanti permessi distinti, così un programma può ottenere solo quello che gli serve — aprire una porta bassa, per esempio — senza essere root. Come leggerle, assegnarle ai file al posto del SUID e azzerarle nei servizi.

Perché conta

Nel modello classico c’erano solo due stati: root (può tutto) o non-root (quasi niente). Questo creava un problema: un programma che aveva bisogno di un solo potere di root — per esempio aprire la porta 80, riservata a root — doveva girare interamente da root, con tutti i suoi poteri. Le capabilities spezzano l’onnipotenza di root in ~40 permessi distinti, così si può concedere esattamente quello che serve. È il privilegio minimo applicato ai poteri del kernel.

Alcune capabilities chiave

1
2
3
4
5
6
CAP_NET_BIND_SERVICE  → aprire porte sotto la 1024 (es. 80, 443) senza essere root
CAP_NET_ADMIN         → configurare la rete (interfacce, firewall)
CAP_SYS_ADMIN         → "il nuovo root": enorme e pericolosa, da evitare come la peste
CAP_DAC_OVERRIDE      → ignorare i permessi dei file (quasi root sui file)
CAP_CHOWN             → cambiare proprietario dei file
CAP_SYS_PTRACE        → ispezionare/alterare altri processi (debug, ma anche attacco)

CAP_SYS_ADMIN è tristemente nota come “il nuovo root”: concede talmente tanto che darla equivale quasi a dare root. Diffidane.

Leggere e assegnare capabilities sui file

Si possono attaccare a un eseguibile (file capabilities): il programma le ottiene all’esecuzione, senza essere SUID-root. È l’alternativa moderna e più sicura al SUID.

1
2
3
4
5
6
7
getcap -r /usr/bin 2>/dev/null         # elenca i binari con capabilities assegnate
# es: /usr/bin/ping = cap_net_raw+ep   ← ping non è più SUID-root, ha solo questa capability

# dare a un'app il permesso di aprire la porta 80, senza renderla root né SUID
setcap 'cap_net_bind_service=+ep' /usr/local/bin/myapp
getcap /usr/local/bin/myapp            # verifica
setcap -r /usr/local/bin/myapp         # rimuovere tutte le capabilities

Il +ep significa effective + permitted: la capability è attiva all’avvio. Perché questo è meglio del SUID? Perché myapp ottiene solo il potere di aprire porte basse, non l’intero root: se viene compromesso, l’attaccante non eredita l’onnipotenza.

Azzerare le capabilities nei servizi

Nel sandboxing systemd abbiamo visto CapabilityBoundingSet=. Qui si chiude il cerchio: si parte da zero e si concede solo lo stretto necessario.

1
2
3
4
# nella unit del servizio
CapabilityBoundingSet=CAP_NET_BIND_SERVICE   # il servizio può SOLO aprire porte basse
AmbientCapabilities=CAP_NET_BIND_SERVICE     # e le riceve all'avvio, senza essere root
# tutto il resto dei poteri di root è precluso, anche se gira come root

Così un web server può legarsi alla 443 pur avendo azzerato ogni altro potere: compromesso, non può caricare moduli kernel, non può ignorare i permessi dei file, non può amministrare la rete.

Verificare le capabilities di un processo

1
2
3
grep Cap /proc/<pid>/status            # bitmask delle capabilities del processo
getpcaps <pid>                         # versione leggibile
capsh --decode=0000000000003000        # decodifica una bitmask in nomi

Conclusione

Le capabilities trasformano il root tutto-o-niente in un insieme di permessi concedibili uno a uno: un binario ottiene solo CAP_NET_BIND_SERVICE invece di tutto root, un servizio azzera il bounding set e riceve solo ciò che gli serve. È privilegio minimo a livello di kernel. Ma capabilities e permessi restano discrezionali; per una politica che nemmeno root o il proprietario possano aggirare serve il controllo d’accesso obbligatorio: SELinux, prossimo capitolo.

Built with Hugo
Theme Stack designed by Jimmy