
Eine Exploit-Primitive im Linux-Kernel, inspiriert von DirtyPipe
An exploit primitive in linux kernel inspired by DirtyPipe (CVE-2022-0847).
Vor einiger Zeit habe ich, wie viele erfahrene Sicherheitsforscher vor mir, die DirtyPipe-Schwachstelle (CVE-2022-0847) studiert und reproduziert. Dabei wurde mir tief bewusst, wie nützlich diese Schwachstelle ist. Sie beginnt mit einem Speicher-Initialisierungsfehler und endet mit der Modifikation beliebiger Dateien – ganz ohne KASLR-Leak oder ROP-, JOP-Operationen unterwegs. Daher sind auch keine Umgehungen von SMEP, SMAP usw. erforderlich.
Nach der Reproduktion begann ich darüber nachzudenken: Warum ist DirtyPipe so nützlich? Wird diese Schwachstelle mit ihrer Behebung nicht nur eine Eintagsfliege sein, ohne jeglichen Lernwert für zukünftige Exploit-Entwicklungen?
Plötzlich wurde mir klar, dass die Struktur hinter DirtyPipe – struct pipe_buffer – mir sehr bekannt vorkam. Ah! Ist das nicht genau die Struktur, die mit dem GFP_KERNEL_ACCOUNT-Flag allokiert wird, in slab-1k liegt, und ein ops-Feld enthält, das ich oft für KASLR-Leaks und RIP-Hijacking verwendet habe?
Sofort fühlte ich mich wie ein Narr. Warum habe ich damals überhaupt das ops-Feld von pipe_buffer für ROP verändert? Wenn ich direkt das flags-Feld von pipe_buffer ändere und es mit splice kombiniere, kann ich doch direkt beliebige Dateien beschreiben! So müsste ich weder KASLR leaken, noch den Exploit an verschiedene Kernel-Versionen anpassen, mich um Gadgets kümmern oder SMEP, SMAP, KPTI usw. umgehen.
Ich holte sofort meine früheren Exploits hervor, mit denen ich CVE-2021-22555 und andere Exploits rund um die pipe_buffer-Struktur reproduziert und gelernt hatte, änderte sie leicht und konnte sie in ungepatchten verschiedenen Kernel-Versionen erfolgreich ausnutzen. Hier sind ein paar Beispiele:
Da ich zuvor niemanden gesehen habe, der dies als Primitive in Linux-Kernel-Exploits verwendet, habe ich mir erlaubt, es selbst als „Pipe Primitive“ zu bezeichnen XD.
Zusammengefasst benötigt man also die Fähigkeit, die Pipe-Struktur zu modifizieren, z. B. durch eine UAF unter slab-1k oder einen außerhalb der Reihenfolge liegenden Out-of-Bounds-Write (also nicht direkt sequenzielles Overflow) unter slab-1k.
So muss im Kernel >= 5.8 lediglich flag |= PIPE_BUF_FLAG_CAN_MERGE der Splice-Seite in pipe_buffer gesetzt werden (wenn möglich, kann man offset und len auch auf 0 setzen, um vom Dateianfang an zu schreiben); im Kernel < 5.8 muss zuerst anon_pipe_ops in pipe_buffer geleakt werden und dann das ops der Splice-Seite auf anon_pipe_ops geändert werden (da in Versionen < 5.8 das Merge-Verhalten vom ops abhängt) (wenn möglich, kann man offset und len ebenfalls auf 0 setzen).
Was die Frage angeht, wie man damit Container-Escape macht? Ich denke, was ich tun würde, wäre ähnlich wie bei CVE-2019-5736 XD.