Fehler beim Ablegen/Senken von Privilegien
Beschreibung
Fehler beim Ablegen/Senken von Privilegien ist eine Schwachstellenklasse, die auftritt, wenn ein Produkt Privilegien nicht reduziert, bevor es die Kontrolle über eine Ressource an einen Akteur überträgt, der diese erhöhten Berechtigungen nicht besitzt. Wenn ein System mit erhöhten Berechtigungen Prozesse, Dateien oder andere Ressourcen an einen anderen Prozess oder Benutzer übergibt, ohne vorher seine erhöhten Privilegien abzulegen, verbreiten sich diese erhöhten Privilegien im gesamten System. Dies erzeugt Anfälligkeit für Privilegieneskalationsangriffe, da der empfangende Akteur Zugang zu Fähigkeiten erhält, die weit über das Beabsichtigte hinausgehen.
Risiko
Das Versäumnis, Privilegien vor der Ressourcenübergabe ordnungsgemäß abzulegen, erzeugt schwere Sicherheitsrisiken, indem es Privilegieneskalation über Vertrauensgrenzen hinweg ermöglicht. Wenn Setuid-Programme, Daemons oder Dienste erhöhte Privilegien länger als nötig behalten, kann jede Schwachstelle im Code (wie Buffer Overflows, Command Injection oder Path Traversal) mit diesen erhöhten Privilegien ausgenutzt werden. Das Risiko ist besonders akut in Unix/Linux-Systemen, wo Root-Privilegien vollständigen Systemzugang bieten. Kindprozesse, die erhöhte Privilegien erben, können ausgenutzt werden, um das gesamte System zu kompromittieren. Zusätzlich können Ressourcen, die mit erhöhten Privilegien erstellt wurden (Dateien, Netzwerkverbindungen, Shared Memory), falsche Berechtigungen haben, was unbefugten Zugriff ermöglicht, selbst nachdem der erstellende Prozess beendet wurde.
Lösung
Implementieren Sie Privilegientrennung durch Systemkompartimentierung mit klaren Vertrauensgrenzen. Legen Sie Privilegien sofort nach Abschluss von Operationen ab, die sie erfordern, gemäß dem Prinzip der geringsten Privilegien. Verwenden Sie erhöhte Berechtigungen nur wenn nötig, geben Sie sie dann auf, bevor Sie Operationen ausführen, die von nicht vertrauenswürdigem Input beeinflusst werden könnten. Beim Implementieren der Privilegienablegung adressieren Sie alle Aspekte: Benutzer-ID, Gruppen-ID, zusätzliche Gruppen, Capabilities und alle prozess-vererbbaren Attribute. Verifizieren Sie, dass Privilegienablegungsoperationen erfolgreich waren, indem Sie Rückgabewerte prüfen und den neuen Privilegienzustand bestätigen. Schließen oder schützen Sie ordnungsgemäß alle Ressourcen (Dateihandles, Sockets, Shared Memory), die mit erhöhten Privilegien erstellt wurden, bevor Sie zu niedrigeren Privilegienstufen wechseln. Entwerfen Sie Privilegientrennungs-Architekturen, bei denen nur minimaler Code mit erhöhten Privilegien läuft und der Großteil der Anwendungslogik unprivilegiert läuft.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Bereich: Zugriffskontrolle Beibehaltene Privilegien verhindern, dass Zugriffsrechte ordnungsgemäß eingeschränkt werden. Akteure, die begrenzte Berechtigungen haben sollten, operieren mit erhöhtem Zugang, was unbefugten Ressourcenzugriff, Systemmodifikation und Privilegieneskalation durch jede nachfolgende Schwachstelle ermöglicht. |
| Nicht-Abstreitbarkeit | Bereich: Zugriffskontrolle, Nicht-Abstreitbarkeit Das System kann Aktionen dem imitierten Benutzer zuschreiben anstatt dem tatsächlichen Akteur, was Audit-Trails und Verantwortlichkeit kompromittiert. Aktionen, die mit geerbten Privilegien ausgeführt werden, können unter falschen Identitäten protokolliert werden. |
Beispielcode
Anfälliger Code (C)
Die folgenden Beispiele demonstrieren Fehler beim Ablegen von Privilegien:
// Anfällig: Läuft weiter als root nach chroot
#include <stdio.h>
#include <unistd.h>
int vulnerable_chroot_service(char *app_home, char *filename) {
// Dateisystem-Sicht einschränken
chroot(app_home);
chdir("/");
// Anfällig: Läuft immer noch als root!
// Jede Schwachstelle hier gibt dem Angreifer Root-Zugang
FILE* data = fopen(filename, "r+");
if (data != NULL) {
process_file(data); // Buffer Overflow hier = Root-Kompromittierung
fclose(data);
}
return 0;
}
// Anfällig: Setuid-Programm legt Privilegien vor exec nicht ab
int vulnerable_exec_program(char *program_path) {
// Läuft als root wegen setuid-Bit
// Anfällig: Führt externes Programm mit Root-Privilegien aus
// Der PATH oder das Programm könnte von Angreifer kontrolliert werden
char *env_program = getenv("HELPER_PROGRAM");
if (env_program != NULL) {
execve(env_program, NULL, NULL); // Läuft als root!
}
return 0;
}
// Anfällig: Legt Gruppenprivilegien nicht ab
int vulnerable_partial_drop(uid_t target_uid) {
// Benutzerprivileg ablegen
setuid(target_uid);
// Anfällig: Zusätzliche Gruppen nicht abgelegt!
// Prozess ist möglicherweise immer noch Mitglied privilegierter Gruppen
// Anfällig: Ursprüngliche Gruppen-ID nicht geändert!
// getgid() gibt immer noch Root-Gruppe zurück
do_sensitive_operation();
return 0;
}
# Anfällig: Python-Dienst behält Root-Privilegien
import os
import subprocess
class VulnerableService:
def start_worker(self, task_config):
# Hauptprozess läuft als root
pid = os.fork()
if pid == 0:
# Kindprozess - sollte unprivilegierter Worker sein
# Anfällig: Keine Privilegienablegung!
# Worker läuft mit Root-Privilegien
# Benutzer-kontrollierte Konfiguration
script_path = task_config.get('script')
subprocess.run(['/bin/bash', script_path]) # Läuft als root!
def create_log_file(self, log_path):
# Anfällig: Erstellt Datei als root, legt dann Privilegien ab
# Datei gehört jetzt root mit root-Berechtigungen
with open(log_path, 'w') as f:
f.write("Log gestartet\n")
# Jetzt Privilegien ablegen
os.setuid(unprivileged_uid)
# Anfällig: Log-Datei gehört immer noch root
# Prozess kann nicht mehr in sein eigenes Log schreiben!
# Oder schlimmer: Log-Datei ist wegen umask für andere schreibbar
Korrigierter Code (C)
// Korrigiert: Ordnungsgemäße Privilegienablegung
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>
#include <stdlib.h>
int drop_privileges(const char *username) {
struct passwd *pw = getpwnam(username);
if (pw == NULL) {
return -1;
}
// Zuerst zusätzliche Gruppen ablegen
if (setgroups(0, NULL) != 0) {
return -1;
}
// Gruppenprivileg ablegen (muss geschehen während noch root)
if (setgid(pw->pw_gid) != 0) {
return -1;
}
// Zuletzt Benutzerprivileg ablegen
if (setuid(pw->pw_uid) != 0) {
return -1;
}
// Verifizieren, dass Ablegung erfolgreich war
if (getuid() != pw->pw_uid ||
geteuid() != pw->pw_uid ||
getgid() != pw->pw_gid ||
getegid() != pw->pw_gid) {
return -1;
}
// Verifizieren, dass wir root nicht wiedererlangen können
if (setuid(0) != -1) {
// Dies sollte fehlschlagen! Wenn es erfolgreich ist, ist Privilegienablegung fehlgeschlagen
return -1;
}
return 0;
}
int secure_chroot_service(char *app_home, char *filename, char *run_as_user) {
// Ressourcen öffnen, die benötigt werden, während noch privilegiert
// (falls Ressourcen Root-Zugang benötigen)
// Dateisystem-Sicht einschränken
if (chroot(app_home) != 0) {
return -1;
}
if (chdir("/") != 0) {
return -1;
}
// JETZT Privilegien ablegen
if (drop_privileges(run_as_user) != 0) {
fprintf(stderr, "Privilegienablegung fehlgeschlagen\n");
return -1;
}
// Sicher: Läuft jetzt als unprivilegierter Benutzer
FILE* data = fopen(filename, "r+");
if (data != NULL) {
process_file(data); // Jeder Exploit begrenzt auf unprivilegierten Benutzer
fclose(data);
}
return 0;
}
int secure_exec_program(char *program_path, uid_t run_as_uid, gid_t run_as_gid) {
// Programmpfad validieren - muss absolut und in erlaubter Liste sein
if (!is_allowed_program(program_path)) {
return -1;
}
// Privilegien VOR exec ablegen
if (setgroups(0, NULL) != 0 ||
setgid(run_as_gid) != 0 ||
setuid(run_as_uid) != 0) {
return -1;
}
// Verifizieren, dass Ablegung erfolgreich war
if (getuid() != run_as_uid || geteuid() != run_as_uid) {
return -1;
}
// Umgebung für Sicherheit löschen
char *safe_env[] = {"PATH=/usr/bin", NULL};
// Sicher: Externes Programm läuft unprivilegiert
execve(program_path, NULL, safe_env);
// Wenn wir hier ankommen, ist exec fehlgeschlagen
return -1;
}
# Korrigiert: Python-Dienst mit ordnungsgemäßer Privilegienablegung
import os
import subprocess
import pwd
import grp
class SecureService:
def drop_privileges(self, username):
"""Alle Root-Privilegien ablegen"""
pw = pwd.getpwnam(username)
# Zusätzliche Gruppen ablegen
os.setgroups([])
# Gruppen- dann Benutzerprivilegien ablegen
os.setgid(pw.pw_gid)
os.setuid(pw.pw_uid)
# Verifizieren
if os.getuid() != pw.pw_uid or os.getgid() != pw.pw_gid:
raise RuntimeError("Privilegienablegung fehlgeschlagen")
# Verifizieren, dass wir root nicht wiedererlangen können
try:
os.setuid(0)
raise RuntimeError("Privilegienablegung-Verifizierung fehlgeschlagen")
except PermissionError:
pass # Erwartet - wir sollten nicht setuid(0) können
def start_worker(self, task_config, worker_user):
# Hauptprozess läuft als root
pid = os.fork()
if pid == 0:
# Kindprozess
# Privilegien ZUERST ablegen
self.drop_privileges(worker_user)
# Skriptpfad validieren
script_path = task_config.get('script')
if not self.is_allowed_script(script_path):
os._exit(1)
# Sicher: Läuft jetzt als unprivilegierter Benutzer
subprocess.run(['/bin/bash', script_path])
os._exit(0)
def create_log_file(self, log_path, owner_user):
"""Log-Datei mit korrektem Besitz erstellen"""
pw = pwd.getpwnam(owner_user)
# Datei mit restriktiven Berechtigungen erstellen
old_umask = os.umask(0o077)
try:
fd = os.open(log_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o640)
os.write(fd, b"Log gestartet\n")
os.fchown(fd, pw.pw_uid, pw.pw_gid) # Besitz ändern
os.close(fd)
finally:
os.umask(old_umask)
# Jetzt sicher, Privilegien abzulegen
self.drop_privileges(owner_user)
Die Korrektur stellt sicher, dass Privilegien abgelegt werden, bevor nicht vertrauenswürdiger Input verarbeitet wird, verifiziert, dass Privilegienablegungen erfolgreich waren, und behandelt alle Aspekte von Privilegien (Benutzer, Gruppe, zusätzliche Gruppen).
Ausgenutzt in der Praxis
Setuid-Programm-Exploits (Unix/Linux-Systeme, Fortlaufend)
Zahlreiche Setuid-Programme wurden ausgenutzt, weil sie Privilegien nicht vor der Verarbeitung von Benutzereingaben oder dem Ausführen externer Programme abgelegt haben. Angreifer verwenden Umgebungsvariablen-Manipulation, Symlink-Angriffe und Input-Injection, um Root-Zugang durch diese erhöhten Prozesse zu erlangen.
Container-Runtime-Privilegieneskalation (Container-Plattformen, 2019-Gegenwart)
Container-Runtimes haben Fehler beim Ablegen von Privilegien erfahren, bei denen Container, die als unprivilegierte Benutzer laufen sollten, Root-Capabilities behalten haben. Bemerkenswerte Beispiele umfassen CVE-2019-5736 in runc, wo ein bösartiger Container die Host-runc-Binary aufgrund unzureichender Privilegienisolation überschreiben könnte.
Daemon-Privilegienbeibehaltung (Verschiedene Dienste, Historisch)
Netzwerk-Daemons, die als root für Port-Binding starteten, aber Privilegien vor der Behandlung von Client-Verbindungen nicht ablegten, wurden ausgenutzt. Schwachstellen im Verbindungsbehandlungscode gaben Angreifern Root-Zugang aufgrund beibehaltener Privilegien.
Tools zum Testen/Ausnutzen
-
PEDA/GEF — GDB-Erweiterungen für Exploit-Entwicklung, die helfen, den Privilegienzustand während des Debuggens zu identifizieren.
-
Lynis — Sicherheitsaudit-Tool, das Setuid-Programme und Privilegienkonfigurationsprobleme identifiziert.
-
checksec — Skript zur Prüfung von Sicherheitseigenschaften von Binaries einschließlich Setuid-Konfiguration.
CVE-Beispiele
-
CVE-2004-2504 — Windows-Programm, das als SYSTEM läuft, führt andere Programme ohne Privilegienablegung aus.
-
CVE-2004-0806 — Setuid-Programm legt Privilegien nicht ab, bevor es umgebungsvariablen-spezifiziertes Programm ausführt.
-
CVE-2000-1213 — Programm legt Privilegien nach Erwerb von Raw-Socket nicht ab.
-
CVE-2001-0787 — Fehler beim Ablegen von Privilegien in verwandten Gruppen während Privilegiensenkung.
Referenzen
-
MITRE Corporation. "CWE-271: Privilege Dropping / Lowering Errors." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/271.html
-
Chen, H., Wagner, D., and Dean, D. "Setuid Demystified." USENIX Security Symposium. https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf
-
CERT C Secure Coding Standard. "POS36-C. Observe correct revocation order while relinquishing privileges." https://wiki.sei.cmu.edu/confluence/display/c/POS36-C