Skip to content
KitploitKITPLOIT
ToolsBlog
Einreichen
ToolsBlog
Einreichen

Hacking-, PenTest- und Cybersicherheits-Tools für Ihr Sicherheitsarsenal!

Kitploit ist ein Verzeichnis von Hacking-, Cybersicherheits- und Pentesting-Tools. Entdecken Sie die neuesten Projekt-Updates, um Schwachstellen zu finden, Systeme zu analysieren, Tests zu automatisieren und Ihre Sicherheit zu stärken.

··Feeds·Kontakt·Datenschutz·© 2026 Kitploit

Tool-Verzeichnis

Kategorien

Alle Kategorien anzeigen
Loading categories
page_inject — CVE-2026-31431-killed Page-Cache-Exploit — Code-Ausführung in Container, die dieselbe Image-Ebene teilen | Kitploit
Tools/GitHubGitHub/sgkdev/page_inject
SchwachstellenanalyseExploitationPost-ExploitationPenetrationstestsRed TeamingContainer-AusbruchBinary-Exploitation
GitHubsgkdev/page_inject

page_inject

CVE-2026-31431-killed Page-Cache-Exploit — Code-Ausführung in Container, die dieselbe Image-Ebene teilen

Repository anzeigen
74146vor 4 MonatenVon Kitploit geprüft

Beliebteste

Alle anzeigen →

Entdecken Sie die meistgenutzten Tools unserer Community.

Alle Tools erkunden

Durchsuchen Sie unsere Tool-Sammlung

Alle Tools anzeigen →
Teilen

page_inject - AF_ALG aead containerübergreifende Escape

AF_ALG aead-Schwachstellen-Exploit für Containerflucht – pivotiere von einem kompromittierten Container in jeden anderen Container, der dieselbe libc.so.6-Ebene des Images nutzt.

Hierbei handelt es sich um ein Escape-Primitiv: Es wird von innerhalb eines unprivilegierten Containers ausgeführt, den der Angreifer bereits kompromittiert hat, und nutzt den AF_ALG authencesn-ESN-Rotationsfehler (4-Byte-Arbitrary-Write, CVE-2026-31431), um einen persistenten read()-Hook in den Page-Cache-Seiten von libc.so.6 zu platzieren. Da Docker/containerd die unteren Layer von OverlayFS mit gemeinsamen Inodes bereitstellen, sind diese Seiten für jeden anderen Container sichtbar, der aus demselben Image erstellt wurde – der Hook feuert auch in deren Prozessen, und der Angreifer erhält Befehlsausführung in jedem dieser Container.

Bedrohungsmodell

  • Der Angreifer hat Shell-Zugriff auf einen einzelnen Container (nennen wir ihn victim) auf einem Host, der andere Container () aus dem wie ausführt.
siblings
gleichen Image
victim
  • victim läuft mit der Standard-Docker/k8s-Konfiguration: unprivilegierte UID innerhalb des Container-Benutzernamensraums, Standard-Seccomp-Profil, Standard-AppArmor-Profil, keine speziellen Capabilities, keine Host-Bind-Mounts.
  • victim hat nur:
    • Lesezugriff auf seine eigene libc (/usr/lib/x86_64-linux-gnu/libc.so.6 oder dort, wo die Distribution sie installiert)
    • den üblichen socket(AF_ALG, ...)-Syscall-Familie
    • die üblichen splice/vmsplice-Syscalls
    • Schreibzugriff auf ein Verzeichnis, das es mit chmod +x versehen kann (z. B. /tmp)
  • Der Kernel muss anfällig für CVE-2026-31431 sein (jegliche algif_aead + authencesn-Builds vor dem Upstream-Revert-Fix).
  • Das ist alles. Kein spezielles CAP_*, kein Zugriff auf das Host-Dateisystem. Der Angreifer lädt eine statisch gelinkte, in sich geschlossene Binärdatei in den Container, führt sie aus, und die Page-Cache-Korruption – und damit der Hook – wird für jeden anderen Container sichtbar.

    Wie die Exploit-Kette zusammenhängt

    1. Page-Cache-Seitenidentität. Innerhalb eines OverlayFS-Containers wird /usr/lib/.../libc.so.6 durch den ext4-Inode der unteren Image-Ebene bereitgestellt. Jeder Container, der aus demselben Image gestartet wird, teilt sich diesen zugrunde liegenden Inode, und der Page-Cache des Kernels ist durch den zugrunde liegenden Inode – nicht durch Overlay oder Namespace – indiziert. Ein einzelner 4-Byte-Schreibvorgang in eine Page-Cache-Seite ist daher für alle Prozesse in anderen Containern sichtbar, die diese Seite per mmap eingebunden haben.

    2. AF_ALG aead-Schwachstelle macht aus einem solchen Schreibvorgang viele. algif_aead verknüpft das RX-iov des Benutzers mit den nachfolgenden authsize-Bytes des gesplickten TX-SGL, und die ESN-Rotation von authencesn legt 4 Bytes des seq_high-Feldes des AAD an dst[assoclen + cryptlen] ab – also das erste Byte dieses verketteten fremden Endstücks. Die gesplickte Seite ist eine Page-Cache-Seite einer Datei, auf die der Angreifer nur Lesezugriff hat, aber die Chiffre kopiert trotzdem Bytes hinein, ohne Dirty-Buchführung. (Siehe crypto/algif_aead.c und crypto/authencesn.c für die zugrunde liegende Mechanik.)

    3. Aufbau eines aufrufbaren Primitivs. Als Erstes bootet page_inject Zone A – eine asm-codierte Neuimplementierung derselben AF_ALG-Übung (write_cache.asm), die in die .text-Höhle von libc eingefügt wird. Dadurch wird der 4-Byte-Schreibvorgang zu einem regulären call innerhalb jedes zukünftigen Hook-Payloads, ohne dass pro Aufruf ein Socket aufgebaut werden muss.

    4. Installation des Hooks. Der Injektor schreibt dann Zone C (zone_c.asm) in die .text-Höhle von libc und patcht die ersten 7–12 Bytes von read() mit einem E9 disp32-Sprung darauf. Die verdrängten Bytes des Prologs werden im Fast-Path von Zone C originalgetreu nachgebildet (drei verschiedene glibc-Prologe werden erkannt – siehe „Prologbehandlung" unten). Der Hook ist jetzt live im Page-Cache von libc.

    5. Ausbreitung des Hooks. Jeder andere Container führt Prozesse aus, die ständig read() aufrufen (Logging-Daemons, Healthchecks, cat /etc/hostname, usw.). Beim ersten solchen Aufruf in einem anderen Container springt der gekaperte Prolog in Zone C, welches:

      • stat("/") den Root-Inode des Containers (eine stabile, pro-Namespace-ID) und verwendet ihn als Slot-Schlüssel des Containers,
      • die Slottabelle nach einem vorhandenen Eintrag mit diesem Schlüssel durchsucht,
      • falls abwesend, den Schlüssel registriert und einen langlebigen Befehlsschleifen-Kindprozess forkt, der das CMD-Gebiet auf Anweisungen pollt,
      • zu read()+N zurückkehrt, damit der Aufrufer nichts merkt. Der ursprüngliche Prozess im anderen Container läuft weiter. Von nun an hat der Angreifer einen Daemon in diesem Container.
    6. Befehlskanal. Der Angreifer verwendet dasselbe page_inject-Binary im --shell-Modus, um Befehle in das CMD-Gebiet des Slotbereichs zu schreiben. Der Hook-Kindprozess jedes registrierten anderen Containers pollt, forkt /bin/sh -c <cmd>, erfasst stdout/stderr im OUTPUT-Gebiet, signalisiert Fertigstellung und kehrt zum Pollen zurück. Die Shell zeigt die Ausgabe. Da jeder CMD/OUTPUT-Schreibvorgang ebenfalls durch das Schwachstellenprimitiv läuft, ist keine besondere Berechtigung erforderlich.

    7. Unhook. Wenn fertig, stellt unhook die ursprünglichen Prolog-Bytes von read() wieder her und setzt die Slottabelle zurück; Hook-Kindprozesse sehen bei ihrem nächsten Durchlauf einen leeren Slot und beenden sich selbst. Die Page-Cache-Modifikationen selbst sind sauber (der Kernel hat die geänderten Seiten nie als dirty markiert), sodass sobald alle Container, die libc per mmap eingebunden haben, gestoppt sind, ein drop_caches den Cache vollständig zurücksetzt – es bleibt kein Artefakt auf der Festplatte zurück.

    Bauen

    Der Injektor wird außerhalb des Opfer-Containers gebaut – typischerweise auf dem eigenen Entwicklungsrechner des Angreifers –, da die meisten Produktionscontainer-Images keinen Compiler mitliefern. Eine Standard-Linux-x86_64-Entwicklungsumgebung mit gcc (mit -static-Link-Unterstützung) und nasm reicht aus.

    root@kitploit:~
    make            # assembliert .asm-Quellen via gen_arrays.sh, linkt statisches page_inject
    make shellcode  # erzeugt auch inspizierbare .bin-Flachbinaries
    make clean      # entfernt generierte Dateien und das Binary
    

    Die Ausgabe ist ein einzelnes statisch gelinktes ELF (./page_inject), das auf jedem modernen x86_64-Linux-Kernel läuft.

    Auslieferung und Verwendung (vom Opfer-Container aus)

    Sobald der Angreifer eine Shell auf victim hat, lädt er das Binary in ein beschreibbares Verzeichnis (üblicherweise /tmp):

    root@kitploit:~
    # innerhalb des kompromittierten Containers, Angreifer-Session
    victim$ ./page_inject
    

    Ohne Argumente verwendet page_inject standardmäßig /usr/lib/x86_64-linux-gnu/libc.so.6 (den Post-Merge-Pfad für Debian/Ubuntu). Bei anderen Distributionen befindet sich libc an einem anderen Pfad; entweder explizit angeben oder --root / verwenden, um die integrierte Lookup-Tabelle von der Container-Root aus zu durchsuchen:

    root@kitploit:~
    # Fedora / Rocky / CentOS
    victim$ ./page_inject /usr/lib64/libc.so.6
    
    # Arch
    victim$ ./page_inject /usr/lib/libc.so.6
    
    # Auto-Erkennung, unabhängig von der Distribution:
    victim$ ./page_inject --root /
    

    Jeder Aufruf bewirkt dasselbe: ELF-Parsing der libc im Container, Installation des Hooks in ihrem Page-Cache, Überwachung der Slottabelle für ca. 30 Sekunden, während sich andere Container registrieren, und einmaliges Ausführen von id gegen den ersten registrierten anderen Container als Korrektheitsprüfung.

    Nach dem Bootstrap kann man mit der Befehlsshell auf jeden registrierten anderen Container zugreifen:

    root@kitploit:~
    victim$ ./page_inject --shell --no-bootstrap
    === page-cache shell ===
    Containers (3):
      [0] 0x0018598d  <- target
      [1] 0x001859ab
      [2] 0x001859cd
    
    inject:0018598d> exec id
    uid=0(root) gid=0(root) groups=0(root)
    inject:0018598d> target 0x001859ab
    inject:001859ab> exec hostname
    1ccd66abee9d
    inject:001859ab> exec cat /etc/shadow
    root:$6$.....
    inject:001859ab> unhook
    ... read() prologue restored, slot table zeroed ...
    

    unhook entfernt den Hook in allen anderen Containern auf einen Schlag und lässt die Hook-Kindprozesse sich selbst beenden.

    root@kitploit:~
    Usage: page_inject [OPTIONS] [LIBC_PATH]
    
    Options:
      --root <prefix>   Auto-resolve libc.so.6 under <prefix> using the
                        built-in fixed-path lookup table. Inside the
                        victim container that's normally --root / .
      --shell [0xKEY]   Drop into interactive command shell after
                        injection. Optional KEY pre-selects the target.
      --no-bootstrap    Skip injection (shell-only; hook must already
                        be live in the page cache).
      --timeout SEC     Slot monitoring timeout in --shell mode
                        (default 30 s).
      --help, -h        Show help.
    
    Default libc (when no --root and no LIBC_PATH given):
      /usr/lib/x86_64-linux-gnu/libc.so.6
    

    Dualer Injektionspfad

    Verschiedene glibc-Builds lassen unterschiedlich viel Platz in der .text-Höhle zwischen dem ausführbaren LOAD-Segment und dem nächsten schreibgeschützten LOAD. page_inject wählt zur Injektionszeit zwischen zwei Layouts:

    • Pfad A – nur libc (Standard). Sowohl Zone C als auch Zone A befinden sich in der .text-Höhle von libc. Die Slottabelle + CMD + OUTPUT-Bereiche befinden sich im .hash-Abschnitt von libc – veraltete SysV-Hash-Daten, die ld.so zur Laufzeit nicht liest, da es stattdessen .gnu.hash verwendet. Wenn .hash fehlt (Archs moderne Toolchain), schneidet page_inject den Slot-Region aus dem Ende von .eh_frame_hdr heraus, nachdem es zuerst das fde_count-Feld verkleinert hat, sodass der Unwinder die freigegebenen Bytes nicht mehr als Teil des FDE-Binärsuchindex betrachtet (der Unwinder fällt transparent auf eine lineare Suche von .eh_frame für jede IP zurück, deren FDE früher im abgeschnittenen Bereich lag – LSB-vorgeschriebenes Verhalten).

    • Pfad B – libc-Trampolin + ld.so-Payload. Einige glibc-Builds verkleinern die libc-Höhle unter die Größe, die für das vollständige Zone-C + Zone-A-Payload erforderlich ist (Ubuntu 24.04 / glibc 2.39 liefert eine 711-Byte-Höhle). In diesem Fall schreibt page_inject ein 36-Byte-Trampolin in die libc-Höhle – es realisiert das Fast-Path-.bss-Schlüssel-Gate innerhalb von libc – und springt im langsamen Pfad auf die Laufzeitbasis von ld.so mittels libcs GOT-Slot für _rtld_global (ein ld.so-seitiges Symbol, das jede glibc privat importiert) und springt in eine Basisregister-Variante von Zone C in der .text-Höhle von ld.so. Die Slottabelle + CMD + OUTPUT + .bss-Schlüssel bleiben alle in libc; die ld.so-seitige Zone C erreicht sie durch rbp + offset, nachdem das Trampolin rbp = libc_base gesetzt hat.

    Wenn keines der Layouts passt, lehnt page_inject sauber ab, ohne etwas in libc oder ld.so auf der Festplatte oder im Page-Cache zu schreiben.

    read()-Prologbehandlung

    Verschiedene glibc-Versionen erzeugen unterschiedliche Eröffnungssequenzen in read(). Der Injektor erkennt jede, liest die Bytes zurück, die der Hook verdrängt, und bildet sie im Fast-Path von Zone C nach, sodass read() im Einzelthread-Modus korrekt bei read+N fortgesetzt wird:

    glibc-BereichProlog (nach optionalem endbr64)Hinweise
    2.36 / 2.39cmpb $0x0, __libc_single_threaded(%rip)7 Bytes; nachgebildeter cmpb setzt ZF für das ursprüngliche jne .Lthreaded.
    2.43push rbp; movsxd rdi,edi; xor r9d,r9d7 Bytes; Byte-für-Byte nachgebildet.
    2.31 / 2.35mov eax, fs:[0x18]8 Bytes; Byte-für-Byte nachgebildet (FS-präfigiertes [disp32] ist absolut, nicht RIP-relativ, daher ist die Byte-Kopie originalgetreu).

    Der Fast-Path-Emulationslot in Zone C ist für den längsten bekannten Prolog (8 Bytes) plus den 5-Byte-rel32-Sprung ausgelegt; kürzere Prologe füllen den nachfolgenden Slot-Byte mit einem NOP-Füller, sodass die Slot-Länge konstant ist.

    Dateistruktur

    root@kitploit:~
    page_inject/
      page_inject.c           Hauptinjektor: ELF-Parsing, Schwachstellenprimitiv,
                              Dualpfad-Layoutauswahl, Injizieren + Unhook.
      zone_c.asm              Hook-Dispatcher-Shellcode für Pfad A.
      zone_c_ld.asm           Hook-Dispatcher-Shellcode für Pfad B (rbp-Basisvariante).
      trampoline.asm          36-Byte-libc-seitiger Stub für Pfad B.
      write_cache.asm         Zone A (Schwachstellen-Schreibprimitiv-Shellcode).
      gen_arrays.sh           Assembliert .asm -> asm_bytecode.c.
      asm_bytecode.c          [generiert] Shellcode-Byte-Arrays.
      Makefile                Build-System.
    

    Getestete und unterstützte Matrix

    Der Exploit wurde Ende-zu-Ende auf den folgenden Container-Snapshot-Distributionen verifiziert. Bei jedem Eintrag wurde page_inject von innerhalb eines Containers injiziert, und der Hook hat in einem anderen Container ausgelöst, der aus demselben Image gestartet wurde; Befehle wurden korrekt über den Page-Cache-Kanal ausgeführt; und der Unhook hat den libc-Seitenzustand sauber wiederhergestellt.

    ImageglibcInjektionspfadread()-PrologSlot-Region
    debian:bookworm2.36Acmpb.hash
    ubuntu:24.042.39Bcmpb.hash (libc-seitig, über rbp von ld.so adressiert)
    ubuntu:22.042.35ATLS-fs.hash
    fedora:402.39Acmpb.hash
    archlinux:latest2.43Apush-rbp.eh_frame_hdr (verkürztes Ende)

    Betriebshinweise

    • page_inject ist absichtlich statisch gelinkt, damit der eigene Prozess des Angreifers nicht von dem von ihm installierten Hook beeinflusst wird.
    • page_inject erkennt eine „bereits gehookte" libc (E9 + NOPs am read()-Prolog) und weigert sich, erneut zu injizieren. Wenn Sie sich in einer Testumgebung befinden und Ihr Page-Cache in diesem Zustand feststeckt, stoppen Sie alle Container, die das Image verwenden, und setzen Sie mit drop_caches zurück.
    Tool herunterladen