Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
page_inject — CVE-2026-31431-killed page-cache exploit — esecuzione di codice nei container che condividono lo stesso layer dell'immagine | Kitploit
Strumenti/GitHubGitHub/sgkdev/page_inject
Analisi delle VulnerabilitàExploitPost-ExploitPenetration TestingRed TeamingEscape dal ContainerBinary Exploitation
GitHubsgkdev/page_inject

page_inject

CVE-2026-31431-killed page-cache exploit — esecuzione di codice nei container che condividono lo stesso layer dell'immagine

Vedi Repository
74143 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

page_inject - fuga tra container AF_ALG aead

Exploit cross-container della vulnerabilità aead AF_ALG -- passare da un container compromesso a ogni container fratello che condivide lo stesso layer dell'immagine libc.so.6.

Questa è una primitiva di fuga: viene eseguita dall'interno di un container non privilegiato che l'attaccante ha già compromesso, e usa il bug di scrittura arbitraria di 4 byte per rotazione ESN authencesn di AF_ALG (CVE-2026-31431) per piantare un hook persistente di read() nelle pagine della page cache di libc.so.6. Poiché Docker / containerd appoggia i file dei layer inferiori di overlayfs su inode condivisi, quelle pagine sono visibili a ogni container fratello istanziato dalla stessa immagine -- l'hook scatta anche nei loro processi, e l'attaccante ottiene l'esecuzione di comandi dentro ciascuno di essi.

Modello di minaccia

  • L'attaccante ha accesso shell a un singolo container (chiamiamolo victim) su un host che esegue altri container (siblings) dalla stessa immagine di victim.
  • victim gira con la postura Docker/k8s predefinita: uid non privilegiato dentro il namespace utente del container, profilo seccomp predefinito, profilo AppArmor predefinito, nessuna capability speciale, nessun bind mount dell'host.
  • victim ha solo:
    • accesso in lettura alla propria libc (/usr/lib/x86_64-linux-gnu/libc.so.6 o dove la distribuzione la installa)
    • la famiglia standard di syscall socket(AF_ALG, ...)
    • le syscall standard splice / vmsplice
    • accesso in scrittura a una directory su cui può fare chmod +x (es. /tmp)
  • Il kernel deve essere vulnerabile a CVE-2026-31431 (qualsiasi build algif_aead + authencesn precedente al fix di revert a monte).

Questo è tutto. Nessuna CAP_* speciale, nessun accesso al filesystem dell'host. L'attaccante inserisce un binario statico autosufficiente dentro il container, lo esegue, e la corruzione della page cache -- e quindi l'hook -- diventa visibile a ogni container fratello.

Come si compone l'exploit

  1. Identità della pagina nella page cache. Dentro un container overlayfs, /usr/lib/.../libc.so.6 è servito dall'inode ext4 del layer inferiore dell'immagine. Ogni container avviato dalla stessa immagine condivide quell'inode di supporto, e la page cache del kernel è indicizzata in base all'inode sottostante -- non dall'overlay né dal namespace. Quindi una singola scrittura di 4 byte in una pagina della page cache è visibile a tutti i processi dei container fratelli che hanno quella pagina in mmap.

  2. La vulnerabilità aead AF_ALG trasforma una tale scrittura in molte. algif_aead concatena l'iovec RX dell'utente con i byte finali authsize del SGL TX spliced, e la rotazione ESN di authencesn parcheggia 4 byte del campo seq_high dell'AAD in dst[assoclen + cryptlen] -- che è il primo byte di quella coda esterna concatenata. La pagina spliced è una pagina della page cache di un file su cui l'attaccante ha solo accesso in lettura, ma il cipher vi copia comunque dei byte, senza contabilità dirty. (Vedi crypto/algif_aead.c e crypto/authencesn.c per i meccanismi sottostanti.)

  3. Bootstrap di una primitiva invocabile. La prima cosa che fa page_inject è il bootstrap della -- una re-implementazione in assembly dello stesso giro AF_ALG (), collocata nella cave della libc. Questo rende la scrittura di 4 byte una regolare dall'interno di qualsiasi futuro payload hook, senza bisogno di setup socket a ogni chiamata.

Compilazione

L'iniettore viene compilato fuori dal container vittima -- tipicamente sulla macchina di sviluppo dell'attaccante -- perché la maggior parte delle immagini container di produzione non include un compilatore. Un ambiente di sviluppo Linux x86_64 standard con gcc (con supporto al link -static) e nasm è sufficiente.

root@kitploit:~
make            # assembles .asm sources via gen_arrays.sh, links static page_inject
make shellcode  # also produces inspectable .bin flat binaries
make clean      # removes generated files and the binary

L'output è un singolo ELF staticamente linkato (./page_inject) che gira su qualsiasi kernel Linux x86_64 moderno.

Consegna e utilizzo (dal container vittima)

Una volta che l'attaccante ha shell su victim, carica il binario in una directory scrivibile (tipicamente /tmp):

root@kitploit:~
# inside the compromised container, attacker session
victim$ ./page_inject

Senza argomenti, page_inject usa come predefinito /usr/lib/x86_64-linux-gnu/libc.so.6 (la posizione post-merge di Debian/Ubuntu). Per altre distribuzioni la libc è in un percorso diverso; passatela esplicitamente oppure usate --root / per scansionare la tabella di lookup integrata a partire dalla root del container:

root@kitploit:~
# Fedora / Rocky / CentOS
victim$ ./page_inject /usr/lib64/libc.so.6

# Arch
victim$ ./page_inject /usr/lib/libc.so.6

# Auto-detect, regardless of distro:
victim$ ./page_inject --root /

Ognuna delle due invocazioni fa la stessa cosa: parse ELF della libc nel container, installa l'hook nella sua page cache, monitora la tabella slot per ~30 s mentre i fratelli si registrano, ed esegue un id one-shot contro il primo fratello che si è registrato come controllo di sanità.

Dopo il bootstrap, si entra nella shell di comando per pilotare qualsiasi fratello registrato:

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 rimuove l'hook da ogni container fratello in un colpo solo e lascia che i figli hook si auto-terminino.

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

Doppio percorso di iniezione

Build glibc diverse lasciano quantità diverse di spazio cave .text tra il segmento LOAD eseguibile e il successivo LOAD di sola lettura. page_inject seleziona tra due layout al momento dell'iniezione:

  • Percorso A -- solo libc (predefinito). Sia la Zona C che la Zona A stanno nella cave .text della libc. Le aree tabella slot + CMD + OUTPUT stanno nella sezione .hash della libc -- dati hash SysV legacy che ld.so non legge a runtime perché usa .gnu.hash al loro posto. Quando .hash è assente (toolchain moderna di Arch), page_inject ricava la regione slot dalla coda di .eh_frame_hdr, dopo aver prima ridotto il campo fde_count così che l'unwinder non consideri più i byte liberati parte dell'indice di ricerca binaria FDE (l'unwinder ripiega in modo trasparente su una scansione lineare di .eh_frame per qualsiasi IP la cui FDE si trovava nella gamma troncata -- comportamento imposto da LSB).

  • Percorso B -- trampolino libc + payload ld.so. Alcune build glibc riducono la cave della libc al di sotto della dimensione necessaria per il payload completo Zona C + Zona A (Ubuntu 24.04 / glibc 2.39 include una cave da 711 B). In quel caso page_inject scrive un trampolino da 36 B nella cave della libc -- esegue il gate della chiave .bss del fast-path intra-libc -- e nel percorso lento calcola la base runtime di ld.so dallo slot GOT della libc per (un simbolo lato ld.so che ogni glibc importa privatamente) e salta in una variante con registro base della Zona C nella cave di . La tabella slot + CMD + OUTPUT + chiave restano tutte nella libc; la Zona C lato ld.so le raggiunge tramite dopo che il trampolino imposta .

Se nessuno dei due layout entra, page_inject rifiuta in modo pulito senza scrivere nulla su libc o ld.so né su disco né nella page cache.

Gestione del prologo di read()

Versioni glibc diverse emettono sequenze iniziali diverse in read(). L'iniettore riconosce ciascuna, rilegge i byte che l'hook sposta e li emula nel fast-path della Zona C, così che una read() single-threaded riprenda correttamente da read+N:

Lo spazio di emulazione del fast-path nella Zona C è dimensionato per il prologo conosciuto più lungo (8 byte) più il jmp rel32 a 5 byte; i prologhi più corti riempiono il byte finale dello spazio con un NOP di riempimento, così che la lunghezza totale dello spazio sia costante.

Struttura dei file

root@kitploit:~
page_inject/
  page_inject.c           Main injector: ELF parsing, vuln primitive,
                          dual-path layout selection, inject + unhook.
  zone_c.asm              Path-A hook dispatcher shellcode.
  zone_c_ld.asm           Path-B hook dispatcher (rbp-base variant).
  trampoline.asm          Path-B 36-byte libc-side stub.
  write_cache.asm         Zone A (vuln write primitive shellcode).
  gen_arrays.sh           Assemble .asm -> asm_bytecode.c.
  asm_bytecode.c          [generated] shellcode byte arrays.
  Makefile                Build system.

Matrice testata e supportata

L'exploit è stato verificato end-to-end sulle seguenti distribuzioni container. Per ogni voce, page_inject è stato iniettato dall'interno di un container e il suo hook è scattato in un container fratello avviato dalla stessa immagine; i comandi sono stati eseguiti correttamente tramite il canale della page cache; e unhook ha ripristinato in modo pulito lo stato delle pagine della libc.

Note operative

  • page_inject è staticamente linkato di proposito, così che il processo dell'attaccante stesso non sia influenzato dall'hook che installa.
  • page_inject riconosce una libc "già hookata" (E9 + nop al prologo di read()) e rifiuta di re-iniettare. Se vi trovate in un ambiente di test e la vostra page cache è bloccata in quello stato, fermate tutti i container che usano l'immagine e fate drop_caches per resettare.
Scarica lo strumento
Zona A
write_cache.asm
.text
call
  • Installazione dell'hook. L'iniettore poi scrive la Zona C (zone_c.asm) nella cave .text della libc e applica una patch ai primi 7-12 byte di read() con un salto E9 disp32 verso di essa. I byte spostati del prologo vengono emulati fedelmente nel fast-path della Zona C (vengono riconosciuti tre diversi prologhi glibc -- vedi "Gestione del prologo" sotto). L'hook ora è attivo nella page cache della libc.

  • Propagazione dell'hook. Ogni container fratello esegue processi che chiamano read() di continuo (demoni di log, healthcheck, cat /etc/hostname, qualsiasi cosa). Alla prima chiamata del genere dentro un container fratello, il prologo dirottato salta nella Zona C, che:

    • fa stat("/") dell'inode root del container (un ID stabile per namespace), usandolo come chiave di slot del container,
    • scansiona la tabella slot alla ricerca di una voce esistente con quella chiave,
    • se assente, registra la chiave e fa fork() di un longevo figlio a ciclo di comando che interroga l'area CMD alla ricerca di ordini,
    • torna a read()+N così il chiamante non sospetta nulla. Il processo originale del fratello continua a girare. Da questo momento l'attaccante ha un demone dentro quel container.
  • Canale di comando. L'attaccante usa lo stesso binario page_inject in modalità --shell per scrivere comandi nell'area CMD della regione slot. Il figlio hook di ogni container fratello registrato interroga, fa fork di /bin/sh -c <cmd>, cattura stdout/stderr nell'area OUTPUT, segnala il completamento e torna a interrogare. La shell mostra l'output. Poiché ogni scrittura CMD/OUTPUT passa anche attraverso la primitiva della vulnerabilità, non serve alcun privilegio speciale.

  • Rimozione dell'hook. Quando ha finito, unhook ripristina i byte originali del prologo di read() e azzera la tabella slot; i figli hook vedono uno slot vuoto alla loro iterazione successiva e si auto-terminano. Le modifiche alla page cache in sé sono pulite (il kernel non ha mai marcato dirty le pagine modificate), quindi una volta fermati tutti i container che hanno libc in mmap, un drop_caches ripristina completamente la cache -- non rimane alcun artefatto su disco.

  • _rtld_global
    .text
    ld.so
    .bss
    rbp + offset
    rbp = libc_base
    Intervallo glibcPrologo (dopo l'eventuale endbr64)Note
    2.36 / 2.39cmpb $0x0, __libc_single_threaded(%rip)7 byte; la cmpb emulata imposta ZF per l'jne .Lthreaded originale.
    2.43push rbp; movsxd rdi,edi; xor r9d,r9d7 byte; emulata byte per byte.
    2.31 / 2.35mov eax, fs:[0x18]8 byte; emulata byte per byte (il [disp32] con prefisso FS è assoluto, non RIP-relativo, quindi la copia byte per byte è fedele).
    ImmagineglibcPercorso di iniezionePrologo di read()Regione slot
    debian:bookworm2.36Acmpb.hash
    ubuntu:24.042.39Bcmpb.hash (lato libc, indirizzata via rbp da ld.so)
    ubuntu:22.042.35ATLS-fs.hash
    fedora:402.39Acmpb.hash
    archlinux:latest2.43Apush-rbp.eh_frame_hdr (coda troncata)