Nicht-terminierte Strings in Netfilter

Firewalls sind aus der heutigen IT-Welt nicht mehr wegzudenken. Linux bietet mit Iptables eine sehr mächtige Umgebung zum Aufsetzen beliebiger Firewall-Regelsysteme. Sie verwendet das Framework Netfilter, in dem nun Programmierfehler entdeckt worden sind.

Iptables benutzt Netfilter, um sich via Hooks in den Netzwerkverkehr einzuklinken. Dazu stehen Module wie “ip_tables”, “ip6_tables” und “arp_tables” bereit. Kürzlich wurde ein Programmierfehler im Netfilter-Code des Kernels entdeckt. Dieser führt dazu, dass ein lokaler Angreifer möglicherweise Speicherbereiche des Kernels auslesen kann.

Die Schwachstelle befindet sich in der Datei “net/ipv4/netfilter/arp_tables.c”. Um eine erfolgreiche Attacke durchführen zu können, benötigt der Angreifer allerdings zusätzlich “CAP_NET_ADMIN”-Rechte. Dies ist eine recht mächtige Capability im Linux Kernel und gestattet es dem Anwender beispielsweise, die Konfiguration von Netzwerkkarten oder die Firewall-Regeln zu ändern.

Die Datei “arp_tables.c” enthält mehrere fehlerhafte Funktionen. Dabei handelt es sich jedoch stets um den gleichen Schwachstellentyp: einen nicht-terminierten C-String. Der Linux-Kernel ist hauptsächlich in nicht-typsicherem C programmiert, wodurch es häufig zu Problemen beim Verarbeiten von Strings kommt. Wird in C etwa ein String via

const char *str = "tux";

definiert, so ist das äquivalent zu

const char *str = {'t', 'u', 'x', 0};

In C ist ein String demnach einfach eine Folge von Zeichen, die durch ein Nullzeichen terminiert wird. Die String-Länge wird nicht direkt gespeichert, und zahlreiche C-Funktionieren verlassen sich auf die Null am Ende. Fehlt diese, kann das zu schwerwiegenden Sicherheitslecks führen. Wird beispielsweise ein nicht-terminierter String mit “sprintf()”verarbeitet, können andere Daten aus dem Stack oder Heap mitkopiert werden.

Genau das passiert an mehreren Stellen der Datei “arp_tables.c”. In der Funktion “static int do_arpt_get_ctl()” beispielsweise werden Daten via “copy_from_user()” aus dem Userspace in den Kernelspace kopiert. Dabei wird auch die String-Variable “rev.name” gesetzt. Allerdings findet sich im Code keine Überprüfung, ob die Variable nach dem Kopieren auch wirklich korrekt null-terminiert ist. Anschließend wird der so kopierte String an “try_then_request_module()” und “__request_module()” weitergeleitet. Das ist kritisch, denn der in “rev.name” gespeicherte String wird hier mit “vsnprintf()” bearbeitet und dann an den Modul-Code des Kernels weitergeleitet, um ein entsprechendes Modul zu laden. Da der String aber nicht terminiert ist, können so Daten aus dem Kernel in den Speicherbereich normaler Anwendungen kopiert werden. Auf diese Weise kann der Angreifer dann interne Daten des Kernels lesen.

Das Problem lässt sich trivial durch

tmp.name[sizeof(tmp.name)-1] = 0;

beheben, was im entsprechenden Kernelpatch auch geschieht. Damit ist sichergestellt, dass der String ordentlich terminiert ist, und kein Kernelspeicher gelangt in die Hände des Angreifers.

Die Fehler wurden in den Versionen 2.6.15-rc1 eingeführt. Fehler wie diese und allgemeinere Format-String-Fehler demonstrieren, wie wichtig Typsicherheit für sichere Programmentwicklung ist. Die String-Klasse der C++-Standard-Bibliothek beispielsweise bietet einen deutlich sichereren Umgang mit Zeichenketten. Sie verlässt sich nicht auf Null-Terminierung, sondern speichert die Länge des Strings explizit. Statische Typisierung wie sie in Java, Scala oder Ada implementiert ist, hilft zusätzlich, derartige Probleme zu erkennen und zu vermeiden.

Nach oben