
CVE-2023-3269: Linux-Kernel Privilegieneskalations-Schwachstelle
(GitHub-CI-verifizierter Exploit)
Es wurde ein Fehler in der Handhabung der Stapelerweiterung im Linux-Kernel 6.1 bis 6.4 gefunden, auch bekannt als „Stack Rot“. Der Maple-Baum, der für die Verwaltung virtueller Speicherbereiche zuständig ist, kann Knotenersetzungen durchführen, ohne den MM-Schreib-Lock ordnungsgemäß zu erlangen, was zu Use-After-Free-Problemen führt. Ein nicht privilegierter lokaler Benutzer könnte diesen Fehler ausnutzen, um den Kernel zu kompromittieren und seine Rechte zu erweitern.
Da StackRot eine Schwachstelle im Linux-Kernel ist, die im Speicherverwaltungs-Subsystem gefunden wurde, betrifft sie fast alle Kernelkonfigurationen und erfordert nur minimale Fähigkeiten, um sie auszulösen. Es sei jedoch darauf hingewiesen, dass Maple-Knoten mittels RCU-Callbacks freigegeben werden, wodurch die tatsächliche Speicherfreigabe bis nach der RCU-Schonfrist verzögert wird. Folglich gilt die Ausnutzung dieser Schwachstelle als herausfordernd.
Nach meinem besten Wissen gibt es derzeit keine öffentlich verfügbaren Exploits, die auf Use-After-Free-by-RCU (UAFBR)-Fehler abzielen. Dies ist der erste Fall, bei dem nachgewiesen wurde, dass UAFBR-Fehler ausnutzbar sind, selbst ohne das Vorhandensein der Einstellungen CONFIG_PREEMPT oder CONFIG_SLAB_MERGE_DEFAULT. Bemerkenswerterweise wurde dieser Exploit erfolgreich in der Umgebung demonstriert, die von Google kCTF VRP (bzImage_upstream_6.1.25, config) bereitgestellt wurde.
Die StackRot-Schwachstelle ist im Linux-Kernel seit Version 6.1 vorhanden, als die VMA-Baumstruktur von Rot-Schwarz-Bäumen auf Maple-Bäume geändert wurde.
Immer wenn der Systemaufruf mmap() verwendet wird, um eine
Speicherabbildung einzurichten, erzeugt der Kernel eine Struktur namens
vm_area_struct, um den entsprechenden virtuellen Speicherbereich (VMA)
darzustellen. Diese Struktur speichert verschiedene Informationen, darunter
Flags, Eigenschaften und andere relevante Details zur Abbildung.```c
struct vm_area_struct {
long unsigned int vm_start; /* 0 8 /
long unsigned int vm_end; / 8 8 /
struct mm_struct * vm_mm; / 16 8 /
pgprot_t vm_page_prot; / 24 8 /
long unsigned int vm_flags; / 32 8 /
union {
struct {
struct rb_node rb attribute((aligned(8))); / 40 24 /
/ --- cacheline 1 boundary (64 bytes) --- /
long unsigned int rb_subtree_last; / 64 8 /
} attribute((aligned(8))) shared attribute((aligned(8))); / 40 32 /
struct anon_vma_name * anon_name; / 40 8 /
} attribute((aligned(8))); / 40 32 /
/ --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- /
struct list_head anon_vma_chain; / 72 16 /
struct anon_vma * anon_vma; / 88 8 /
const struct vm_operations_struct * vm_ops; / 96 8 /
long unsigned int vm_pgoff; / 104 8 /
struct file * vm_file; / 112 8 /
void * vm_private_data; / 120 8 /
/ --- cacheline 2 boundary (128 bytes) --- /
atomic_long_t swap_readahead_info; / 128 8 /
struct vm_userfaultfd_ctx vm_userfaultfd_ctx; / 136 0 */
/* size: 136, cachelines: 3, members: 14 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} attribute((aligned(8)));
Anschließend benötigt der Kernel bei Seitenfehlern oder anderen speicherbezogenen Systemaufrufen eine schnelle Suche der VMA allein anhand der Adresse. Zuvor wurden die VMAs mithilfe von Rot-Schwarz-Bäumen verwaltet. Ab Linux-Kernel Version 6.1 fand jedoch die Migration zu Maple Trees statt. [Maple Trees][mt] sind RCU-sichere B-Baum-Datenstrukturen, die für die Speicherung nicht überlappender Bereiche optimiert sind. Dennoch erhöht ihre komplexe Natur die Komplexität des Codebestands und führt die StackRot-Sicherheitslücke ein.
[mt]: https://docs.kernel.org/6.4/core-api/maple_tree.html
Im Kern besteht ein Maple Tree aus Maple Nodes. Obwohl die Struktur des Baums komplex sein kann, ist es wichtig zu beachten, dass diese Komplexität nichts mit dem StackRot-Fehler zu tun hat. Daher wird im gesamten Artikel angenommen, dass der Maple Tree nur aus einem einzigen Node besteht, nämlich dem Wurzelknoten.
Dieser Wurzelknoten kann bis zu 16 Intervalle enthalten. Diese Intervalle können entweder eine Lücke darstellen oder auf eine VMA verweisen. Da auch Lücken als Intervalle zählen, sind alle Intervalle sequentiell verbunden, sodass innerhalb der Knotenstruktur nur 15 Endpunkte, auch Pivots genannt, benötigt werden. Beachten Sie, dass der linkste Endpunkt und der rechtste Endpunkt weggelassen werden, da sie vom übergeordneten Knoten abgerufen werden können.```c
struct maple_range_64 {
struct maple_pnode * parent; /* 0 8 */
long unsigned int pivot[15]; /* 8 120 */
/* --- cacheline 2 boundary (128 bytes) --- */
union {
void * slot[16]; /* 128 128 */
struct {
void * pad[15]; /* 128 120 */
/* --- cacheline 3 boundary (192 bytes) was 56 bytes ago --- */
struct maple_metadata meta; /* 248 2 */
}; /* 128 128 */
}; /* 128 128 */