Skip to content

Repository files navigation

page_inject

Linux page-cache injector for cross-container escape — pivot from one compromised container into every sibling that shares the same libc image layer. Multiple kernel write primitives: AF_ALG (CVE-2026-31431), ESP fragment (CVE-2026-43284), RxRPC (CVE-2026-43500), io_uring (CVE-2026-43494).

page_inject exploit

How it works

Runs inside an unprivileged container. Uses the AF_ALG authencesn ESN-rotation 4-byte arbitrary-write bug to plant a persistent read() hook in the page-cache pages of libc.so.6. Because Docker/containerd back overlayfs lower-layer files with shared inodes, those pages are visible to every sibling container from the same image — the hook fires in their processes too.

Threat model:

  • Attacker has shell in one container, unprivileged uid, default seccomp, default AppArmor, no capabilities, no host mounts
  • Only needs: read access to libc, socket(AF_ALG), splice/vmsplice, a writable /tmp
  • Kernel must be vulnerable to CVE-2026-31431 (any algif_aead + authencesn build prior to upstream fix)

Exploitation chain:

  1. Overlayfs containers share backing inode → page-cache write visible to all siblings
  2. AF_ALG authencesn ESN rotation parks 4 bytes into a spliced page-cache page (the vuln)
  3. Bootstrap Zone A — callable write primitive in libc's .text cave
  4. Install Zone C — read() hook (prologue displacement + container registration + command loop)
  5. Patch read() with JMP → hook fires in every sibling on next read() call
  6. Command channel via page-cache CMD/OUTPUT areas — no network needed
  7. Unhook restores prologue, zeros slots, hook children self-terminate

Warning: crash risk

This tool modifies the page cache of a shared libc. Incorrect usage or unexpected termination can leave the hook in a broken state, causing all containers sharing that libc to crash (SIGSEGV on any read() call).

Known crash scenarios:

  • Killing page_inject mid-injection (partial hook write → invalid instruction at read() entry)
  • Node memory pressure evicting hook pages before unhook completes
  • Running against a libc with insufficient .text cave space (tool detects this and refuses, but edge cases exist)

Recovery from a crashed state:

# On the affected node (requires host access):
echo 3 > /proc/sys/vm/drop_caches
# Then restart affected containers

If drop_caches is not possible, a node reboot clears all page cache state.

Safe usage guidelines

  1. Always test in an isolated environment first. Never run against production without prior validation on an identical staging setup.
  2. Ensure you can access the node. If the hook breaks libc, you need host-level access to run drop_caches. Without it, affected containers are unrecoverable until node restart.
  3. Run unhook before exiting. The interactive shell's unhook command restores read() and lets hook children self-terminate cleanly.
  4. Do not run multiple instances concurrently. Two injectors targeting the same libc will corrupt each other's hook state.
  5. Check cave size before injection. The tool checks automatically, but verify with --dry-run (if available) on unfamiliar glibc builds.
  6. Scope of impact: the hook affects ALL containers on the same node using the same base image. This is by design but means collateral damage is unavoidable in multi-tenant environments.

Write backends

Backend CVE Granularity Collateral Speed
AF_ALG (default) CVE-2026-31431 4 bytes 0 ~10ms/4B
Dirty Frag CVE-2026-43284 4 bytes 0 ~100ms/4B
RxRPC/rxkad CVE-2026-43500 1 byte 0 ~50ms/B
PinTheft CVE-2026-43494 4096 bytes 0 ~500ms/page

Building

make            # assembles .asm sources via gen_arrays.sh, links static page_inject
make shellcode  # produces inspectable .bin flat binaries
make clean      # removes generated files

Requires: gcc (with -static support), nasm, make, hexdump. Output: single statically-linked ELF (~870KB).

Usage

# Inject hook (default AF_ALG backend)
./page_inject /usr/lib/x86_64-linux-gnu/libc.so.6

# Alternative backends
./page_inject --rxrpc /usr/lib/x86_64-linux-gnu/libc.so.6
./page_inject --dirty-frag /usr/lib/x86_64-linux-gnu/libc.so.6
./page_inject --pintheft /usr/lib/x86_64-linux-gnu/libc.so.6

# Auto-detect libc path
./page_inject --root /

# Interactive shell (after injection)
./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 cat /etc/shadow
root:$6$.....
inject:001859ab> unhook
... read() prologue restored, slot table zeroed ...

Dual injection path

  • Path A (default): Zone C + Zone A in libc's .text cave. Slot/CMD/OUTPUT in .hash section.
  • Path B: 36B trampoline in libc → full payload in ld.so's .text cave. For glibc builds with small libc caves (Ubuntu 24.04 / glibc 2.39).

If neither fits, refuses cleanly without writing anything.

Compatibility

Image glibc Path Prologue Slot region
debian:bookworm 2.36 A cmpb .hash
ubuntu:24.04 2.39 B cmpb .hash (via rbp)
ubuntu:22.04 2.35 A TLS-fs .hash
fedora:40 2.39 A cmpb .hash
archlinux:latest 2.43 A push-rbp .eh_frame_hdr

Credits

  • Original: sgkdev/page_inject by Massimiliano Oldani
  • CVE-2026-31431 discovery and the AF_ALG authencesn exploit technique
  • This fork: additional write backends, Kubernetes-focused testing

License

MIT. See LICENSE.

The original sgkdev/page_inject does not declare a license at time of forking. The MIT license applies to modifications and additions in this repository.

Disclaimer

This tool is for authorized security testing and research only. Unauthorized use against systems you do not own or have explicit written permission to test is illegal and may result in criminal prosecution.

About

Linux page-cache injector for cross-container escape. Multiple kernel write primitives

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages