Die setuid
Permission-Bit weist Linux an, ein Programm mit der effektiven Benutzer-ID des Eigentümers anstelle des Ausführenden auszuführen:
> cat setuid-test.c
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv) {
printf("%d", geteuid());
return 0;
}
> gcc -o setuid-test setuid-test.c
> ./setuid-test
1000
> sudo chown nobody ./setuid-test; sudo chmod +s ./setuid-test
> ./setuid-test
65534
Dies gilt jedoch nur für ausführbare Dateien; Shell-Skripte ignorieren das setuid-Bit:
> cat setuid-test2
#!/bin/bash
id -u
> ./setuid-test2
1000
> sudo chown nobody ./setuid-test2; sudo chmod +s ./setuid-test2
> ./setuid-test2
1000
Wikipedia sagt:
Aufgrund der erhöhten Wahrscheinlichkeit von Sicherheitslücken ignorieren viele Betriebssysteme das setuid-Attribut, wenn es auf ausführbare Shell-Skripte angewendet wird.
Angenommen, ich bin bereit, diese Risiken zu akzeptieren, gibt es eine Möglichkeit, Linux anzuweisen, das setuid-Bit in Shell-Skripten genauso zu behandeln wie in ausführbaren Dateien?
Wenn nicht, gibt es eine allgemeine Problemumgehung für dieses Problem? Meine aktuelle Lösung besteht darin, ein sudoers
hinzuzufügen Eintrag, um ALL
zuzulassen mit NOPASSWD
ein bestimmtes Skript unter dem Benutzer ausführen, unter dem es ausgeführt werden soll um die Passwortabfrage zu vermeiden. Der größte Nachteil dabei ist die Notwendigkeit eines sudoers
Eintrag jedes Mal, wenn ich dies tun möchte, und die Notwendigkeit für den Aufrufer, sudo some-script
auszuführen statt nur some-script
Akzeptierte Antwort:
Linux ignoriert das setuid¹-Bit bei allen interpretierten ausführbaren Dateien (d. h. ausführbaren Dateien, die mit einem #!
beginnen Linie). Die häufig gestellten Fragen zu comp.unix.questions erklären die Sicherheitsprobleme mit setuid-Shell-Skripten. Diese Probleme sind von zweierlei Art:Shebang-bezogen und Schalen-bezogen; Auf weitere Details gehe ich weiter unten ein.
Wenn Sie sich nicht um Sicherheit kümmern und setuid-Skripte zulassen möchten, müssen Sie unter Linux den Kernel patchen. Ab 3.x-Kerneln müssen Sie meiner Meinung nach einen Aufruf zu install_exec_creds
hinzufügen im load_script
Funktion, vor dem Aufruf von open_exec
, aber ich habe es nicht getestet.
Setuid Kram
Der Art und Weise, wie Shebang (#!
) wird typischerweise implementiert:
- Der Kernel öffnet die ausführbare Datei und stellt fest, dass sie mit
#!
beginnt . - Der Kernel schließt die ausführbare Datei und öffnet stattdessen den Interpreter.
- Der Kernel fügt den Pfad zum Skript in die Argumentliste ein (als
argv[1]
) und führt den Interpreter aus.
Wenn setuid-Skripte mit dieser Implementierung erlaubt sind, kann ein Angreifer ein beliebiges Skript aufrufen, indem er einen symbolischen Link zu einem bestehenden setuid-Skript erstellt, es ausführt und den Link ändert, nachdem der Kernel Schritt 1 ausgeführt hat und bevor der Interpreter dazu kommt Eröffnung seines ersten Arguments. Aus diesem Grund ignorieren die meisten Unices das setuid-Bit wenn sie einen Kram entdecken.
Eine Möglichkeit, diese Implementierung zu sichern, wäre, dass der Kernel die Skriptdatei sperrt, bis der Interpreter sie geöffnet hat (beachten Sie, dass dies nicht nur das Aufheben der Verknüpfung oder das Überschreiben der Datei, sondern auch das Umbenennen eines Verzeichnisses im Pfad verhindern muss). Aber Unix-Systeme neigen dazu, sich vor obligatorischen Sperren zu scheuen, und symbolische Links würden eine korrekte Sperrfunktion besonders schwierig und invasiv machen. Ich glaube nicht, dass das irgendjemand so macht.
Einige wenige Unix-Systeme (hauptsächlich OpenBSD, NetBSD und Mac OS X, die alle eine aktivierte Kernel-Einstellung erfordern) implementieren sicheres setuid-Shebang mit einem zusätzlichen Feature:dem Pfad /dev/fd/N
bezieht sich auf die bereits geöffnete Datei im Dateideskriptor N (Also öffnen Sie /dev/fd/N
entspricht in etwa dup(N)
). Viele Unix-Systeme (einschließlich Linux) haben /dev/fd
aber keine setuid-Skripte.
- Der Kernel öffnet die ausführbare Datei und stellt fest, dass sie mit
#!
beginnt . Nehmen wir an, der Dateideskriptor für die ausführbare Datei ist 3. - Der Kernel öffnet den Interpreter.
- Der Kernel fügt
/dev/fd/3
ein die Argumentliste (alsargv[1]
) und führt den Interpreter aus.
Die Shebang-Seite von Sven Mascheck hat eine Menge Informationen über Shebang über Unices hinweg, einschließlich Setuid-Unterstützung.
Setuid-Interpreter
Nehmen wir an, Sie haben es geschafft, Ihr Programm als root auszuführen, entweder weil Ihr Betriebssystem setuid shebang unterstützt oder weil Sie einen nativen binären Wrapper (wie sudo
) verwendet haben ). Haben Sie eine Sicherheitslücke geöffnet? Vielleicht . Das Problem hier ist nicht über interpretierte vs. kompilierte Programme. Das Problem ist, ob Ihr Laufzeitsystem verhält sich sicher, wenn es mit Privilegien ausgeführt wird.
-
Jede dynamisch verknüpfte native ausführbare Binärdatei wird in gewisser Weise vom dynamischen Ladeprogramm interpretiert (zB
/lib/ld.so
), die die vom Programm benötigten dynamischen Bibliotheken lädt. Auf vielen Unices können Sie den Suchpfad für dynamische Bibliotheken über die Umgebung konfigurieren (LD_LIBRARY_PATH
ist ein allgemeiner Name für die Umgebungsvariable) und lädt sogar zusätzliche Bibliotheken in alle ausgeführten Binärdateien (LD_PRELOAD
). Der Aufrufer des Programms kann beliebigen Code im Kontext dieses Programms ausführen, indem er eine speziell gestaltetelibc.so
platziert in$LD_LIBRARY_PATH
(neben anderen Taktiken). Alle vernünftigen Systeme ignorieren denLD_*
Variablen in ausführbaren setuid-Dateien. -
In Schalen wie sh, csh und Derivate werden Umgebungsvariablen automatisch zu Shell-Parametern. Durch Parameter wie
PATH
,IFS
, und viele mehr, hat der Aufrufer des Skripts viele Möglichkeiten, beliebigen Code im Kontext des Shell-Skripts auszuführen. Einige Shells setzen diese Variablen auf vernünftige Standardwerte, wenn sie feststellen, dass das Skript mit Privilegien aufgerufen wurde, aber ich weiß nicht, ob es eine bestimmte Implementierung gibt, der ich vertrauen würde. -
Die meisten Laufzeitumgebungen (egal ob nativ, Bytecode oder interpretiert) haben ähnliche Eigenschaften. Nur wenige treffen besondere Vorsichtsmaßnahmen in ausführbaren setuid-Dateien, obwohl diejenigen, die nativen Code ausführen, oft nichts Ausgefalleneres tun als dynamisches Linken (was Vorsichtsmaßnahmen trifft).
-
Perl ist eine bemerkenswerte Ausnahme. Es unterstützt ausdrücklich setuid-Skripte auf sichere Weise. Tatsächlich kann Ihr Skript setuid ausführen, selbst wenn Ihr Betriebssystem das setuid-Bit in Skripten ignoriert. Das liegt daran, dass Perl mit einem Setuid-Root-Hilfsprogramm ausgeliefert wird, das die notwendigen Prüfungen durchführt und den Interpreter für die gewünschten Skripte mit den gewünschten Privilegien erneut aufruft. Dies wird im perlsec-Handbuch erklärt. Früher benötigten Setuid-Perl-Skripte
#!/usr/bin/suidperl -wT
statt#!/usr/bin/perl -wT
, aber auf den meisten modernen Systemen#!/usr/bin/perl -wT
ist ausreichend.
Beachten Sie, dass die Verwendung eines nativen binären Wrappers nichts tut, um diese Probleme zu verhindern . Tatsächlich kann es die Situation schlimmer machen , weil es Ihre Laufzeitumgebung daran hindern könnte, zu erkennen, dass es mit Privilegien aufgerufen wird, und seine Laufzeitkonfigurierbarkeit zu umgehen.
Ein nativer Binär-Wrapper kann ein Shell-Skript sicher machen, wenn der Wrapper die Umgebung bereinigt . Das Skript muss darauf achten, nicht zu viele Annahmen zu treffen (z. B. über das aktuelle Verzeichnis), aber das geht. Sie können dafür sudo verwenden, vorausgesetzt, es ist so eingerichtet, dass es die Umgebung bereinigt. Das Blacklisting von Variablen ist fehleranfällig, also immer Whitelisting. Stellen Sie mit sudo sicher, dass die Datei env_reset
Option eingeschaltet ist, dass setenv
ausgeschaltet ist, und diese env_file
und env_keep
nur harmlose Variablen enthalten.
TL,DR:
- Setuid shebang ist unsicher, wird aber normalerweise ignoriert.
- Wenn Sie ein Programm mit Privilegien ausführen (entweder über sudo oder setuid), schreiben Sie nativen Code oder Perl oder starten Sie das Programm mit einem Wrapper, der die Umgebung bereinigt (z. B. sudo mit dem
env_reset
Option).
¹ Diese Diskussion gilt gleichermaßen, wenn Sie „setgid“ durch „setuid“ ersetzen; sie werden beide vom Linux-Kernel in Skripten ignoriert