Assignment Chef icon Assignment Chef
All German tutorials

Programming lesson

Set-UID-Programme und die System()-Funktion: Eine praktische Übung zur Computersicherheit

Lerne in dieser Schritt-für-Schritt-Anleitung, wie die system()-Funktion in C funktioniert, wie Set-UID-Programme root-Rechte verleihen und wie Angreifer diese Schwachstellen ausnutzen können – inklusive aktueller Beispiele aus der Praxis.

Set-UID-Programm system()-Funktion Computersicherheit Privilege Escalation Path-Hijacking Capability Leaking C-Programmierung Sicherheit root-Rechte Programm getuid geteuid Set-UID Schwachstelle CTF Sicherheit Linux Sicherheit Sicherheitslücke system() Set-UID Tutorial CSCI 180 Sicherheitsübung

Einführung in Set-UID-Programme und die System()-Funktion

In diesem Tutorial lernst du die grundlegenden Konzepte von Set-UID-Programmen und der system()-Funktion in C kennen. Diese Themen sind zentral für die Computersicherheit, da sie zeigen, wie scheinbar harmlose Programme zu Sicherheitslücken werden können. Wir verwenden praktische Beispiele, die du selbst nachvollziehen kannst – ähnlich wie bei einem aktuellen Hacking-Wettbewerb oder einer CTF-Challenge.

Was ist die system()-Funktion?

Die system()-Funktion in C führt einen Befehl in einer Shell aus. Im Gegensatz zu execve(), das den aktuellen Prozess ersetzt, startet system() zuerst /bin/sh und übergibt dann den Befehl an diese Shell. Das ist vergleichbar mit einem AI-Assistenten, der einen Befehl an eine andere KI weiterleitet – die Sicherheitsimplikationen sind enorm.

// systemtest.c
#include <stdlib.h>

int main() {
    system("ls");
    return 0;
}

Wenn du dieses Programm kompilierst (gcc systemtest.c -o systemtest) und ausführst, siehst du die Ausgabe des ls-Befehls. Aber welches ls wird ausgeführt? Die Antwort hängt von der PATH-Umgebungsvariable ab.

Die PATH-Umgebungsvariable manipulieren

Die PATH-Variable enthält eine Liste von Verzeichnissen, in denen die Shell nach ausführbaren Dateien sucht. Wenn du system("ls") aufrufst, sucht die Shell zuerst in den in PATH angegebenen Verzeichnissen. Du kannst PATH ändern, um dein eigenes ls-Programm auszuführen:

export PATH=.:$PATH

Jetzt sucht die Shell zuerst im aktuellen Verzeichnis. Schreibe ein eigenes ls-Programm:

// ls.c
#include <stdio.h>

int main() {
    printf("Eigenes ls-Programm\n");
    return 0;
}

Kompiliere es (gcc ls.c -o ls) und führe ./systemtest aus. Du siehst die Ausgabe deines eigenen ls-Programms – eine klassische Path-Hijacking-Technik, die oft in Sicherheitsvorlesungen und CTFs gezeigt wird.

Set-UID-Programme: Root-Rechte für normale Benutzer

Set-UID-Programme laufen mit den Rechten des Dateibesitzers, nicht des Ausführenden. Wenn du die Besitzerrechte auf root setzt und das Set-UID-Bit aktivierst, erhält das Programm root-Privilegien – unabhängig davon, wer es startet. Das ist wie ein VIP-Pass, der dir Zugang zu Bereichen gibt, die normalerweise gesperrt sind.

sudo chown root systemtest
sudo chmod 4755 systemtest

Jetzt kannst du als normaler Benutzer systemtest ausführen, und es läuft mit root-Rechten. Das ist nützlich für administrative Aufgaben, aber gefährlich, wenn das Programm Schwachstellen enthält – ähnlich wie ein ungesicherter API-Endpunkt in einer Web-App.

Real- und Effective-User-ID auslesen

Um zu prüfen, mit welchen Rechten ein Programm läuft, kannst du getuid() (reale Benutzer-ID) und geteuid() (effektive Benutzer-ID) verwenden. Füge in dein ls-Programm ein:

printf("Real UID: %d\n", getuid());
printf("Effective UID: %d\n", geteuid());

Wenn du systemtest ausführst, erwartest du, dass die effektive UID 0 (root) ist. Aber in modernen Ubuntu-Systemen verhindert /bin/sh (das oft auf dash verlinkt ist) die Ausführung in Set-UID-Kontexten – es setzt die effektive UID auf die reale UID zurück. Das ist eine Sicherheitsmaßnahme, die viele Angriffe vereitelt.

Umgehung der Schutzmaßnahme mit Zsh

Um dennoch zu demonstrieren, wie ein Angriff funktioniert, kannst du /bin/sh auf zsh umleiten, das keinen solchen Schutz hat:

sudo rm /bin/sh
sudo ln -s /bin/zsh /bin/sh

Jetzt zeigt die effektive UID 0 an – der Angriff funktioniert. Dies ist ein Paradebeispiel für Privilege Escalation, ein Thema, das in der aktuellen Sicherheitsforschung heiß diskutiert wird, etwa bei Zero-Day-Exploits in Cloud-Umgebungen.

Der echte Angriff: Shell als ls tarnen

Als Angreifer könntest du eine Shell namens ls erstellen und sie im aktuellen Verzeichnis platzieren. Wenn das Set-UID-Programm system("ls") aufruft, wird deine Shell mit root-Rechten gestartet. Du erhältst eine Root-Shell (erkennbar am #-Prompt).

cp /bin/sh ./ls

Führe ./systemtest aus – du landest in einer Root-Shell. Von hier aus kannst du beliebige Befehle mit root-Rechten ausführen. Das ist der Kern vieler realer Exploits, wie sie etwa bei der Dirty COW-Schwachstelle oder in aktuellen Container-Escapes vorkommen.

Capability Leaking: Wenn Privilegien nicht richtig entzogen werden

Ein weiteres Sicherheitsproblem ist das Capability Leaking. Wenn ein Set-UID-Programm seine root-Rechte mit setuid(getuid()) zurückgibt, aber vorher geöffnete Dateien oder andere Ressourcen nicht schließt, können diese weiterhin mit root-Rechten genutzt werden.

// capleak.c
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main() {
    int fd = open("/etc/readonlyfile", O_RDWR);
    setuid(getuid());
    write(fd, "Hallo", 5);
    close(fd);
    return 0;
}

Erstelle die Datei /etc/readonlyfile als root mit Berechtigung 644 (nur lesbar für andere). Kompiliere das Programm, mache es zum Set-UID-Programm und führe es als normaler Benutzer aus. Obwohl die effektive UID nach setuid() auf die reale UID gesetzt wird, bleibt der Dateideskriptor fd geöffnet und erlaubt das Schreiben – ein klassisches Capability Leaking.

Dieses Beispiel zeigt, warum das Prinzip der geringsten Privilegien (Least Privilege) so wichtig ist. In der Praxis, etwa bei Cloud-Diensten oder Microservices, kann Capability Leaking zu schwerwiegenden Sicherheitslücken führen, wenn nicht alle Berechtigungen vor dem Downgrade entzogen werden.

Fazit und Ausblick

Set-UID-Programme und die system()-Funktion sind mächtige Werkzeuge, aber auch häufige Quellen für Sicherheitslücken. Dieses Tutorial hat dir gezeigt, wie Path-Hijacking, Privilege Escalation und Capability Leaking funktionieren – Techniken, die in der realen Welt von Angreifern und in CTF-Wettbewerben gleichermaßen eingesetzt werden. Mit diesem Wissen kannst du sicherere Programme schreiben und bestehende Systeme besser absichern.

Wenn du mehr über Computersicherheit lernen möchtest, empfehle ich dir, dich mit Themen wie Buffer Overflows, SQL-Injection oder Cross-Site Scripting zu beschäftigen – allesamt relevante Angriffsvektoren in der heutigen IT-Landschaft.