Linux ignora il bit setuid¹ su tutti gli eseguibili interpretati (ovvero eseguibili che iniziano con una #!
linea). Le FAQ di comp.unix.questions spiegano i problemi di sicurezza con gli script di shell setuid. Questi problemi sono di due tipi: correlati a shebang e relativi alla shell; Vado più in dettaglio di seguito.
Se non ti interessa la sicurezza e vuoi consentire gli script setuid, sotto Linux, dovrai patchare il kernel. A partire dai kernel 3.x, penso che sia necessario aggiungere una chiamata a install_exec_creds
nella load_script
funzione, prima della chiamata a open_exec
, ma non ho ancora testato.
Setuid Shebang
Esiste una condizione di competizione inerente al modo in cui shebang ( #!
) viene generalmente implementato:
- Il kernel apre l'eseguibile e trova che inizia con
#!
.
- Il kernel chiude l'eseguibile e apre invece l'interprete.
- Il kernel inserisce il percorso dello script nell'elenco degli argomenti (as
argv[1]
) ed esegue l'interprete.
Se gli script setuid sono consentiti con questa implementazione, un utente malintenzionato può invocare uno script arbitrario creando un collegamento simbolico a uno script setuid esistente, eseguendolo e organizzando la modifica del collegamento dopo che il kernel ha eseguito il passaggio 1 e prima che l'interprete riesca a aprendo il suo primo argomento. Per questo motivo, la maggior parte dei unices ignorano il bit setuid quando rilevano uno shebang.
Un modo per proteggere questa implementazione sarebbe che il kernel blocchi il file di script fino a quando l'interprete non lo ha aperto (si noti che ciò deve impedire non solo di scollegare o sovrascrivere il file, ma anche di rinominare qualsiasi directory nel percorso). Ma i sistemi unix tendono a evitare i blocchi obbligatori e i collegamenti simbolici renderebbero una funzione di blocco corretta particolarmente difficile e invasiva. Non penso che nessuno lo faccia in questo modo.
Alcuni sistemi unix (principalmente OpenBSD, NetBSD e Mac OS X, che richiedono tutti un'impostazione del kernel per essere abilitati) implementano shebang setuid sicuri usando una funzione aggiuntiva: il percorso si riferisce al file già aperto sul descrittore di file N (quindi l'apertura è approssimativamente equivalente a ). Molti sistemi unix (incluso Linux) hanno ma non script setuid./dev/fd/N
/dev/fd/N
dup(N)
/dev/fd
- Il kernel apre l'eseguibile e trova che inizia con
#!
. Supponiamo che il descrittore di file per l'eseguibile sia 3.
- Il kernel apre l'interprete.
- Il kernel inserisce
/dev/fd/3
la lista degli argomenti (as argv[1]
) ed esegue l'interprete.
La pagina shebang di Sven Mascheck contiene molte informazioni su shebang attraverso gli unici, incluso il supporto setuid .
Interpreti setuidi
Supponiamo che tu sia riuscito a far funzionare il tuo programma come root, sia perché il tuo sistema operativo supporta setuid shebang o perché hai usato un wrapper binario nativo (come sudo
). Hai aperto una falla di sicurezza? Forse . Il problema qui non riguarda i programmi interpretati o compilati. Il problema è se il tuo sistema di runtime si comporta in modo sicuro se eseguito con privilegi.
Qualsiasi eseguibile binario nativo collegato dinamicamente viene in qualche modo interpretato dal caricatore dinamico (ad es. /lib/ld.so
), Che carica le librerie dinamiche richieste dal programma. Su molti unices, puoi configurare il percorso di ricerca per le librerie dinamiche attraverso l'ambiente ( LD_LIBRARY_PATH
è un nome comune per la variabile d'ambiente) e persino caricare librerie aggiuntive in tutti i binari eseguiti ( LD_PRELOAD
). L'invoker del programma può eseguire codice arbitrario nel contesto di quel programma inserendo un appositamente predisposto libc.so
in $LD_LIBRARY_PATH
(tra le altre tattiche). Tutti i sistemi sani ignorano le LD_*
variabili negli eseguibili setuid.
In shell come sh, csh e derivati, le variabili di ambiente diventano automaticamente parametri di shell. Attraverso parametri come PATH
, IFS
e molti altri, l'invocatore dello script ha molte opportunità di eseguire codice arbitrario nel contesto degli script della shell. Alcune shell impostano queste variabili su valori predefiniti sani se rilevano che lo script è stato invocato con privilegi, ma non so che esiste un'implementazione particolare di cui mi fiderei.
La maggior parte degli ambienti di runtime (nativi, bytecode o interpretati) hanno funzionalità simili. Pochi prendono precauzioni speciali negli eseguibili setuid, anche se quelli che eseguono codice nativo spesso non fanno nulla di più fantasioso del collegamento dinamico (che prende precauzioni).
Perl è un'eccezione notevole. Esso supporta esplicitamente script setuid in modo sicuro. In effetti, il tuo script può eseguire setuid anche se il tuo sistema operativo ha ignorato il bit setuid sugli script. Questo perché perl viene fornito con un helper root setuid che esegue i controlli necessari e reinvoca l'interprete sugli script desiderati con i privilegi desiderati. Questo è spiegato nel manuale perlsec . Ha usato essere che gli script Perl setuid bisogno #!/usr/bin/suidperl -wT
invece di #!/usr/bin/perl -wT
, ma sulla maggior parte dei sistemi moderni, #!/usr/bin/perl -wT
è sufficiente.
Si noti che l'utilizzo di un wrapper binario nativo non fa nulla in sé per prevenire questi problemi . In effetti, può peggiorare la situazione , perché potrebbe impedire all'ambiente di runtime di rilevare che è invocato con privilegi e di bypassare la sua configurabilità di runtime.
Un wrapper binario nativo può rendere sicuro uno script di shell se il wrapper disinfetta l'ambiente . Lo script deve fare attenzione a non fare troppe ipotesi (ad esempio sulla directory corrente), ma questo va bene. È possibile utilizzare sudo per questo, a condizione che sia impostato per disinfettare l'ambiente. Le variabili nella blacklist sono soggette a errori, quindi sempre nella whitelist. Con sudo, assicurarsi che l' env_reset
opzione è attiva, che setenv
è spento, e che env_file
e env_keep
contengono solo variabili innocui.
TL, DR:
- Setuid Shebang è insicuro ma di solito ignorato.
- Se si esegue un programma con privilegi (tramite sudo o setuid), scrivere codice nativo o perl o avviare il programma con un wrapper che disinfetta l'ambiente (come sudo con l'
env_reset
opzione).
¹ Questa discussione si applica ugualmente se si sostituisce "setgid" con "setuid"; entrambi sono ignorati dal kernel Linux sugli script