2014-12-12 21 views
8

Entrambe system() e execve() possono essere utilizzate per eseguire un altro comando all'interno di un programma. Perché nei programmi set-UID, system() è pericoloso, mentre execve() è sicuro?system() vs execve()

risposta

9

system() e execve() funzionano in modi diversi. system() invocherà sempre la shell e questa shell eseguirà il comando come processo separato (questo è il motivo per cui è possibile utilizzare i caratteri jolly e altre funzionalità della shell nella riga di comando quando si utilizza system()).

execve() (e le altre funzioni della famiglia exec()) sostituisce il processo corrente con quello essendo generato direttamente (la funzione execve() non restituisce, salvo in caso di guasto). Infatti l'implementazione system() deve utilizzare una sequenza di chiamate fork(), execve() e wait() per eseguire la sua funzione.

Naturalmente entrambi sono pericolosi a seconda di ciò che viene eseguito quando il processo ha i privilegi di root. system(), tuttavia, comporta alcuni pericoli aggiuntivi a causa del "livello" di shell aggiuntivo che utilizza le violazioni di sicurezza di una stanza aperta quando invoca una shell di root come nel caso della domanda (ad esempio, il processo ha il bit suid).

+0

Quindi, quando si usa execve() .. si cita che sostituisce il processo corrente .. il processo resterà impostato? – Jake

+0

Sì.Il "nuovo" processo avviato da execve eredita un numero di proprietà di quello che viene sostituito, come fileedescriptors, socket, ecc. E l'uid effettivo è uno di questi, ma ci sono situazioni in cui l'uid cambia durante l'esecuzione di execve, come se l'eseguibile indicato dal parametro execve ha il bit suid impostato. In questo caso, l'uid viene modificato nel proprietario del file come definito nel filesystem. – Marcelo

12

system chiamerà la shell (sh) per eseguire il comando inviato come argomento. Il problema con system perché il comportamento della shell dipende dall'utente che esegue il comando. Un piccolo esempio:

Creazione di un file test.c:

#include <stdio.h> 

int main(void) { 
    if (system ("ls") != 0) 
     printf("Error!"); 
    return 0; 
} 

Poi:

$ gcc test.c -o test 

$ sudo chown root:root test 

$ sudo chmod +s test 

$ ls -l test 
-rwsr-sr-x 1 root root 6900 Dec 12 17:53 test 

Creazione di uno script chiamato ls nella directory corrente:

$ cat > ls 
#!/bin/sh 

/bin/sh 

$ chmod +x ls 

Ora:

$ PATH=. ./test 
# /usr/bin/id 
uid=1000(cuonglm) gid=1000(cuonglm) euid=0(root) egid=0(root) groups=0(root), 
24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),105(scanner), 
110(bluetooth),111(netdev),999(docker),1000(cuonglm) 
# /usr/bin/whoami 
root 

Oops, hai una shell con i privilegi di root.

execve non chiama shell. Esegue il programma che gli è passato come primo argomento. Il programma deve essere un eseguibile binario o un inizio di script con la riga shebang.

+0

Il fatto di non dire 'system()' è senza problemi ma non si risolverà quanto sopra utilizzando i percorsi assoluti nell'eseguibile binario? – Bratchley

+1

@JoelDavis, no, avresti almeno bisogno di cancellare l'intero ambiente, dare valori predefiniti sani ad alcuni envvars (PATH, HOME ...), se necessario preservare alcuni oggetti dopo la sanificazione (TERM, DISPLAY, LANG. ..) assicurati che i fds 0, 1, 2 siano aperti ... Fondamentalmente fai ciò che fa sudo. Anche allora, non ci andrei. Non invocare una shell nel contesto di escalation dei privilegi se ciò può essere evitato. Nota che 'ls' può fare cose di fantasia con il suo ambiente, quindi anche senza' system() ', dovresti probabilmente disinfettare l'ambiente. Quando si usano i setuids, si vuole minimizzare ciò che viene fatto come root (tipicamente non si eseguono i comandi). –

+3

@JoelDavis: No, hai ancora problemi, anche se utilizzi il percorso completo. Se si utilizza '/ bin/ls', l'utente può aggiungere'/'a' $ IFS', causando la divisione della shell '/ bin/ls' in' bin' e 'ls'. Ora, un eseguibile chiamato 'bin' nella directory corrente può fare la stessa cosa di' ls' nella mia risposta. – cuonglm

0

Oltre ai problemi di sicurezza indicati con system(), il processo generato eredita l'ambiente del programma principale. Questo può essere molto problematico quando si utilizza suid, ad esempio quando il processo chiamante imposta LD_LIBRARY_PATH - variabile ambientale.

Con il exec() -famiglia il programma chiamante può impostare l'ambiente esattamente su ciò che è necessario (e sicuro) per il programma chiamato prima di chiamare exec().

E ovviamente la shell chiamata da system() può avere problemi di sicurezza.