
Technische Analyse und Exploit-Entwicklung für CVE-2021-3156, einen Heap-basierten Pufferüberlauf in Sudo, einschließlich drei funktionierender Exploits für lokale Privilegieneskalation auf wichtigen Linux-Distributionen.
Qualys Security Advisory
Baron Samedit: Heap-basierter Pufferüberlauf in Sudo (CVE-2021-3156)
Zusammenfassung Analyse Ausnutzung Danksagungen Zeitplan
Wir haben einen heap-basierten Pufferüberlauf in Sudo (https://www.sudo.ws/) entdeckt. Diese Schwachstelle:
ist von jedem lokalen Benutzer ausnutzbar (normale Benutzer und Systembenutzer, sudoers und Nicht-sudoers), ohne Authentifizierung (d. h., der Angreifer muss das Passwort des Benutzers nicht kennen);
wurde im Juli 2011 eingeführt (Commit 8255ed69) und betrifft alle Legacy- Versionen von 1.8.2 bis 1.8.31p2 sowie alle stabilen Versionen von 1.9.0 bis 1.9.5p1 in ihrer Standardkonfiguration.
Wir haben drei verschiedene Exploits für diese Schwachstelle entwickelt und volle Root-Rechte auf Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27) und Fedora 33 (Sudo 1.9.2) erlangt. Andere Betriebssysteme und Distributionen sind wahrscheinlich ebenfalls ausnutzbar.
Wenn Sudo ausgeführt wird, um einen Befehl im "Shell"-Modus auszuführen (shell -c command):
entweder über die Option -s, die Sudos MODE_SHELL-Flag setzt;
oder über die Option -i, die Sudos MODE_SHELL- und MODE_LOGIN_SHELL-Flags setzt;
dann schreibt parse_args() zu Beginn von Sudos main() argv um (Zeilen 609-617), indem es alle Kommandozeilenargumente verkettet (Zeilen 587-595) und alle Meta-Zeichen mit Backslashes escaped (Zeilen 590-591):
Später, in sudoers_policy_main(), verkettet set_cmnd() die Kommandozeilenargumente in einen heap-basierten Puffer "user_args" (Zeilen 864-871) und entfernt das Escaping der Meta-Zeichen (Zeilen 866-867), "für sudoers-Abgleich- und Protokollierungszwecke":
Leider gilt: Wenn ein Kommandozeilenargument mit einem einzelnen Backslash- Zeichen endet, dann:
ist bei Zeile 866 "from[0]" das Backslash-Zeichen und "from[1]" der Nullterminator des Arguments (d. h., kein Leerzeichen);
wird bei Zeile 867 "from" inkrementiert und zeigt auf den Nullterminator;
wird bei Zeile 868 der Nullterminator in den Puffer "user_args" kopiert, und "from" wird erneut inkrementiert und zeigt auf das erste Zeichen nach dem Nullterminator (d. h., außerhalb der Grenzen des Arguments);
liest und kopiert die "while"-Schleife bei den Zeilen 865-869 Zeichen außerhalb der Grenzen in den Puffer "user_args".
Mit anderen Worten: set_cmnd() ist anfällig für einen heap-basierten Puffer- überlauf, weil die Zeichen außerhalb der Grenzen, die in den Puffer "user_args" kopiert werden, in dessen Größe (berechnet in den Zeilen 852-853) nicht enthalten waren.
Theoretisch kann jedoch kein Kommandozeilenargument mit einem einzelnen Backslash-Zeichen enden: Wenn MODE_SHELL oder MODE_LOGIN_SHELL gesetzt ist (Zeile 858, eine notwendige Bedingung, um den anfälligen Code zu erreichen), dann ist MODE_SHELL gesetzt (Zeile 571) und parse_args() hat bereits alle Meta-Zeichen escaped, einschließlich Backslashes (d. h., es hat jeden einzelnen Backslash mit einem zweiten Backslash escaped).
In der Praxis jedoch sind der anfällige Code in set_cmnd() und der Escape- Code in parse_args() von geringfügig unterschiedlichen Bedingungen umgeben:
versus:
Unsere Frage lautet also: Können wir MODE_SHELL und entweder MODE_EDIT oder MODE_CHECK setzen (um den anfälligen Code zu erreichen), aber nicht das standardmäßige MODE_RUN (um den Escape-Code zu vermeiden)?
Die Antwort scheint nein zu sein: Wenn wir MODE_EDIT (Option -e, Zeile 361) oder MODE_CHECK (Option -l, Zeilen 423 und 519) setzen, dann entfernt parse_args() MODE_SHELL aus den "valid_flags" (Zeilen 363 und 424) und beendet mit einem Fehler, wenn wir ein ungültiges Flag wie MODE_SHELL angeben (Zeilen 532-533):
Aber wir haben eine Hintertür gefunden: Wenn wir Sudo als "sudoedit" statt als "sudo" ausführen, dann setzt parse_args() automatisch MODE_EDIT (Zeile 270), setzt aber "valid_flags" nicht zurück, und die "valid_flags" enthalten MODE_SHELL standardmäßig (Zeilen 127 und 249):