it-swarm.com.de

Verwenden Sie das Setuid-Bit ordnungsgemäß

Ich habe einen Prozess, der Root-Rechte benötigt, wenn er von einem normalen Benutzer ausgeführt wird. Anscheinend kann ich das "Setuid-Bit" verwenden, um dies zu erreichen. Wie geht das auf einem POSIX-System richtig?

Wie kann ich dies auch mit einem Skript tun, das einen Interpreter verwendet (Bash, Perl, Python, PHP usw.)?

45
goldilocks

Das setuid-Bit kann für eine ausführbare Datei festgelegt werden, sodass das Programm beim Ausführen die Berechtigungen des Eigentümers der Datei anstelle des tatsächlichen Benutzers hat, sofern diese unterschiedlich sind. Dies ist der Unterschied zwischen effektiv uid (Benutzer-ID) und real uid.

Einige gängige Dienstprogramme, wie z. B. passwd, gehören root und werden auf diese Weise aus der Notwendigkeit heraus konfiguriert (passwd muss auf /etc/shadow Zugreifen, das nur von root gelesen werden kann).

Die beste Strategie dabei ist, sofort alles zu tun, was Sie als Superuser tun müssen, und dann die Berechtigungen zu verringern, damit Fehler oder Missbrauch beim Ausführen von root weniger wahrscheinlich sind. Dazu setzen Sie die effektiv UID des Prozesses auf die echte UID. 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;
}

Die relevanten Funktionen, die in den meisten höheren Sprachen gleichwertig sein sollten, wenn sie häufig auf POSIX-Systemen verwendet werden:

  • getuid(): Ruft die echte UID ab.
  • geteuid(): Holen Sie sich die effektive UID.
  • seteuid(): Setze die effektive UID.

Sie können nichts mit dem letzten tun, der für die echte UID unangemessen ist außer insofern, als das Setuid-Bit in der ausführbaren Datei gesetzt wurde. Um dies zu versuchen, kompilieren Sie gcc test.c -o testuid. Sie müssen dann mit Berechtigungen:

chown root testuid
chmod u+s testuid

Der letzte setzt das Setuid-Bit. Wenn Sie jetzt ./testuid Als normaler Benutzer ausführen, wird der Prozess standardmäßig mit der effektiven UID 0, root ausgeführt.

Was ist mit Skripten?

Dies variiert von Plattform zu Plattform , aber unter Linux können Dinge, die einen Interpreter erfordern, einschließlich Bytecode, das setuid-Bit nur verwenden, wenn es auf dem Interpreter gesetzt ist (was sehr, sehr dumm wäre ). Hier ist ein einfaches Perl-Skript, das den obigen C-Code nachahmt:

#!/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"; 

Perl hat spezielle Variablen für effektive UID ($>) Und echte UID ($<) Eingebaut. Wenn Sie jedoch dasselbe chown und chmod versuchen, das mit der kompilierten ausführbaren Datei (aus C, vorheriges Beispiel) verwendet wurde, macht dies keinen Unterschied. Das Skript kann keine Berechtigungen erhalten.

Die Antwort darauf besteht darin, eine setuid-Binärdatei zum Ausführen des Skripts zu verwenden:

#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);
}

Kompilieren Sie dieses gcc --std=c99 whatever.c -o perlsuid Und dann chown root perlsuid && chmod u+s perlsuid. Sie können jetzt jedes Perl-Skript mit einer effektiven UID von 0 ausführen, unabhängig davon, wem es gehört.

Eine ähnliche Strategie funktioniert mit PHP, Python usw. Aber ...

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

BITTE BITTE Lass so etwas NICHT herumliegen. Höchstwahrscheinlich möchten Sie tatsächlich im Namen kompilieren des Skripts als absoluter Pfad , dh ersetzen Sie den gesamten Code in main() durch:

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

Sie stellen sicher, dass /opt/suid_scripts Und alles darin für Nicht-Root-Benutzer schreibgeschützt sind. Andernfalls könnte jemand etwas gegen whatever.pl Eintauschen.

Beachten Sie außerdem, dass viele Skriptsprachen es Umgebungsvariablen ermöglichen, die Art und Weise zu ändern, in der sie ein Skript ausführen . Beispielsweise kann eine Umgebungsvariable dazu führen, dass eine vom Aufrufer bereitgestellte Bibliothek geladen wird, sodass der Aufrufer beliebigen Code als Root ausführen kann. Wenn Sie also nicht wissen, dass sowohl der Interpreter als auch das Skript selbst gegenüber allen möglichen Umgebungsvariablen robust sind, tun Sie dies NICHT .

Was soll ich stattdessen tun?

Eine sicherere Möglichkeit, einem Nicht-Root-Benutzer das Ausführen eines Skripts als Root zu ermöglichen, besteht darin, eine Sudo -Regel hinzuzufügen und den Benutzer Sudo /path/to/script Ausführen zu lassen. Sudo entfernt die meisten Umgebungsvariablen und ermöglicht es dem Administrator, genau auszuwählen, wer den Befehl mit welchen Argumenten ausführen kann. Ein Beispiel finden Sie unter Ausführen eines bestimmten Programms als Root ohne Kennwortabfrage? .

54
goldilocks

Ja, das kannst du, aber es ist wahrscheinlich eine sehr schlechte Idee . Normalerweise würden Sie das SUID-Bit nicht direkt in Ihrer ausführbaren Datei setzen, sondern ein Sudo (8) oder su (1) verwenden, um es auszuführen (und begrenzen, wer es ausführen kann).

Beachten Sie jedoch, dass es viele Sicherheitsprobleme gibt, wenn reguläre Benutzer Programme (und insbesondere Skripte!) Als Root ausführen können. Die meisten von ihnen müssen dies tun, es sei denn, das Programm ist speziell und sehr, sehr sorgfältig geschrieben, um damit umzugehen. Böswillige Benutzer könnten es verwenden, um die Root-Shell leicht zu erhalten.

Selbst wenn Sie dies tun würden, wäre es eine sehr gute Idee, zuerst ALLE Eingaben in das Programm zu bereinigen, das Sie als Root ausführen möchten, einschließlich seiner Umgebung, Befehlszeilenparameter, STDIN und aller von ihm verarbeiteten Dateien sowie Daten, die von Netzwerkverbindungen stammen es öffnet sich usw. usw. Es ist extrem schwer zu tun und noch schwieriger, es richtig zu machen.

Beispielsweise können Sie Benutzern erlauben, die nur einige Dateien mit dem Editor bearbeiten. Der Editor ermöglicht jedoch die Befehlsausführung externer Helfer oder das Anhalten, sodass Benutzer die Root-Shell ausführen können, was sie möchten. Oder Sie führen möglicherweise Shell-Skripte aus, die für Sie sehr sicherheitsrelevant aussehen, aber der Benutzer kann sie mit modifizierten $ IFS- oder anderen Umgebungsvariablen ausführen, die das Verhalten vollständig ändern. Oder es könnte PHP Skript sein, das "ls" ausführen soll, aber der Benutzer führt es mit geändertem $ PATH aus und lässt es daher eine Shell ausführen, die er "ls" nennt. Oder Dutzende anderer Sicherheitsvorkehrungen Probleme.

Wenn das Programm wirklich einen Teil seiner Arbeit als Root ausführen muss, ist es wahrscheinlich am besten, es so zu ändern, dass es als normale Benutzer ausgeführt wird. Führen Sie jedoch einfach (nachdem Sie so viel wie möglich bereinigt haben) den Teil aus, der unbedingt Root benötigt, über das suid-Hilfsprogramm.

Beachten Sie, dass selbst wenn Sie allen lokalen Benutzern vollständig vertrauen (z. B. geben Sie ihnen das Root-Passwort), dies eine schlechte Idee ist, da JEDER von ihnen eine unbeabsichtigte Sicherheitsüberwachung durchführt (z. B. ein PHP) = Skript online oder schwaches Passwort oder was auch immer) ermöglicht es einem Angreifer plötzlich, den gesamten Computer vollständig zu gefährden.

11
Matija Nalis

Ich habe diese Art von Lösung.

  1. Stellen Sie eine C-Hülle her, die für die Shebang-Linie vorgesehen ist. Setuid es root.
  2. Unmittelbar nach dem Start prüft der Wrapper, ob das als Argument angegebene Skript setuid/setgid ist.
    • wenn nein - root löschen priv.
    • wenn ja - Ändern Sie die effektive UID/GID, um die Setuid/Setgid-Bits wiederzugeben.
  3. Überprüfen Sie, ob das angegebene Skript eine reguläre Datei ist. Wenn nicht, beenden Sie das Programm.
  4. exec Perl über "Perl -x-Skript".

Eine strengere/eingeschränktere Version könnte dies auch tun (und/oder):

  • verbieten, Skripte auszuführen, die setuid/setgid root sind,
  • führen Sie Perl im fehlerhaften Modus aus ("Perl -x -T-Skript").

Eine noch fortgeschrittenere Version könnte Perl über Sudo starten ("exec Sudo ... Perl -x script").

0
user404221