Verletzung des Prinzips der geringsten Privilegien

Beschreibung

Verletzung des Prinzips der geringsten Privilegien ist eine Schwachstelle, die auftritt, wenn ein Produkt mit mehr Privilegien als für die anstehende Aufgabe notwendig arbeitet, insbesondere wenn erhöhte Privilegien, die für spezifische Operationen wie chroot() erforderlich sind, nach Abschluss der Operation beibehalten werden. Programme, die unnötige erhöhte Privilegien beibehalten, erzeugen Sicherheitsrisiken, da jede im Programm entdeckte Schwachstelle mit diesen erhöhten Privilegien ausgenutzt werden kann, anstatt auf den minimalen Zugang beschränkt zu sein, den das Programm tatsächlich benötigt. Diese Schwäche stellt ein Versäumnis dar, dem Sicherheitsprinzip der geringsten Privilegien zu folgen, das besagt, dass Subjekten nur der minimale Zugang gewährt werden sollte, der zur Erfüllung ihrer Aufgaben notwendig ist.

Risiko

Das Arbeiten mit übermäßigen Privilegien verstärkt die Auswirkungen anderer Schwachstellen erheblich. Wenn Programme Root- oder Administratorzugang länger als nötig behalten, bieten Buffer Overflows, Command Injection, Path Traversal und andere Exploits Angreifern erhöhten Zugang anstatt begrenzten Benutzerebenen-Zugang. Das Risiko verstärkt sich über Zeit, da die Angriffsfläche von privilegiertem Code typischerweise größer als nötig ist. Service-Konten und Daemons, die während ihrer gesamten Lebensdauer mit erhöhten Privilegien laufen, stellen dauerhafte hochwertige Ziele dar. Selbst vorübergehende Privilegienbeibehaltung erzeugt Schwachstellenfenster, während derer Exploits maximale Auswirkung erzielen können. Organisationen, die das Prinzip der geringsten Privilegien nicht implementieren, haben auch Compliance-Probleme mit Vorschriften wie PCI DSS, die minimalen Zugang vorschreiben.

Lösung

Legen Sie erhöhte Privilegien sofort nach Abschluss von Operationen ab, die sie erfordern. Strukturieren Sie Programme so, dass privilegienerfordernde Operationen am Anfang der Ausführung stattfinden, wonach Privilegien dauerhaft aufgegeben werden können. Verwenden Sie fähigkeitsbasierte Sicherheit, wo unterstützt, um nur spezifische erforderliche Fähigkeiten beizubehalten anstatt vollständigen Root-Zugang. Implementieren Sie Privilegientrennungs-Architekturen, bei denen eine minimale privilegierte Komponente nur notwendige erhöhte Operationen ausführt und mit unprivilegierten Komponenten kommuniziert, die komplexe Logik und nicht vertrauenswürdigen Input handhaben. Verifizieren Sie, dass Privilegienablegungsoperationen erfolgreich sind, indem Sie Rückgabewerte prüfen und den resultierenden Privilegienzustand bestätigen. Entwerfen Sie Systeme mit expliziten Vertrauenszonen und stellen Sie sicher, dass sensible Daten innerhalb angemessener Grenzen geschützt bleiben. Wenn erhöhte Privilegien nicht vermieden werden können, minimieren Sie den Codepfad, der mit diesen Privilegien läuft, und wenden Sie Defense in Depth an.

Häufige Auswirkungen

AuswirkungDetails
Zugriffskontrolle, VertraulichkeitBereich: Zugriffskontrolle, Vertraulichkeit

Angreifer, die eine Schwachstelle im Programm ausnutzen, können mit erhöhten Privilegien auf Ressourcen zugreifen, die mit dem ursprünglichen oder beabsichtigten Privilegienniveau des Angreifers nicht zugänglich wären. Dieses Risiko wird verstärkt, wenn es mit anderen Schwachstellen wie Buffer Overflows, Command Injection oder Code-Ausführungsfehlern kombiniert wird.

Beispielcode

Anfälliger Code (C)

Die folgenden Beispiele demonstrieren Verletzungen des Prinzips der geringsten Privilegien:

// Anfällig: Behält root nach privilegierter Operation
#include <stdio.h>
#include <unistd.h>

int vulnerable_service(const char *app_home, const char *filename) {
    // Privilegierte Operation ausführen
    chroot(app_home);
    chdir("/");

    // Anfällig: Läuft immer noch als root!
    // Aller nachfolgender Code läuft mit unnötigen Privilegien

    FILE* data = fopen(filename, "r+");
    if (data != NULL) {
        // Schwachstelle hier (z.B. Buffer Overflow) = Root-Kompromittierung
        process_user_data(data);
        fclose(data);
    }

    // Lang laufende Service-Schleife - gesamte Dauer läuft als root
    while (1) {
        handle_client_connection();  // Jeder Client wird als root behandelt!
    }

    return 0;
}
// Anfällig: Privileg nur vorübergehend abgelegt
#include <unistd.h>
#include <sys/types.h>

void vulnerable_privilege_management(void) {
    uid_t original_uid = getuid();  // Original speichern (wahrscheinlich nicht-root)

    // Zu root eskalieren
    setuid(0);

    // Privilegierte Operation ausführen
    do_privileged_operation();

    // Privileg ablegen
    setuid(original_uid);

    // Unprivilegierte Arbeit ausführen
    do_unprivileged_work();

    // Anfällig: Privileg kann wiedererlangt werden!
    // setuid hat nur effektive UID gesetzt, saved-set-uid ist immer noch 0
    setuid(0);  // Dies ist erfolgreich!
    do_malicious_operation_as_root();
}
// Anfällig: Java-Anwendung mit übermäßigen Berechtigungen
public class VulnerableService {

    public void processUserRequest(Request request) {
        // Läuft mit AllPermission im Security-Manager
        // oder mit unnötig breitem Dateisystemzugang

        // Jede Operation läuft mit maximalen Privilegien
        File userFile = new File(request.getFilePath());

        // Schwachstelle: Path Traversal nutzt vollen Systemzugang aus
        readFile(userFile);  // Kann JEDE Datei auf System zugreifen

        // Netzwerkoperationen haben auch übermäßigen Zugang
        connectToServer(request.getServerUrl());  // Keine Einschränkungen
    }
}

Korrigierter Code (C)

// Korrigiert: Privilegien sofort nach privilegierten Operationen ablegen
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <grp.h>
#include <pwd.h>

int permanently_drop_privileges(const char *username) {
    struct passwd *pw = getpwnam(username);
    if (pw == NULL) {
        return -1;
    }

    // Zusätzliche Gruppen löschen
    if (setgroups(0, NULL) != 0) {
        return -1;
    }

    // Reale, effektive UND gespeicherte Gruppen-ID setzen
    if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) {
        return -1;
    }

    // Reale, effektive UND gespeicherte Benutzer-ID setzen
    if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) {
        return -1;
    }

    // Verifizieren, dass wir Privilegien nicht wiedererlangen können
    if (setuid(0) != -1) {
        return -1;  // Sollte fehlgeschlagen sein!
    }

    return 0;
}

int secure_service(const char *app_home, const char *filename,
                   const char *run_as_user) {
    // Privilegierte Operation ausführen (erfordert root)
    if (chroot(app_home) != 0) {
        return -1;
    }
    if (chdir("/") != 0) {
        return -1;
    }

    // SOFORT Privilegien nach privilegierter Operation ablegen
    if (permanently_drop_privileges(run_as_user) != 0) {
        fprintf(stderr, "Privilegienablegung fehlgeschlagen\n");
        return -1;
    }

    // Läuft jetzt als unprivilegierter Benutzer
    // Jede Schwachstelle ist auf den Zugang dieses Benutzers begrenzt

    FILE* data = fopen(filename, "r+");
    if (data != NULL) {
        process_user_data(data);  // Begrenzte Auswirkung bei Ausnutzung
        fclose(data);
    }

    // Service-Schleife läuft unprivilegiert
    while (1) {
        handle_client_connection();  // Begrenzte Privilegien
    }

    return 0;
}
// Korrigiert: Fähigkeitsbasierte Sicherheit anstelle von vollem root verwenden
#include <sys/capability.h>
#include <sys/prctl.h>

int drop_to_capabilities(cap_value_t *needed_caps, int num_caps) {
    cap_t caps;

    // Leeren Fähigkeitssatz erstellen
    caps = cap_init();
    if (caps == NULL) {
        return -1;
    }

    // Nur erforderliche Fähigkeiten hinzufügen
    if (cap_set_flag(caps, CAP_PERMITTED, num_caps, needed_caps, CAP_SET) != 0 ||
        cap_set_flag(caps, CAP_EFFECTIVE, num_caps, needed_caps, CAP_SET) != 0) {
        cap_free(caps);
        return -1;
    }

    // Den eingeschränkten Fähigkeitssatz anwenden
    if (cap_set_proc(caps) != 0) {
        cap_free(caps);
        return -1;
    }

    cap_free(caps);

    // Weitere Fähigkeitsvererbung verhindern
    prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);

    return 0;
}

int secure_network_service(void) {
    // An privilegierten Port binden (erfordert CAP_NET_BIND_SERVICE)
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    bind_to_port(sock, 443);

    // Auf minimale Fähigkeiten reduzieren
    cap_value_t minimal_caps[] = {CAP_NET_BIND_SERVICE};
    if (drop_to_capabilities(minimal_caps, 1) != 0) {
        return -1;
    }

    // Service läuft nur mit Netzwerkfähigkeit, keine anderen Root-Kräfte
    while (1) {
        handle_connection(sock);
    }
}
// Korrigiert: Java-Anwendung mit eingeschränkten Berechtigungen
public class SecureService {

    private static final Set<String> ALLOWED_DIRECTORIES =
        Set.of("/app/data", "/app/uploads", "/app/logs");

    public void processUserRequest(Request request) {
        // Dateizugang validieren und einschränken
        File userFile = new File(request.getFilePath());
        if (!isPathAllowed(userFile)) {
            throw new SecurityException("Zugang verweigert: " + userFile);
        }

        // SecurityManager mit eingeschränkter Richtlinie verwenden
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkRead(userFile.getAbsolutePath());
        }

        readFile(userFile);

        // Netzwerkzugang validieren und einschränken
        if (!isAllowedServer(request.getServerUrl())) {
            throw new SecurityException("Verbindung nicht erlaubt");
        }
        connectToServer(request.getServerUrl());
    }

    private boolean isPathAllowed(File file) {
        try {
            String canonical = file.getCanonicalPath();
            return ALLOWED_DIRECTORIES.stream()
                .anyMatch(canonical::startsWith);
        } catch (IOException e) {
            return false;
        }
    }
}

Die Korrektur stellt sicher, dass Privilegien sofort nach Operationen abgelegt werden, die sie erfordern, und verwendet permanente Ablegungen (setresuid), um Privilegienwiedererlangung zu verhindern.


Ausgenutzt in der Praxis

Daemon-Service-Exploits (Unix/Linux-Systeme, Fortlaufend)

Netzwerk-Daemons, die Root-Privilegien behielten, während sie Client-Anfragen verarbeiteten, wurden wiederholt ausgenutzt. Buffer Overflows und Format-String-Schwachstellen in Diensten wie sendmail, BIND und anderen als root laufenden Daemons gaben Angreifern vollständigen Systemzugang.

Container-Runtime-Schwachstellen (Container-Plattformen, 2019-Gegenwart)

Container-Runtimes, die mit erhöhten Privilegien laufen, haben Schwachstellen erfahren, bei denen Container-Escape möglich war, weil die Runtime mehr Privilegien als nötig beibehielt. CVE-2019-5736 demonstrierte, wie beibehaltene Privilegien in Container-Runtimes ausgenutzt werden könnten.

Webserver-CGI-Exploits (Webserver, Historisch)

Webserver, die CGI-Skripte mit übermäßigen Privilegien ausführten, ermöglichten Webanwendungsschwachstellen, gesamte Systeme zu kompromittieren. Skripte, die als root oder mit breitem Dateisystemzugang liefen, verwandelten einfache Web-Schwachstellen in vollständige Systemkompromittierungen.


Tools zum Testen/Ausnutzen

  • Lynis — Sicherheitsaudit-Tool, das Prozesse identifiziert, die mit übermäßigen Privilegien laufen.

  • pscap — Tool zur Anzeige von Fähigkeiten laufender Prozesse, identifiziert Privilegienkonfigurationen.

  • grsecurity — Linux-Kernel-Sicherheitspatches, die RBAC und fähigkeitsbasierte Sicherheitstools enthalten.


CVE-Beispiele

Verletzungen des Prinzips der geringsten Privilegien sind typischerweise beitragende Faktoren anstatt eigenständiger CVEs. Sie verstarken die Auswirkungen anderer Schwachstellen:

  • CVE-2019-5736 — runc-Container-Escape nutzte von Container-Runtime beibehaltene Privilegien aus.

  • CVE-2021-4034 — Polkit pkexec lokale Privilegieneskalation nutzte suid-root-Ausführung aus.

  • CVE-2021-3156 — Sudo-Heap-Overflow eskalierte aufgrund von setuid-Privilegien zu root.


Referenzen

  1. MITRE Corporation. "CWE-272: Least Privilege Violation." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/272.html

  2. CERT C Secure Coding Standard. "POS02-C. Follow the principle of least privilege." https://wiki.sei.cmu.edu/confluence/display/c/POS02-C

  3. CERT Oracle Secure Coding Standard for Java. "SEC00-J. Do not allow privileged blocks to leak sensitive information." https://wiki.sei.cmu.edu/confluence/display/java/SEC00-J

  4. Saltzer, J.H. and Schröder, M.D. "The Protection of Information in Computer Systems." IEEE. https://ieeexplore.ieee.org/document/1451869