Eine Schwachstelle in der Bibliothek Librsvg hat zur Folge, dass ein entfernter Angreifer Denial-of-Service-Attacken durchführen kann, wobei ein Exploit hierfür derzeit im Umlauf ist. Prinzipiell ist auch das Ausführen von Befehlen mit den Rechten des Anwenders möglich.
Die Librsvg wird von Anwendungen zum Verarbeiten skalierbarer Vektorgrafiken im SVG-Format herangezogen. Für die nun gefundene Sicherheitslücke ist ein Denial-of-Service-Exploit im Umlauf, der beim Laden mit einer Librsvg-Applikation dazu führt, dass diese mit einem Segmentation Fault abstürzt. Dies ist noch die harmlose Variante der Schwachstelle, denn prinzipiell ist es damit auch möglich, Befehle mit den Rechten des Programm-Anwenders auszuführen, wobei dafür derzeit kein Exploit verfügbar ist. Wer den Debugger Gdb auf das abstürzende Programm ansetzt, erhält folgenden Backtrace:
[Switching to Thread 0xb7d81b70 (LWP 17083)] 0x00000000 in ?? () (gdb) bt #0 0x00000000 in ?? () #1 0x002b7d08 in rsvg_filter_primitive_render (ctx=0x8357b28, self=<optimized out>) at rsvg-filter.c:85 #2 rsvg_filter_render (self=0x82e57f8, source=0x82ce4f8, context=0x82ddfd0, bounds=0x82f9140, channelmap=0x2cf6cb "2103") at rsvg-filter.c:499 #3 0x002ca0e7 in rsvg_cairo_pop_render_stack (ctx=0x82ddfd0) at rsvg-cairo-draw.c:970 ...
Offensichtlich stürzt das Programm in der Funktion “rsvg_filter_primitive_render()”, die für das Rendern von Filter-Funktionen zuständig ist. Um zu verstehen, woher dieses Problem kommt, muss man zunächst wissen, dass Librsvg jedem Grafik-Element in einer SVG-Datei eine “RsvgNode”-Struktur (in der Header-Datei “rsvg-private.h”) zuordnet:
typedef struct _RsvgNode RsvgNode;
struct _RsvgNode { RsvgState *state; RsvgNode *parent; GPtrArray *children; RsvgNodeType type; const char *name; /* owned by the xmlContext, invalid after parsing! */ void (*free) (RsvgNode * self); void (*draw) (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate); void (*set_atts) (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag *);
};
In dieser Struktur werden die verschiedenen Elemente einer Vektorgrafik abgelegt, wobei eine baumartige Organisation zum Einsatz kommt. Daher enthält “RsvgNode” Children- und Parent-Zeiger. Diese Nodes können verschiedene Elemente der Grafik wie Linien, Pfade, Rechtecke repräsentieren, aber auch komplexere Filter-Funktionen wie Blending. Das Allozieren und Initialisieren der “RsvgNode”-Strukturen verschiedener Grafikelemente einer Vektordatei erfolgt in der Funktion “rsvg_standard_element_start()” in der Quelltextdatei “rsvg-base.c”:
static void rsvg_standard_element_start (RsvgHandle * ctx, const char *name, RsvgPropertyBag * atts)
{ /*replace this stuff with a hash for fast reading! */ RsvgNode *newnode = NULL; if (!strcmp (name, "g")) newnode = rsvg_new_group (); else if (!strcmp (name, "a")) /*treat anchors as groups for now */ newnode = rsvg_new_group (); else if (!strcmp (name, "switch")) newnode = rsvg_new_switch (); else if (!strcmp (name, "defs")) newnode = rsvg_new_defs (); else if (!strcmp (name, "use")) newnode = rsvg_new_use (); else if (!strcmp (name, "path")) newnode = rsvg_new_path (); else if (!strcmp (name, "line")) newnode = rsvg_new_line (); else if (!strcmp (name, "rect")) newnode = rsvg_new_rect ();
... else if (!strcmp (name, "feBlend")) newnode = rsvg_new_filter_primitive_blend ();
... else { ... newnode = rsvg_new_group (); } if (newnode) { newnode->type = g_string_new (name); newnode->parent = ctx->priv->currentnode; rsvg_node_set_atts (newnode, ctx, atts); rsvg_defs_register_memory (ctx->priv->defs, newnode); if (ctx->priv->currentnode) { rsvg_node_group_pack (ctx->priv->currentnode, newnode); ctx->priv->currentnode = newnode; } else if (!strcmp (name, "svg")) { ctx->priv->treebase = newnode; ctx->priv->currentnode = newnode; } }
Jedem Element ist ein eigener Name zugeordnet. Der Code ruft zum Erzeugen der dazugehörigen RsvgNode-Struktur die passende Funktion “rsvg_new_Elementname()” auf. Bei diesen Elementen kann es sich auch um Filter-Funktionen handeln, die ebenfalls in Nodes abgelegt werden. Der Name von Filtern beginnt nach Librsvg-Konvention immer mit “fe”, wie das obige Code-Fragment es für den Blending-Filter “feBlend” zeigt. Sollte der Name der Bibliothek jedoch unbekannt sein, so wird mit “rsvg_new_group()” der Node ein “RsvgNodeGroup”-Objekt zugeordnet.
Am Ende der zahlreichen If-Abfragen sollte ein gültiges “newnode”-Objekt zur Verfügung stehen. Der letzte Teil der Funktion überprüft dies und setzt bei positivem Ausgang, die Einträge von “newnode”. Neben “parent” und diversen Attributen wird dabei auch das Feld “type” gesetzt, das einfach den Namen des Elements aufnimmt. Dieses Feld ist später im Code sehr wichtig, weil es dazu dient, das Verhalten bestimmter Elemente anhand des Typs zu identifzizieren, so dass beispielsweise ein Pfad anders behandelt wird als ein Rechteck und beide wieder anders als ein Filter.
Das Rendern von Filtern erledigt die Funktion “rsvg_filter_render()” in der Datei “rsvg-filter.c”, die die Nodes der Grafik durchläuft und nach Filtern sucht. Dabei macht sie sich zunutze, dass Filter, deren Name immer mit “fe” beginnen, auch im Type-Eintrag der Node diesen Namen führem. Die Funktion sucht also einfach nach Elementen, deren Type-Eintrag mit “fe” beginnt:
...
for (i = 0; i < self->super.children->len; i++) { current = g_ptr_array_index (self->super.children, i); if (!strncmp (current->super.type->str, "fe", 2)) rsvg_filter_primitive_render (current, ctx);
}
...
Falls der String-Vergleich via “strncmp()” bestätigt, dass Type die Zeichenkette “fe” enthält, wird die Funktion “rsvg_filter_primitive_render()” aufgerufen. Und hier liegt nun das Problem: was passiert, wenn ein Angreifer eine Datei zusammenstellt, die unbekannte Element-Namen enthält, die mit “fe” beginnen? Dann wird diesem Element mit “rsvg_new_group()” während der Initialisierung ein “RsvgNodeGroup”-Objekt zugeordnet, also kein Filter-Objekt. Trotzdem wird das Type-Feld auf den Namen des Elements gesetzt, womit es mit “fe” beginnt. Die Funktion “rsvg_filter_render()”-Funktion hält das Element aufgrund der Type-Eintrags für einen gültigen Filter und versucht diesen Filter zu rendern. Natürlich geht das schief. Im Detail greift die Funktion dann auf nicht initialisierte Speicherbereiche zu, wodurch es zu dem Segmentation Fault kommt.
Der Patch für diese Schwachstelle ist recht umfangreich, weil zahlreiche Teile des Codes geändert werden müssen, um das Schema der Element-Identifikation zu ändern. In der Librsvg-Version 2.34.1 ist das Problem behoben.

