Utilizzo corretto del bit setuid


45

Ho un processo che ha bisogno dei privilegi di root quando viene eseguito da un utente normale. Apparentemente posso usare il "bit setuid" per ottenere questo risultato. Qual è il modo corretto di farlo su un sistema POSIX?

Inoltre, come posso farlo con uno script che utilizza un interprete (bash, perl, python, php, ecc.)?

Risposte:


53

Il bit setuid può essere impostato su un file eseguibile in modo che, quando eseguito, il programma disponga dei privilegi del proprietario del file anziché dell'utente reale, se diversi. Questa è la differenza tra uid effettivo (id utente) e uid reale .

Alcune utility comuni, come ad esempio passwd, sono di proprietà di root e configurate in questo modo per necessità ( passwddeve accedere a ciò /etc/shadowche può essere letto solo da root).

La strategia migliore quando si esegue questa operazione è fare tutto ciò che è necessario fare come superutente immediatamente, quindi abbassare i privilegi in modo che errori o abusi abbiano meno probabilità di verificarsi durante l'esecuzione di root. Per fare ciò, devi impostare l' UID efficace del processo sul suo UID reale. In POSIX C:

#define _POSIX_C_SOURCE 200112L // Needed with glibc (e.g., linux).
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

void report (uid_t real) {
    printf (
        "Real UID: %d Effective UID: %d\n",
        real,
        geteuid()
    );
}

int main (void) {
    uid_t real = getuid();
    report(real);
    seteuid(real);
    report(real);
    return 0;
}

Le funzioni pertinenti, che dovrebbero avere un equivalente nella maggior parte delle lingue di livello superiore se utilizzate comunemente nei sistemi POSIX:

  • getuid(): Ottieni il vero uid.
  • geteuid(): Ottieni l' UID efficace .
  • seteuid(): Imposta l' Uid efficace .

Non puoi fare nulla con l'ultimo inappropriato per il vero uid tranne nella misura in cui il bit setuid è stato impostato sull'eseguibile . Quindi per provare questo, compila gcc test.c -o testuid. È quindi necessario, con privilegi:

chown root testuid
chmod u+s testuid

L'ultimo imposta il bit setuid. Se ora esegui ./testuidun normale utente, il processo verrà eseguito per impostazione predefinita con uid 0 effettivo, root.

Che dire degli script?

Questo varia da piattaforma a piattaforma , ma su Linux, le cose che richiedono un interprete, incluso il bytecode, non possono usare il bit setuid a meno che non sia impostato sull'interprete (che sarebbe molto molto stupido). Ecco un semplice script perl che imita il codice C sopra:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

print "Real UID: $< Effective UID: $>\n";
$> = $<; # Not an ASCII art greedy face, but genuine perl...
print "Real UID: $< Effective UID: $>\n"; 

Fedele alle sue radici * nixy, perl ha incorporato variabili speciali per uid ( $>) e real uid ( $<) efficaci . Ma se provi lo stesso chowne lo chmodusi con l'eseguibile compilato (da C, esempio precedente), non farà alcuna differenza. Lo script non può ottenere privilegi.

La risposta a questa è usare un binario setuid per eseguire lo script:

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

int main (int argc, char *argv[]) {
    if (argc < 2) {
        puts("Path to perl script required.");
        return 1;
    }
    const char *perl = "perl";
    argv[0] = (char*)perl;
    return execv("/usr/bin/perl", argv);
}

Compila questo gcc --std=c99 whatever.c -o perlsuid, quindi chown root perlsuid && chmod u+s perlsuid. Ora puoi eseguire qualsiasi script perl con un uid effettivo di 0, indipendentemente da chi lo possiede.

Una strategia simile funzionerà con php, python, ecc. Ma ...

# Think hard, very important:
>_< # Genuine ASCII art "Oh tish!" face

PER FAVORE, PER FAVORE NON lasciare questo tipo di cose in giro . Molto probabilmente, in realtà vuoi compilare il nome dello script come un percorso assoluto , cioè sostituire tutto il codice main()con:

    const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
    return execv("/usr/bin/perl", (char * const*)args);

Si assicurano /opt/suid_scriptsche tutto sia in sola lettura per utenti non root. Altrimenti, qualcuno potrebbe scambiare qualsiasi cosa per whatever.pl.

Inoltre, attenzione che molti linguaggi di scripting consentono alle variabili di ambiente di cambiare il modo in cui eseguono uno script . Ad esempio, una variabile di ambiente potrebbe causare il caricamento di una libreria fornita dal chiamante, consentendo al chiamante di eseguire codice arbitrario come root. Pertanto, a meno che non si sappia che sia l'interprete che lo script stesso sono robusti rispetto a tutte le possibili variabili di ambiente, NON FARLO .

Quindi cosa dovrei fare invece?

Un modo più sicuro per consentire a un utente non root di eseguire uno script come root è quello di aggiungere una regola sudo e far eseguire l'utente sudo /path/to/script. Sudo elimina la maggior parte delle variabili di ambiente e consente inoltre all'amministratore di selezionare con precisione chi può eseguire il comando e con quali argomenti. Vedi Come eseguire un programma specifico come root senza una richiesta di password? per un esempio.


1
molte shell si comporteranno diversamente se chiamate come -privilegedshell e, se chiamate da uno script responsabile, possono anche essere invocate in suid rootmodo sicuro in quel modo. Temo tuttavia che l'idea di una sceneggiatura responsabile sia un po 'un sogno nel mondo reale. Ad ogni modo, probabilmente vale la pena ricordare quanto sia importante evitare scritture di ogni tipo, se possibile, mentre le autorizzazioni sono elevate ...
Mikeserv,

@mikeserv Non ho familiarità con i concetti di " -privilegedshell" e "script responsabile". Vuoi fare un'altra risposta sulle conchiglie? Naturalmente non posso prometterti il ​​segno di spunta;) Non ci sono molti buoni motivi per farlo - sudoè un'opzione molto migliore se possibile - L'ho scritto come una risposta generica derivante da una domanda iniziale sull'autenticazione della password di sistema in php .
Riccioli d'oro

1
Non voglio davvero farlo - gli script responsabili sono davvero difficili - al di là di me, probabilmente. Ma devo dire che sono d'accordo con la tua analisi del sudo- considero sudocome psicotica in quanto finge , non di essere root. Puoi anche mettere globs di guscio nel tuo sudoers. suè meglio secondo me a scopi di scripting, sebbene sudosia buono per le applicazioni live. Ma nessuno dei due è esplicito come una sceneggiatura con una #!/bin/ksh -pbangline o qualunque cosa sia suid kshin $PATH.
mikeserv,

Mentre questa risposta risponde alla domanda, gli script setuid sono una ricetta per aprirti a una vulnerabilità di escalation di privilegi locali. C'è un motivo per cui gli script non possono essere impostati facilmente. La risposta corretta qui è sudo.
mdadm,

Mi sono preso la libertà di modificare la tua risposta per menzionare almeno l'enorme vulnerabilità con le variabili di ambiente. Non mi piace molto come una risposta canonica perché va in molte digressioni su un metodo insicuro (il wrapper setuid per gli script). Sarebbe meglio raccomandare sudo e lasciare la discussione estesa per un altro thread (l'ho coperto , a proposito).
Gilles 'SO- smetti di essere malvagio'

11

Sì, puoi, ma è probabilmente una pessima idea . Di solito, non dovresti impostare il bit SUID direttamente sul tuo eseguibile, ma usa un sudo (8) o su (1) per eseguirlo (e limitare chi può eseguirlo)

Nota, tuttavia, ci sono molti problemi di sicurezza nel consentire agli utenti regolari di eseguire programmi (e soprattutto script!) Come root. La maggior parte di loro deve farlo a meno che il programma non sia stato scritto in modo specifico e molto accurato per gestirlo, gli utenti malintenzionati potrebbero usarlo per ottenere facilmente la shell di root.

Quindi, anche se dovessi farlo, sarebbe una buona idea disinfettare prima TUTTI gli input per il programma che vuoi eseguire come root, incluso l'ambiente, i parametri della riga di comando, STDIN e tutti i file che elabora, i dati provenienti dalle connessioni di rete si apre, ecc. ecc. È estremamente difficile da fare, e ancora più difficile da fare nel modo giusto.

Ad esempio, puoi consentire agli utenti che modificano solo alcuni file con l'editor. Ma l'editor consente l'esecuzione dei comandi di helper esterni o la sospensione, dando così agli utenti la shell di root di fare ciò che desiderano. Oppure potresti eseguire script di shell che ti sembrano molto sicuri, ma l'utente può eseguirlo con $ IFS modificato o altre variabili di ambiente cambiando completamente il suo comportamento. Oppure potrebbe essere lo script PHP che dovrebbe eseguire "ls", ma l'utente lo esegue con $ PATH modificato e quindi lo fa eseguire una shell che chiama "ls". O dozzine di altri problemi di sicurezza.

Se il programma deve davvero svolgere parte del suo lavoro come root, probabilmente sarebbe meglio modificarlo in modo che funzioni come utenti normali, ma esegue (dopo aver disinfettato il più possibile) la parte che ha assolutamente bisogno di root attraverso il programma di aiuto suid.

Nota che anche se ti fidi completamente di tutti i tuoi utenti locali (come, dai loro la password di root), è una cattiva idea, dato che qualsiasi controllo involontario sulla sicurezza da parte di QUALUNQUE di loro (come, avere uno script PHP online o una password debole, o qualunque cosa) all'improvviso, l'attaccante remoto può compromettere completamente l'intera macchina.


1
distaccato. vedere l' esempio di invocazione sicura nella serie del programmatore POSIX per man sh- e anche questo non riesce a farlo command -p env -i .... Dopo tutto è abbastanza difficile da fare. Tuttavia, se fatto correttamente, per scopi espliciti, può essere meglio per suidun interprete capace che usare suo sudo- dato che il comportamento di uno dei due sarà sempre al di fuori del controllo dello script (anche se suè meno probabile che lanci palle curve mentre tende essere un po 'meno pazzo) . Raggruppare l'intero pacchetto è l'unica scommessa sicura - è meglio essere sicuri che sia tutto.
mikeserv,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.