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 der effektiven UID (Benutzer-ID) und der realen UID.
Einige gängige Dienstprogramme, wie z. B. passwd
, sind im Besitz von root und werden so konfiguriert, dass sie nicht passwd
benötigt werden ( Zugriff /etc/shadow
nur für root möglich).
Die beste Strategie dabei ist, sofort das 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 effektive Benutzer-ID des Prozesses auf die tatsächliche Benutzer-ID. 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 auf POSIX-Systemen häufig verwendet werden:
getuid()
: Holen Sie sich die echte UID.
geteuid()
: Holen Sie sich die effektive UID.
seteuid()
: Stellen Sie die effektive UID ein.
Sie können nichts mit dem letzten tun, das für die echte UID unangemessen ist, außer insofern, als das Setuid-Bit für die ausführbare 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
als normaler Benutzer ausgeführt werden, wird der Prozess standardmäßig mit der effektiven UID 0 root ausgeführt.
Was ist mit Skripten?
Dies ist von Plattform zu Plattform unterschiedlich , aber unter Linux können Dinge, die einen Interpreter, einschließlich Bytecode, erfordern, 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";
Getreu seinen * nixy-Wurzeln hat Perl spezielle Variablen für effektive uid ( $>
) und echte uid ( $<
) eingebaut . Wenn Sie jedoch dasselbe versuchen chown
und chmod
es mit der kompilierten (aus C, vorheriges Beispiel) ausführbaren Datei verwenden, macht es keinen Unterschied. Das Skript kann keine Berechtigungen erhalten.
Die Antwort auf diese Frage 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 dies gcc --std=c99 whatever.c -o perlsuid
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 sowas NICHT herumliegen . Höchstwahrscheinlich möchten Sie den Namen des Skripts tatsächlich als absoluten Pfad kompilieren , dh den gesamten Code in main()
durch Folgendes ersetzen :
const char *args[] = { "perl", "/opt/suid_scripts/whatever.pl" }
return execv("/usr/bin/perl", (char * const*)args);
Sie stellen sicher, dass alle /opt/suid_scripts
Inhalte für Benutzer ohne Rootberechtigung schreibgeschützt sind. Andernfalls könnte jemand etwas gegen etwas eintauschen whatever.pl
.
Beachten Sie außerdem, dass in vielen Skriptsprachen Umgebungsvariablen die Art und Weise ändern können, 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 DIESES NICHT .
Also, was soll ich stattdessen tun?
Eine sicherere Möglichkeit, einem Benutzer ohne Rootberechtigung die Ausführung eines Skripts als Rootberechtigung zu ermöglichen, besteht darin, eine sudo- Regel hinzuzufügen und den Benutzer ausführen zu lassen sudo /path/to/script
. Sudo entfernt die meisten Umgebungsvariablen und ermöglicht es dem Administrator, genau auszuwählen, wer den Befehl mit welchen Argumenten ausführen darf. Siehe So führen Sie ein bestimmtes Programm als root ohne Kennworteingabeaufforderung aus zum Beispiel.
-privileged
Shell aufgerufensuid root
werden. Wenn sie von einem verantwortlichen Skript aufgerufen werden, können sie auch sicher auf diese Weise aufgerufen werden. Ich befürchte jedoch, dass die Vorstellung eines verantwortungsvollen Skripts in der realen Welt ein bisschen wie ein Wunschtraum ist. Wie auch immer, es ist wahrscheinlich erwähnenswert, wie wichtig es ist, Schreibzugriffe jeglicher Art zu vermeiden, wenn dies bei erhöhten Berechtigungen möglich ist ...