
Detaillierte technische Analyse und funktionierender Exploit für CVE-2022-0492 Linux-Kernel-Container-Escape über cgroup release_agent, mit Schritt-für-Schritt-Laboreinrichtung und Mitigationsanleitung.
[toc]
Schwachstellen-ID: CVE-2022-0492
Betroffenes Produkt: Linux Kernel - cgroup
Betroffene Versionen: ~Linux Kernel 5.17-rc3
Auswirkungen: Wenn der Container keine zusätzlichen Sicherheitsmaßnahmen aktiviert hat, kann ein Root-Benutzer im Container zum Host entkommen.
Verwenden Sie Docker auf einem Linux-System mit der anfälligen Kernel-Version.
#关闭所有安全防护启动docker
docker run --rm -it -h cve --name cve --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash
Dieser Artikel verwendet Docker als Testumgebung.
Die Ausnutzungsmethode dieser Schwachstelle ist bereits altbekannt, aber der Schwachpunkt liegt darin, dass beim Ändern des release_agent von cgroup die Berechtigungsprüfung fehlt, was die Hürde für den Escape-Angriff weiter senkt (zuvor war die CAP_SYS_ADMIN-Berechtigung erforderlich, diese Schwachstelle benötigt CAP_SYS_ADMIN nicht). Die spezifischen Unterschiede der Ausnutzungsvoraussetzungen finden Sie im folgenden Abschnitt "Ausnutzungsbedingungen".
Analyse des Patches: Die Funktion cgroup_release_agent_write wurde gepatcht und eine Authentifizierung hinzugefügt. Dies bedeutet, dass der release_agent von cgroup nicht mehr von Benutzern ohne die entsprechenden Berechtigungen geändert werden kann:

Daher wird diese Schwachstelle als fehlerhafte Zugriffskontrolle eingestuft.
cgroup (Linux Control Group) ist eine Funktion des Linux-Kernels, die verwendet wird, um die Ressourcen einer Prozessgruppe (wie CPU, Speicher, Festplatten-E/A usw.) zu begrenzen, zu steuern und zu isolieren.
cgroup hat folgende Subsysteme:
devices Geräteberechtigungen im Prozessbereichcpuset Zuweisung der CPU-Anzahl und Speicherknoten, die ein Prozess verwenden kanncpu Steuerung der CPU-Auslastungcpuacct Statistik der CPU-Nutzung, z.B. Laufzeit, gedrosselte Zeitfreezer Anhalten von Prozessen in der Cgroupnet_cls In Kombination mit tc (traffic controller) Begrenzung der Netzwerkbandbreitenet_prio Festlegen der Netzwerkverkehrspriorität von Prozessenhuge_tlb Begrenzung der HugeTLB-Nutzungperf_event Ermöglicht dem Perf-Tool Leistungsüberwachung basierend auf Cgroup-GruppenAuf dem Host befinden sich alle cgroups unter /sys/fs/cgroup. Dort sind die verschiedenen cgroup-Subsysteme zu sehen:

Das entsprechende cgroup-Subsystem in Docker ist ein untergeordneter Knoten der cgroup auf dem Host. In Docker wird die memory-cgroup wie folgt angezeigt:

Der Knoten mit dem entsprechenden Containernamen im Docker-Verzeichnis des Hosts ist identisch:

cgroup wird in Form eines Dateisystems verwendet. Durch mount wird die cgroup in ein Verzeichnis eingehängt. Die cgroup interagiert über das virtuelle Dateisystem VFS mit uns. Die Schnittstellen der cgroup werden als Dateien dargestellt. Parameter der cgroup können direkt mit Dateioperationen gesetzt werden.
mount -t cgroup -o memory cgroup /tmp/testcgroup

Durch Erstellen eines Unterverzeichnisses im Verzeichnis kann ein untergeordneter cgroup-Knoten erstellt werden: mkdir /tmp/testcgroup/x.
Jedes Subsystem von cgroup hat einen Parameter notify_on_release, dessen Wert vom Typ Boolean ist (1 oder 0). Er kann die Anweisung zur Freigabe des Agents aktivieren bzw. deaktivieren. Wenn notify_on_release aktiviert ist (1) und die cgroup keine Aufgaben mehr enthält (d.h. wenn der letzte Prozess in der cgroup beendet wird und die PID in der tasks-Datei der cgroup leer ist), führt der Systemkernel den Inhalt der Datei aus, die durch den Parameter release_agent angegeben wird. Der Wert von notify_on_release wird durch Ändern der notify_on_release-Datei modifiziert.

Die Schwachstelle liegt in der Änderung des release_agent. Ursprünglich konnte der release_agent geändert werden, solange man Zugriff auf die cgroup hatte, aber für die Verwendung von cgroup war CAP_SYS_ADMIN erforderlich. Später fanden Forscher jedoch heraus, dass durch den Befehl unshare ein neuer Namespace erstellt werden kann, der alle Capabilities erhält, sodass die Einschränkung von CAP_SYS_ADMIN nicht mehr besteht und die Hürde für die Ausnutzung erheblich gesenkt wird.
Der Befehl unshare dient dazu, den angegebenen Namespace vom angegebenen übergeordneten Prozess zu entkoppeln, dann das angegebene Programm auszuführen und dem neu erstellten Namespace beizutreten. Für unsere Schwachstellenausnutzung ist relevant: Der mit unshare neu erstellte Namespace hat alle Capabilities, einschließlich CAP_SYS_ADMIN.

Diese Schwachstellenausnutzung ist identisch mit der traditionellen Escape-Methode mit CAP_SYS_ADMIN und cgroup release_agent, aber die Ausnutzungsbedingungen unterscheiden sich.
Traditioneller release_agent: Der Container benötigt CAP_SYS_ADMIN und es dürfen kein AppArmor oder SELinux aktiviert sein.
CVE-2022-0492: Der Container ist völlig ungeschützt (genauer gesagt: seccomp deaktiviert unshare nicht, AppArmor aktiviert cgroup nicht schreibgeschützt, SELinux deaktiviert). Es ist ausreichend, Root-Rechte im Container zu erlangen. CAP_SYS_ADMIN ist nicht erforderlich.
Es ist erwähnenswert, dass Docker standardmäßig AppArmor mit schreibgeschützter cgroup und seccomp mit Deaktivierung von unshare ohne CAP_SYS_ADMIN aktiviert. Kubernetes verwendet standardmäßig oft ungeschützte Container. Da die Ausnutzung relativ einfach ist, kann man es in konkreten Szenarien versuchen.
Nach der Patch-Behebung: Gemäß dem Patch-Code:

Um die release_agent-Datei ändern zu können, sind zwei Bedingungen erforderlich: 1. Im Root-Namespace sein, 2. CAP_SYS_ADMIN-Capability besitzen.
Daher kann nach der Behebung der Schwachstelle die durch unshare erworbene CAP_SYS_ADMIN nicht mehr zum Ändern des release_agent verwendet werden, da der neue Namespace nicht der Root-Namespace ist. Wenn der Container jedoch bereits über CAP_SYS_ADMIN verfügt, kann diese Escape-Methode weiterhin verwendet werden.
Wenn der Docker-Container mit dem Parameter --cap-add=SYS_ADMIN oder --privileged (privilegierter Container) gestartet wurde, besitzt er CAP_SYS_ADMIN und muss nicht separat erlangt werden, z.B. mit dem folgenden Startbefehl: