Die Zukunft des Penetrationstests: Die transformative Kraft von PentestGPT
06/12/2024ruid, euid, suid und setuid unter Linux
Was wirklich passiert – und warum es im Pentesting entscheidend ist
Wer sich länger mit Linux-Security beschäftigt, kennt diese Situation:
Ein SUID-Binary sieht vielversprechend aus, der Code ist sauber, die Theorie stimmt – und trotzdem landet man am Ende wieder bei nobody.
In den meisten Fällen liegt das Problem nicht im Exploit selbst, sondern in einem Missverständnis darüber, wie Linux mit Benutzer-IDs umgeht. Begriffe wie ruid, euid, suid und Funktionen wie setuid(), system() oder execve() werden oft vereinfacht dargestellt – die Realität ist deutlich feiner und für Pentester extrem relevant.
Dieser Artikel erklärt, wie Linux Benutzeridentitäten wirklich behandelt, warum Shells wie bash einem Privilege Escalation unbemerkt sabotieren können und worauf man in der Praxis achten muss.
Die drei Benutzer-IDs eines Linux-Prozesses
Jeder Linux-Prozess besitzt drei Benutzer-IDs, die jeweils eine andere Aufgabe erfüllen.
Die Real User ID (ruid) beschreibt den Benutzer, der den Prozess gestartet hat. Sie dient hauptsächlich der Nachvollziehbarkeit und Signalsteuerung und ändert sich in der Regel nie. Meldet sich ein Nutzer per SSH an, bleibt dieser Nutzer die ruid für alle daraus gestarteten Prozesse.
Die Effective User ID (euid) ist aus Sicherheitssicht die wichtigste ID. Sie entscheidet darüber, welche Rechte ein Prozess tatsächlich hat: Datei-Zugriffe, privilegierte Systemaufrufe und sicherheitsrelevante Operationen. Normalerweise ist die euid identisch mit der ruid – außer beim Einsatz von SetUID-Binaries.
Die Saved User ID (suid) ermöglicht es einem Prozess, temporär Privilegien abzugeben und sie später wiederherzustellen. Das ist typisch für Programme, die mit erhöhten Rechten starten, kurzzeitig unprivilegierten Code ausführen und anschließend wieder auf ihre ursprünglichen Rechte zurückkehren müssen.
Wichtig ist eine grundlegende Einschränkung:
Ein Prozess, der nicht als root läuft, kann seine euid nur auf Werte ändern, die er bereits besitzt – also ruid, euid oder suid. Alles andere erfordert Root-Rechte oder die Capability CAP_SETUID.
Warum setuid() häufig falsch verstanden wird
Der Name setuid() ist irreführend. Viele gehen davon aus, dass damit einfach die Benutzer-ID geändert wird. Tatsächlich verhält sich die Funktion je nach Kontext sehr unterschiedlich.
Für privilegierte Prozesse setzt setuid() ruid, euid und suid gleichzeitig auf den angegebenen Wert. Das bedeutet: Wird setuid(0) erfolgreich aufgerufen, ist der Prozess dauerhaft root – ein Zurück gibt es dann nicht mehr.
Für nicht-privilegierte Prozesse ist setuid() stark eingeschränkt. Es findet keine echte Rechteerhöhung statt, sondern lediglich ein Wechsel zwischen bereits vorhandenen Identitäten.
Funktionen wie setreuid() oder setresuid() erlauben eine feinere Steuerung. Root-Prozesse können damit beliebige Werte setzen, während nicht-root Prozesse weiterhin nur zwischen bestehenden IDs wechseln dürfen. Diese Funktionen sind nicht als Sicherheitsmechanismus gedacht, sondern zur sauberen Steuerung von Programmabläufen.
execve() vs. system() – gleicher Zweck, völlig anderes Verhalten
Im Pentesting ist nicht nur relevant, welche UID ein Prozess hat, sondern auch wie Programme gestartet werden.
execve() ersetzt den aktuellen Prozess vollständig. Die Prozess-ID bleibt gleich, aber Speicher, Code und Ausführungskontext werden ausgetauscht. Dabei bleiben ruid und euid erhalten – es sei denn, das neue Binary besitzt selbst das SetUID-Bit.
Das macht execve() vorhersehbar und zuverlässig. Es gibt keine Shell, keine Zusatzlogik und keine versteckten Sicherheitsmechanismen.
system() hingegen funktioniert grundlegend anders. Intern wird zunächst ein fork() durchgeführt und anschließend /bin/sh -c ausgeführt. Das bedeutet: Es wird eine Shell gestartet, und genau diese Shell entscheidet mit, was mit den Privilegien passiert.
An dieser Stelle scheitern viele Exploits.
Warum bash Privilegien „kaputt macht“
Auf modernen Systemen zeigt /bin/sh häufig auf bash. Und bash ist sehr strikt, wenn es um Sicherheit geht.
Sobald bash erkennt, dass sich ruid und euid unterscheiden, geht sie davon aus, dass etwas nicht stimmt. Ohne explizite Anweisung setzt sie die euid wieder auf die ruid zurück und entfernt damit effektiv alle erhöhten Rechte.
Genau deshalb funktionieren viele Exploits mit system("id") nicht wie erwartet. Selbst wenn vorher korrekt eine euid gesetzt wurde, verwirft bash diese wieder – es sei denn, sie wird mit dem -p-Parameter gestartet, der explizit anweist, die Privilegien beizubehalten.
Andere Shells verhalten sich teilweise anders, aber die Kernaussage bleibt: Sobald eine Shell beteiligt ist, wird es unberechenbar.
Praxisbeispiele: Wo Privilege Escalation scheitert
Ein klassisches Beispiel ist ein SUID-Programm, das setuid(1000) aufruft und danach system("id") ausführt. Theoretisch sollte der Prozess nun als Benutzer 1000 laufen. Praktisch startet system() eine Shell, bash erkennt die UID-Differenz und setzt die euid zurück – das Ergebnis ist wieder der ursprüngliche Low-Privilege-User.
Wird stattdessen setreuid(1000, 1000) verwendet, sind ruid und euid identisch. bash sieht keinen Grund, einzugreifen, und die Privilegien bleiben erhalten.
Noch sauberer ist der direkte Aufruf von execve("/usr/bin/id"). Ohne Shell bleibt die euid unangetastet und das Verhalten ist exakt vorhersehbar.
Wird hingegen execve("/bin/bash") ohne -p genutzt, greift bash erneut ein und verwirft die erhöhten Rechte. Erst mit bash -p bleiben sie bestehen.
Diese Details wirken klein, sind aber in realen Pentests oft der entscheidende Unterschied zwischen Erfolg und Fehlschlag.
Fazit für Pentester und Security Engineers
Wer SUID-Binaries analysiert, lokale Privilege Escalations entwickelt oder Code Reviews für privilegierte Programme durchführt, sollte diese Mechanismen im Schlaf beherrschen.
Verlasse dich nicht auf Annahmen über setuid(). Sei vorsichtig mit system(). Meide Shells, wenn es möglich ist. Und vergiss nie, dass bash standardmäßig auf der Seite der Sicherheit steht – nicht auf deiner.
Bei yatesh sehen wir regelmäßig Umgebungen, in denen Privilege-Escalation-Pfade vorhanden sind, aber übersehen werden, weil genau diese Feinheiten nicht berücksichtigt wurden. Nicht das System ist sicher – sondern der Exploit ist falsch gebaut.