2014-12-12 24 views
8

Sowohl system() als auch execve() können verwendet werden, um einen anderen Befehl innerhalb eines Programms auszuführen. Warum in UID-Programmen ist system() gefährlich, während execve() sicher ist?system() vs execve()

Antwort

9

system() und execve() arbeiten auf verschiedene Arten. system() ruft immer die Shell auf und diese Shell führt den Befehl als separaten Prozess aus (deshalb können Sie Platzhalter und andere Shell-Funktionen in der Befehlszeile verwenden, wenn Sie system() verwenden).

execve() (und die anderen Funktionen in der exec() Familie) ersetzt den aktuellen Prozess durch den direkt erzeugten (die execve() Funktion wird nicht zurückgegeben, außer im Falle eines Fehlers). In der Tat system() Implementierung soll eine Sequenz von fork(), execve() und wait() Aufrufe verwenden, um seine Funktion zu erfüllen.

Natürlich sind beide gefährlich, abhängig davon, was ausgeführt wird, wenn der Prozess Root-Rechte hat. system(), bringt jedoch einige zusätzliche Gefahren aufgrund der zusätzlichen Shell "Schicht" verwendet, die Raum Sicherheitsverletzungen öffnet, wie es eine Root-Shell wie im Fall Ihrer Frage aufruft (d. H. Der Prozess hat das suid-Bit).

+0

Also wenn Sie execve() verwenden .. Sie erwähnen es ersetzt den aktuellen Prozess .. würde der Prozess setuid bleiben? – Jake

+0

Ja.Der von execve initiierte "neue" Prozess erbt eine Reihe von Eigenschaften des Ersetzten, wie Dateideskriptoren, Sockets, etc. und die effektive uid ist eine davon, aber es gibt Situationen, in denen sich die uid während der Ausführung von execve ändert, wie Wenn in der ausführbaren Datei, auf die der Parameter execve zeigt, das suid-Bit gesetzt ist. In diesem Fall wird die UID in den Dateieigner geändert, wie er im Dateisystem definiert ist. – Marcelo

12

system ruft die Shell (sh) auf, um den als Argument gesendeten Befehl auszuführen. Das Problem mit system, da das Shell-Verhalten von dem Benutzer abhängt, der den Befehl ausführt. Ein kleines Beispiel:

Erstellen einer Datei test.c:

#include <stdio.h> 

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

Dann:

$ cat > ls 
#!/bin/sh 

/bin/sh 

$ chmod +x ls 

Jetzt:

$ 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 

ein Skript ls im aktuellen Verzeichnis namens Erstellen

$ 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 

Hoppla, Sie haben eine Shell mit root-Rechten.

execve ruft keine Shell auf. Es führt das als erstes Argument übergebene Programm aus. Das Programm muss eine binäre ausführbare Datei oder ein Skriptstart mit shebang Zeile sein.

+0

Nicht zu sagen, 'system()' ist ohne Probleme, aber würde nicht das oben genannte durch die Verwendung absoluter Pfade in der binären ausführbaren Datei gelöst werden? – Bratchley

+1

@JoelDavis, nein, Sie müssten mindestens die gesamte Umgebung löschen, einigen envvars vernünftige Standardwerte zuweisen (PATH, HOME ...), falls erforderlich, einige env vars nach der Bereinigung beibehalten (TERM, DISPLAY, LANG. ..) stelle sicher, dass die fds 0, 1, 2 offen sind ... Im Grunde mach was sudo. Selbst dann würde ich nicht dorthin gehen. Rufen Sie nicht eine Shell im Kontext der Rechteerweiterung auf, wenn dies vermieden werden kann. Beachten Sie, dass "ls" mit seiner Umgebung tolle Dinge tun kann, also sollten Sie auch ohne 'system()' die Umgebung bereinigen. Wenn Sie setuids verwenden, möchten Sie minimieren, was als root ausgeführt wird (normalerweise keine Befehle ausführen). –

+3

@JoelDavis: Nein, Sie haben immer noch ein Problem, auch wenn Sie den vollständigen Pfad verwenden. Wenn Sie '/ bin/ls' verwenden, kann der Benutzer'/'zu' $ IFS' hinzufügen, was die Shell-Aufteilung '/ bin/ls' in' bin' und 'ls' verursacht. Jetzt kann eine ausführbare Datei namens "bin" im aktuellen Verzeichnis das gleiche wie "ls" in meiner Antwort tun. – cuonglm

0

Abgesehen von den genannten Sicherheitsproblemen mit system() erbt der erzeugte Prozess die Umgebung des Hauptprogramms. Dies kann sehr problematisch sein, wenn Sie suid verwenden, beispielsweise wenn der aufrufende Prozess LD_LIBRARY_PATH -Umgebungsvariable setzt.

Mit der exec() -Familie kann das aufrufende Programm die Umgebung auf genau das einstellen, was für das aufgerufene Programm benötigt wird (und sicher ist), bevor es exec() aufruft.

Und natürlich kann die Shell, die von system() aufgerufen wird, Sicherheitsprobleme selbst haben.