Zweimal Integer Overflow in Linux-Kernel

Im Linux-Kernel sind kürzlich zwei Integer-Overflow-Schwachstellen entdeckt worden. Diese haben zur Folge, dass entfernte und lokale Angreifer Befehle mit Kernel-Rechten ausführen können.

Erster Integer Overflow: XFS-ACL-Zähler

Wie Xi Wang in seinem Blog berichtet, findet sich ein Integer Overflow in der Implementierung des XFS-Dateisystems. Eine Attacke ist über das Mounten möglich, wobei der Angreifer beispielsweise USB-Flashdrives oder Netzwerk-Mounts einsetzen kann. Diese Sicherheitslücke hat sich durch den Commit ef14f0c1 im Juli 2009 in den Kernel eingeschlichen. Der Programmierfehler befindet sich in der Funktion “xfs_acl_from_disk()”:

STATIC struct posix_acl *xfs_acl_from_disk(struct xfs_acl *aclp)
{ struct posix_acl_entry *acl_e; struct posix_acl *acl; struct xfs_acl_entry *ace; int count, i; count = be32_to_cpu(aclp->acl_cnt); acl = posix_acl_alloc(count, GFP_KERNEL); if (!acl) return ERR_PTR(-ENOMEM); for (i = 0; i < count; i++) { acl_e = &acl->a_entries[i]; ace = &aclp->acl_entry[i]; ... acl_e->e_tag = be32_to_cpu(ace->ae_tag); acl_e->e_perm = be16_to_cpu(ace->ae_perm); ... } return acl; ...
}

Auffällig ist hier, dass der Speicherallokationsfunktion “posix_acl_alloc()” der “count”-Zähler ohne vorherige Bereichskontrolle übergeben wird. Count selbst wird aus “aclp->acl_cnt” extrahiert, was allerdings direkt von dem Dateisystem gelesen wird. Das heißt, ein potenzieller Angreifer hat somit Kontrolle über die “count”-Variable. Dies ist kritisch, denn die Funktion “posix_acl_alloc()” ist wie folgt implementiert:

struct posix_acl *posix_acl_alloc(int count, gfp_t flags)
{ const size_t size = sizeof(struct posix_acl) + count * sizeof(struct posix_acl_entry); struct posix_acl *acl = kmalloc(size, flags); if (acl) posix_acl_init(acl, count); return acl;
}

Der Code setzt “kmalloc()” ein, um Kernelspeicher zu allozieren. Ist “count” groß genug, so kommt es zu einem Integer-Überlauf und der allozierte Speicher ist zu klein, was im weiteren zu einem Heap Buffer Overflow führt, der sich für eine Attacke ausnutzen lässt.

Ein späterer Git-Commit versuchte diese offensichtliche Schwachstelle durch das Hinzufügen der Zeilen

 count = be32_to_cpu(aclp->acl_cnt);
+ if (count > XFS_ACL_MAX_ENTRIES)
+ return ERR_PTR(-EFSCORRUPTED);

zu schließen, die “count” durch “XFS_ACL_MAX_ENTRIES” nach oben beschränkten. Diese Kontrolle würde funktionieren, wäre “count” nicht signed, sondern unsigned. Ein Angreifer kann diesen Check deshalb mit einem negativen Wert umgehen. Ein weiteres, triviales aber wirkungsvolles Patch löst dieses Problem:

- int count, i;
+ unsigned int count, i;

Der Fehler wurde Mitte Dezember 2011 im Git-Repository des Kernels behoben.

Zweiter Integer Overflow: Direct Rendering Manager (DRM):

Ein anderer Integer Overflow ist im Direct Rendering Manager (DRM) des Kernels aufgetaucht. Die Schwachstelle tritt in der Funktion “drm_mode_dirtyfb_ioctl()” auf:

if (num_clips && clips_ptr) { clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); ...

Hierbei ist “num_clips” eine benutzerdefinierte Variable, über die ein Agreifer also Kontrolle hat. Damit kann er für “clips” einen Puffer mit einer falschen Größe allozieren, indem er einen Integer Overflow erzeugt. Ein lokaler Angreifer kann dadurch Befehle mit Kernel-Rechten ausführen. Diese Fehler ist ebenfalls leicht zu korrigieren:

if (num_clips && clips_ptr) { if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { ret = -EINVAL; goto out_err1; } clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); ...

Dabei ist “DRM_MODE_FB_DIRTY_MAX_CLIPS” in “drm/drm_mode.h” mit 256 definiert ist. Dieses Patch ging Ende November 2011 in die Kernel-Quellen ein.

Nach oben