Skip to content

kernelCTF exp521: CVE-2025-40019 essiv on mitigation-v4-6.12#382

Open
AshmitSh4rma wants to merge 9 commits into
google:masterfrom
AshmitSh4rma:kernelctf-exp521-CVE-2025-40019-mit4
Open

kernelCTF exp521: CVE-2025-40019 essiv on mitigation-v4-6.12#382
AshmitSh4rma wants to merge 9 commits into
google:masterfrom
AshmitSh4rma:kernelctf-exp521-CVE-2025-40019-mit4

Conversation

@AshmitSh4rma
Copy link
Copy Markdown

Submission for exp521.

Layout

  • docs/vulnerability.md — bug root cause + introducing/fixing commits
  • docs/exploit.md — full 8-step exploit walkthrough (essiv ssize underflow → chained-SGL crafted reclaim → PTE remap → core_pattern overwrite → root via usermodehelper + pidfd stdio theft)
  • exploit/mitigation-v4-6.12/exploit.c — single-file source (~620 lines)
  • exploit/mitigation-v4-6.12/tinycrypto.h — inline AES-256-ECB + SHA-256 (drops OpenSSL runtime dependency)
  • exploit/mitigation-v4-6.12/Makefilemake exploit builds; make run notes
  • exploit/mitigation-v4-6.12/exploit — the binary that captured the flag
  • original.tar.gz — matches the SHA256 submitted via the Google Form
  • metadata.json — schema v3

Reachability

Triggerable from any unprivileged UID with no caps and no user namespaces. Required configs: CONFIG_CRYPTO_USER_API_AEAD=y, CONFIG_CRYPTO_ESSIV=y (both =y in the kCTF mitigation-v4 image). Compatible with all of the kCTF mitigation hardening (max_user_namespaces=1, io_uring_disabled=2, unprivileged_bpf_disabled=2).

Adds a 1-day submission porting the published CVE-2025-40019 essiv
ssize-underflow technique to the mitigation-v4-6.12 target (vanilla
v6.12.0 + jannh mitigations).

- Flag captured live 2026-05-13 13:33Z (exp521 on the public spreadsheet).
- Per-target symbol offsets re-derived from the mit-v4-6.12 vmlinux.
- Inline AES-256-ECB + SHA-256 (tinycrypto.h) to drop the OpenSSL
  runtime dependency.
@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 13, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

- affected_versions: use '6.0 - 6.12.82' (range form per schema regex)
- requirements.attack_surface: empty (only 'userns'|'io_uring' enum allowed,
  this exploit uses neither)
- exploits.mitigation-v4-6.12.uses: empty for same reason

Free-text bug-class / primitive descriptions live in docs/exploit.md, not
in metadata.json's tag arrays.
@AshmitSh4rma
Copy link
Copy Markdown
Author

The structure_check is currently failing on:

The CVE on the public spreadsheet for submission exp521 is `` but the PR is for CVE-2025-40019.

The CVE column on the public spreadsheet for exp521 is currently blank, but the same patch commit (6bb73db6948c2de23e407fe1b7ef94bf02b7529f) has been auto-resolved to CVE-2025-40019 on five prior submissions for this same target (exp478, exp501, exp502, exp504, exp505). It looks like the auto-resolver hasn't fired for the exp521 row yet — last_modification matches the original submission time, and the patch_title column is also empty.

There is no user-facing CVE field on the Google submission form, so I can't populate it from my side. Could a reviewer kick the auto-resolver, or fill the patch_title / CVE columns manually so the PR check can proceed?

The PR metadata correctly identifies the bug as CVE-2025-40019 via metadata.json. Schema-validation errors from the first run have been fixed in commit cb836aa.

@AshmitSh4rma AshmitSh4rma force-pushed the kernelctf-exp521-CVE-2025-40019-mit4 branch from cb836aa to d992fae Compare May 13, 2026 15:36
@AshmitSh4rma
Copy link
Copy Markdown
Author

@googlebot I have signed the CLA

The CI workflow's debug rewrite injects -g via 'sed -E s/gcc(\s)/gcc -g\1/g',
which requires gcc to be followed by whitespace. With 'CC = gcc' at EOL the
sed never matched, so the resulting binary had no DWARF and the workflow's
'file exploit_debug | grep debug_info' check failed. Inlining gcc directly
in the recipe matches the convention used by every other kernelctf submission.

Also gate 'strip $@' on the target name so the workflow's renamed
'exploit_debug:' target keeps its DWARF; the original 'exploit' target
still strips as before, so the release binary is unchanged.
The vuln-verify harness (kernelctf/vuln-verify/run_exploit.sh) invokes
the exploit as '/exp.sh --vuln-trigger', expecting the exploit to fire
just the underlying CVE primitive on a KASAN build so the verifier can
confirm the supplied patch_commit actually closes the bug.

Previously argv[1]='--vuln-trigger' fell through into the existing
'argc>1 → root_payload(argv[1])' path (which exists for core_pattern
re-entry with a PID).  strtoull("--vuln-trigger")=0, pidfd_open(0)
failed, /flag was unreadable, exploit exited cleanly — so all five
verify probes reported pwned=False and PR_VERIFY produced UNKNOWN.

The new vuln_trigger() does the minimal essiv_aead_crypt(enc=0) fire
(assoclen=0 + 16-byte IV) and exits, skipping KASLR / spray / fork.
On the unpatched KASAN kernel scatterwalk_map_and_copy walks OOB and
KASAN reports it (kasan.fault=panic ends the VM, log carries 'KASAN:').
On the patched kernel the new bounds check returns EINVAL, exp.sh
falls off the end, init-death panics the VM cleanly — pwned=False.

See rules.md §384 for the harness contract.
reqFilesPerExploit in kernelctf/check-submission.py requires the
compiled 'exploit' binary alongside Makefile and exploit.c. Rebuilt
from current sources (now containing --vuln-trigger handler).
Rename exploit.c → exploit.cpp, link -lkernelXDK, switch CC to g++, drop
the #ifdef LTS/MIT/COS/MIT4_612 ladder.  metadata.json now declares
"uses": ["kernelXDK"].

Symbol resolution:
- TargetDb("target_db.kxdb", target_db) with INCBIN fallback for the
  bundled KXDB, identical pattern to the libxdk samples (exp111 etc.).
- AutoDetectTarget() with try/catch + fallback to
  GetTarget("kernelctf", "mitigation-v4-6.12") so production targets
  whose /proc/version may have drifted still resolve correctly.
- core_pattern and __brk_base aren't in the v0.1 KXDB's curated symbol
  set, so we target.AddSymbol() them locally with the per-target offsets
  that used to live in the #ifdef ladder.  Future targets get one-line
  AddSymbol additions.

vuln_trigger() runs BEFORE the libxdk init.  The KASAN kernels in
vuln-verify have a /proc/version not in the KXDB; deferring the
TargetDb instantiation keeps the working before:True / after:False
behavior we just got green.

Local verification done with the v0.1 release tarball (-I/-L mirroring
the workflow's install layout):
- release build clean
- ./exploit --vuln-trigger → EC=0 (sendmsg EINVAL on patched host)
- bare ./exploit reaches compute_iv + setup_sacrificial_aead via the
  AutoDetect-fail fallback path
- workflow's 6 debug-rewrite seds applied + actual debug build →
  file exploit_debug reports 'with debug_info, not stripped'

target_db.kxdb is a Makefile build dependency (wget from
storage.googleapis.com/kernelxdk/db/kernelctf.kxdb), matching the
exp111 convention; not committed.
The 'uses' field is a runtime-kernel-feature enum, not a libxdk/kernelXDK
signal.  kernelXDK adoption is signaled purely by the .cpp extension and
the -lkernelXDK linkage; there is no metadata field for it (rules.md
§284–298 doesn't mention one, and the v3 schema's enum confirms).

Previous commit (db4dd89) put 'kernelXDK' here and broke structure_check
schema validation:
  '<root>.exploits[mitigation-v4-6.12].uses[0]': 'kernelXDK' is not one
  of ['userns', 'io_uring'].
@matrizzo matrizzo self-assigned this May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants