This submission targets mitigation-v4-6.12 as a novelty-only follow-up for CVE-2025-40019. The regular vulnerability slot for this CVE and target was already taken; the relevant new part is the page-table adaptation described in docs/novel-techniques.md.
The exploit turns the ESSIV scatterwalk offset underflow into a 16-byte write through a reclaimed scatterlist entry.
- Create a sacrificial AF_ALG AEAD request that builds a chained receive scatterlist.
- Free that request so the inline first receive SGL, a second receive SGL, and the tag SGL remain as residual slab contents.
- Reclaim the second receive SGL slab slot with a Unix socket control-buffer allocation containing a crafted scatterlist entry.
- Reclaim the freed anonymous pipe pages as user page-table pages.
- Trigger the ESSIV decryption path with
assoclen == 0and an output length of zero, causingscatterwalk_ffwd()to walk the residual SGL chain and write the encrypted IV into a page-table page. - Use a two-pass flow: pass 1 leaks the physical base needed for the target mapping, and pass 2 writes a coredump helper into
core_pattern.
The exploit does not use user namespaces, io_uring, BPF, or a separate KASLR leak service.
The trigger sends exactly 32 bytes to essiv(authenc(hmac(sha256),cbc(aes)),sha256). For this transform, the authentication tag size is 32 bytes. During decrypt, _aead_recvmsg() computes:
outlen = used - authsize = 0x20 - 0x20 = 0
Because outlen is zero, af_alg_get_rsgl() does not initialize the receive SGL. The request nevertheless passes the receive SGL to ESSIV as both source and destination. The vulnerable ESSIV offset calculation wraps to 0xfffffff0, so scatterwalk_ffwd() walks past the inline SGL and follows stale chain entries.
The sacrificial request constructs the stale chain before the trigger request:
first_rsgl[0..15] -> second_rsgl -> tsgl -> anonymous pipe pages
After the sacrificial request is freed, the exploit reclaims the second_rsgl allocation with a Unix socket msg_control buffer. The crafted entry supplies a large length value that steers the final scatterwalk_ffwd() position into the stale tsgl entries. The tsgl entries still encode pages that were freed after pipe closure; those pages are then reclaimed as page-table pages by a controlled mmap() spray.
ESSIV encrypts the IV before copying it back. The exploit embeds AES code so it can precompute an IV that decrypts to the desired 16-byte page-table write for the selected pass.
In pass 1, the write maps a physical window that contains a known kernel trampoline page. Reading through the resulting huge mapping reveals enough physical address information to derive the _stext physical base used by pass 2.
In pass 2, the write maps the 1 GB physical window containing core_pattern and writes the helper payload through the corrupted user mapping.
The payload written into core_pattern is:
|/proc/%P/root/tmp/ex %P
The /proc/%P/root prefix is required because the coredump helper path is resolved outside the process's jail-local mount namespace. The helper reopens the crashing process's standard file descriptors with pidfd_open() and pidfd_getfd(), then reads the flag from the target VM as root.
Build and run on mitigation-v4-6.12:
make
./exploitFor the vulnerability-only KASAN check:
./exploit --vuln-trigger