it-swarm.it

Consenti setuid su script Shell

Il bit di autorizzazione setuid indica a Linux di eseguire un programma con l'ID utente effettivo del proprietario invece dell'esecutore:

> cat setuid-test.c

#include <stdio.h>
#include <unistd.h>

int main(int argc, char** argv) {
    printf("%d", geteuid());
    return 0;
}

> gcc -o setuid-test setuid-test.c
> ./setuid-test

1000

> Sudo chown nobody ./setuid-test; Sudo chmod +s ./setuid-test
> ./setuid-test

65534

Tuttavia, questo vale solo per gli eseguibili; Gli script della shell ignorano il bit setuid:

> cat setuid-test2

#!/bin/bash
id -u

> ./setuid-test2

1000

> Sudo chown nobody ./setuid-test2; Sudo chmod +s ./setuid-test2
> ./setuid-test2

1000

Wikipedia dice :

A causa della maggiore probabilità di difetti di sicurezza, molti sistemi operativi ignorano l'attributo setuid quando applicati a script Shell eseguibili.

Supponendo che io sia disposto ad accettare quei rischi, c'è un modo per dire a Linux di trattare lo stesso bit setuid sugli script Shell come sugli eseguibili?

In caso contrario, esiste una soluzione alternativa per questo problema? La mia soluzione attuale è quella di aggiungere una voce sudoers per consentire a ALL di eseguire un determinato script mentre l'utente lo voglio eseguire, con NOPASSWD per evitare la richiesta della password. Il principale svantaggio di ciò è la necessità di inserire sudoers ogni volta che voglio farlo e la necessità che il chiamante Sudo some-script anziché solo some-script

195
Michael Mrozek

Linux ignora il bit setuid¹ su tutti gli eseguibili interpretati (ovvero eseguibili che iniziano con una riga #!). comp.unix.questions FAQ spiega i problemi di sicurezza con gli script Shell setuid. Questi problemi sono di due tipi: relativi a Shebang e relativi a 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 funzione load_script , prima della chiamata a open_exec, Ma non ho ancora testato.


Setuid Shebang

Esiste una condizione di gara inerente al modo in cui Shebang (#!) Viene generalmente implementato:

  1. Il kernel apre l'eseguibile e scopre che inizia con #!.
  2. Il kernel chiude l'eseguibile e apre invece l'interprete.
  3. Il kernel inserisce il percorso dello script nell'elenco degli argomenti (come 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 ignora il bit setuid quando rileva 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 sicuro setuid Shebang usando un ulteriore caratteristica: il percorso /dev/fd/N si riferisce al file già aperto sul descrittore file [~ # ~] n [~ # ~] (quindi l'apertura di /dev/fd/N è all'incirca equivalente a dup(N)). Molti sistemi unix (incluso Linux) hanno /dev/fd Ma non script setuid.

  1. Il kernel apre l'eseguibile e scopre che inizia con #!. Supponiamo che il descrittore di file per l'eseguibile sia 3.
  2. Il kernel apre l'interprete.
  3. Il kernel inserisce /dev/fd/3 L'elenco degli argomenti (come argv[1]) Ed esegue l'interprete.

La pagina Shebang di Sven Mascheck contiene molte informazioni su Shebang attraverso gli unice, incluso 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 è no sui programmi interpretati vs compilati. Il problema è se il tuo sistema di runtime si comporta in modo sicuro se eseguito con privilegi.

  • Qualsiasi eseguibile binario nativo collegato dinamicamente è in qualche modo interpretato dal caricatore dinamico (es. /lib/ld.so), Che carica le librerie dinamiche richieste da il programma. Su molti unices, puoi configurare il percorso di ricerca delle 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'invocatore del programma può eseguire un codice arbitrario nel contesto di quel programma inserendo un libc.so Appositamente creato in $LD_LIBRARY_PATH (Tra le altre tattiche). Tutti i sistemi sani ignorano le variabili LD_* Negli eseguibili setuid.

  • In shell come sh, csh e derivati, le variabili d'ambiente diventano automaticamente parametri Shell. Attraverso parametri come PATH, IFS e molti altri, l'invocatore dello script ha molte opportunità di eseguire codice arbitrario nel contesto degli script 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 caratteristiche 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. supporta esplicitamente gli 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 . In passato era necessario che gli script Perl setuid richiedessero #!/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 di per sé per prevenire questi problemi . In effetti, può peggiorare la situazione peggio, perché potrebbe impedire all'ambiente di runtime di rilevare che è invocato con privilegi e ignorare la sua configurabilità di runtime.

Un wrapper binario nativo può rendere sicuro uno script 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, assicurati che l'opzione env_reset Sia attivata, che setenv sia disattivato e che env_file E env_keep Contengano solo variabili innocue.


TL, DR:

  • Setuid Shebang è insicuro ma di solito ignorato.
  • Se si esegue un programma con privilegi (tramite Sudo o setuid), scrivere il codice nativo o Perl o avviare il programma con un wrapper che disinfetta l'ambiente (come Sudo con l'opzione env_reset).

¹ Questa discussione si applica ugualmente se si sostituisce "setgid" con "setuid"; sono entrambi ignorati dal kernel Linux negli script

Un modo per risolvere questo problema è chiamare lo script Shell da un programma che può usare il bit setuid.
è qualcosa di simile al Sudo. Ad esempio, ecco come lo faresti in un programma C:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    setuid( 0 );   // you can set it at run time also
    system( "/home/pubuntu/setuid-test2.sh" );
    return 0;
 }

Salvalo come setuid-test2.c.
compilare
Ora esegui il setuid su questo programma binario:

su - nobody   
[enter password]  
chown nobody:nobody a.out  
chmod 4755 a.out  

Ora dovresti essere in grado di eseguirlo e vedrai che lo script viene eseguito senza autorizzazioni nessuno.
Ma anche qui devi codificare il percorso dello script o passarlo come riga di comando arg sopra exe.

57
Hemant

Prefisso alcuni script che si trovano in questa barca così:

#!/bin/sh
[ "root" != "$USER" ] && exec Sudo $0 "[email protected]"

Nota che questo non usa setuid ma esegue semplicemente il file corrente con Sudo.

24
rcrowley

Se vuoi evitare di chiamare Sudo some_script puoi semplicemente fare:

  #!/ust/bin/env sh

  Sudo /usr/local/scripts/your_script

I programmi SETUID devono essere progettati con estrema cura in quanto eseguiti con privilegi di root e gli utenti hanno un ampio controllo su di essi. Hanno bisogno di controllare la sanità mentale tutto. Non puoi farlo con gli script perché:

  • Le conchiglie sono grandi software che interagiscono pesantemente con l'utente. È quasi impossibile controllare il buon senso di tutto, soprattutto perché la maggior parte del codice non è destinata a funzionare in tale modalità.
  • Gli script sono per lo più una soluzione veloce e di solito non sono preparati con tanta cura da consentire setuid. Hanno molte funzionalità potenzialmente pericolose.
  • Dipendono fortemente da altri programmi. Non è sufficiente che Shell sia stato controllato. Anche sed, awk, ecc. dovrebbero essere controllati

Si noti che Sudo fornisce alcuni controlli di integrità ma non è sufficiente: controllare ogni riga nel proprio codice.

Come ultima nota: considerare l'utilizzo delle funzionalità. Consentono di assegnare a un processo in esecuzione come utente privilegi speciali che normalmente richiederebbero privilegi di root. Tuttavia, ad esempio, sebbene ping debba manipolare la rete, non è necessario che abbia accesso ai file. Non sono sicuro comunque se siano ereditati.

12
Maciej Piechotka

comando super [-r reqpath] [args]

Super consente agli utenti specificati di eseguire script (o altri comandi) come se fossero root; oppure può impostare i gruppi uid, gid e/o supplementari in base al comando prima di eseguire il comando. È pensato per essere un'alternativa sicura alla creazione di script setuid root. Super consente inoltre agli utenti ordinari di fornire comandi per l'esecuzione da parte di altri; questi vengono eseguiti con uid, gid e gruppi dell'utente che offre il comando.

Super consulta un file `` super.tab '' per vedere se l'utente è autorizzato ad eseguire il comando richiesto. Se viene concessa l'autorizzazione, super eseguirà pgm [args], dove pgm è il programma associato a questo comando. (L'esecuzione root è consentita per impostazione predefinita, ma può comunque essere negata se una regola esclude root. Agli utenti ordinari è vietata l'esecuzione per impostazione predefinita.)

Se command è un collegamento simbolico (o anche hard link) al super programma, digitare% command args equivale a digitare% super command args (Il comando non deve essere super o super non riconoscerà che è stato invocato tramite un collegamento.)

http://www.ucolick.org/~will/RUE/super/README

http://manpages.ubuntu.com/manpages/utopic/en/man1/super.1.html

5
Nizam Mohamed

Puoi creare un alias per Sudo + il nome dello script. Ovviamente, questo è ancora più lavoro da configurare, dal momento che devi anche impostare un alias, ma ti evita di dover digitare Sudo.

Ma se non ti dispiace per gli orribili rischi per la sicurezza, usa una Shell setuid come interprete per lo script Shell. Non so se funzionerà per te, ma credo che potrebbe.

Lasciatemi dire che sconsiglio di farlo, comunque. Lo sto solo citando per scopi educativi ;-)

4
wzzrd

Se per qualche motivo Sudo non è disponibile, puoi scrivere uno script wrapper sottile in C:

#include <unistd.h>
int main() {
    setuid(0);
    execle("/bin/bash","bash","/full/path/to/script",(char*) NULL,(char*) NULL);
}

E una volta compilato, impostalo come setuid con chmod 4511 wrapper_script.

Questo è simile a un'altra risposta pubblicata, ma esegue lo script con un ambiente pulito e usa esplicitamente /bin/bash Invece della Shell chiamata da system(), e quindi chiude alcune potenziali falle di sicurezza.

Si noti che ciò elimina completamente l'ambiente. Se si desidera utilizzare alcune variabili ambientali senza aprire le vulnerabilità, è sufficiente utilizzare Sudo.

Ovviamente, vuoi assicurarti che lo script stesso sia scrivibile solo da root.

2
Chris