Libxml2-XPath: Fehlerhafte Node-Allokation erlaubt Heap-Overflow

Die ursprünglich für das Gnome-Projekt entwickelte Bibliothek Libxml2 enthält zahlreiche Funktionen zum Verarbeiten von XML-Dokumenten. Unter anderem unterstützt sie das XPath-API, in dessen Implementierung vor kurzem eine Schwachstelle entdeckt wurde.

Diese Sicherheitslücke der Bibliothek hat zur Folge, dass ein entfernter Angreifer eine Heap-Buffer-Overflow-Attacke durchführen kann, weil dieser Fehler beim Verarbeiten spezieller XPath-Ausdrücke auftritt. XPath ist ein Sprachprojekt des W3-Konsortiums und erlaubt den einfachen Zugriff auf in XML-Dokumenten gespeicherte Daten. Ein XPath-Ausdruck adressiert Teile des XML-Dokuments, welches hierbei als Baumstruktur betrachtet wird. Die Knoten (“nodes”) dieses Baums sind XML-Elemente, -Attribute, -Namensräume und Ähnliches. Die Schwachstelle in Libxml2 wurde bei dem Versuch entdeckt, folgende XPath-Anweisung zu verarbeiten:

//@*/preceding::node()/ancestor::node()/ancestor::foo['foo']

Sie alloziert eine große Zahl von Nodes in Libxml2. Dabei hat sich herausgestellt, dass es es ein Problem in der Speicherallokation der Tree-Nodes gibt. Der verantwortliche Programmierfehler befindet sich in der Datei “xpath.c” und sieht so aus:

cur->nodeMax *= 2;
temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * sizeof(xmlNodePtr));
if (temp == NULL) { xmlXPathErrMemory(NULL, "growing nodeset\n"); return;
}
cur->nodeTab = temp;

Ein Programmierer sieht hier sofort, was schiefläuft: Der Eintrag “cur->nodeMax”, der die Zahl allozierter Nodes speichert, wird verdoppelt, bevor die eigentliche Reallokation mit “xmlRealloc()” mit der If-Abfrage auf Erfolg getestet wurde. Schlägt die Allokation fehl, wird “temp” wie üblich als NULL-Zeiger zurückgeliefert, und es kommt zu einem “xmlXPathErrMemory()”-Fehler. Doch zu diesem Zeitpunkt ist “cur->nodeMax” fälschlicherweise schon mit zwei multipliziert.

Teile der Bibliothek verlassen sich auf die Korrektheit dieses Wertes, beispielsweise zum Adressieren von Speicher. Durch den falschen Wert können so unerlaubt falsche Speicherbereiche außerhalb des Node-Puffers angesprochen werden, was ein Sicherheitsrisiko darstellt. Ein entfernter Angreifer kann diese Lücke ausnutzen, indem er sein Opfer dazu bringt, einen geschickt zusammengebauten XParse-Ausdruck aufzurufen.

Der Fix für diese Schwachstelle ist sehr einfach:

temp = (xmlNodePtr *) xmlRealloc(cur->nodeTab, cur->nodeMax * 2 * sizeof(xmlNodePtr));
if (temp == NULL) { xmlXPathErrMemory(NULL, "growing nodeset\n"); return;
}
cur->nodeMax *= 2;
cur->nodeTab = temp;

Dieses Code-Fragment sieht dem ursprünglichen sehr ähnlich, hat aber den entscheidenden Unterschied, dass die Variable “cur->nodeMax” erst dann verdoppelt wird, wenn die Speicherallokation wirklich erfolgreich war. So vermeidet diese Version den Heap-Buffer-Overflow. Diese Korrektur muss in verschiedenen Funktionen innerhalb von “xpath.c” vorgenommen werden: “xmlXPathNodeSetAddNs()”, “xmlXPathNodeSetAddUnique()”, “xmlXPathNodeSetMerge()”, “xmlXPathNodeSetMergeAndClear()” und “xmlXPathNodeSetMergeAndClearNoDupls()”.

Betroffen ist die Version 2.7.8.

Nach oben