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. Was ist der richtige Weg, dies auf einem POSIX-System zu tun?
Wie kann ich dies auch mit einem Skript tun, das einen Interpreter verwendet (bash, perl, python, php usw.)?
Akzeptierte Antwort:
Das setuid-Bit kann für eine ausführbare Datei gesetzt werden, sodass das Programm bei der Ausführung die Rechte des Eigentümers der Datei hat und nicht die des echten Benutzers, falls diese unterschiedlich sind. Das ist der Unterschied zwischen effektiv uid (Benutzer-ID) und real uid.
Einige gängige Dienstprogramme wie passwd
, gehören root und sind notgedrungen so konfiguriert (passwd
muss auf /etc/shadow
zugreifen die nur von root gelesen werden kann).
Die beste Strategie, wenn Sie dies tun, besteht darin, alles, was Sie tun müssen, sofort als Superuser zu tun und dann die Berechtigungen zu verringern, damit Fehler oder Missbrauch beim Ausführen von root weniger wahrscheinlich sind. Dazu legen Sie die wirksame des Prozesses fest uid zu seiner realen 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: %dn",
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 eine Entsprechung haben sollten, wenn sie auf POSIX-Systemen allgemein verwendet werden:
getuid()
:Holen Sie sich das Echte uid.geteuid()
:Holen Sie sich das Effektive uid.seteuid()
:Legen Sie die effektive fest uid.
Sie können nichts mit dem letzten machen, das für die echte uid unpassend ist, außer insofern, als das setuid-Bit auf der ausführbaren Datei gesetzt wurde . Um dies zu versuchen, kompilieren Sie gcc test.c -o testuid
. Sie müssen dann mit Privilegien:
chown root testuid
chmod u+s testuid
Der letzte setzt das setuid-Bit. Wenn Sie jetzt ./testuid
ausführen Als normaler Benutzer sehen Sie, dass der Prozess standardmäßig mit der effektiven UID 0, root, ausgeführt wird.
Was ist mit Skripten?
Dies ist von Plattform zu Plattform unterschiedlich, aber unter Linux können Dinge, die einen Interpreter erfordern, einschließlich Bytecode, das setuid-Bit nicht verwenden, es sei denn, es ist im Interpreter gesetzt (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";
Getreu seinen *nixy-Wurzeln hat Perl spezielle Variablen für eine effektive UID eingebaut ($>
) und echte uid ($<
). Aber wenn Sie dasselbe versuchen chown
und chmod
mit der kompilierten (von C, vorheriges Beispiel) ausführbaren Datei verwendet wird, wird es keinen Unterschied machen. Das Skript kann keine Berechtigungen erhalten.
Die Antwort darauf ist, eine Setuid-Binärdatei zu verwenden, um das Skript auszuführen:
#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
, dann chown root perlsuid && chmod u+s perlsuid
. Sie können jetzt beliebige ausführen Perl-Skript mit einer effektiven UID von 0, 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 Lassen Sie so etwas NICHT herumliegen . Höchstwahrscheinlich möchten Sie den Namen des Skripts tatsächlich als absoluten Pfad kompilieren , d. h. den gesamten Code in main()
ersetzen mit:
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Stellen Sie sicher, dass /opt/suid_scripts
und alles darin ist für Nicht-Root-Benutzer schreibgeschützt. Andernfalls könnte jemand alles für whatever.pl
eintauschen .
Beachten Sie außerdem, dass viele Skriptsprachen zulassen, dass Umgebungsvariablen die Art und Weise ändern, wie sie ein Skript ausführen . Beispielsweise kann eine Umgebungsvariable bewirken, 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 robust gegenüber allen möglichen Umgebungsvariablen sind, TUN SIE DIES NICHT .
Was soll ich stattdessen tun?
Eine sicherere Möglichkeit, einem Nicht-Root-Benutzer zu erlauben, ein Skript als Root auszuführen, 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 dem Administrator außerdem, genau auszuwählen, wer den Befehl ausführen kann und mit welchen Argumenten. Siehe Wie führe ich ein bestimmtes Programm als root ohne Passwortabfrage aus? für ein Beispiel.