CVE-2026-52831: Verified Repro With Script Download
CVE-2026-52831: Nuclio cron trigger shell command injection leading to RCE
CVE-2026-52831 is verified against github.com/nuclio/nuclio · go. Affected versions: <= 1.15.27 (all versions before 0.0.0-20260601075854-3356b86a8bfa). Vulnerability class: RCE. This critical reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00283.
What Is CVE-2026-52831?
CVE-2026-52831 is a critical OS command injection vulnerability (CWE-78) in the Nuclio Kubernetes controller's cron trigger generation, allowing arbitrary shell command execution in the cluster. Pruva reproduced it (reproduction REPRO-2026-00283).
CVE-2026-52831 Severity
CVE-2026-52831 is rated critical severity.
Critical — the most severe class — typically remotely exploitable with severe impact. Treat as an emergency.
Affected github.com/nuclio/nuclio Versions
github.com/nuclio/nuclio · go versions <= 1.15.27 (all versions before 0.0.0-20260601075854-3356b86a8bfa) are affected.
How to Reproduce CVE-2026-52831
pruva-verify REPRO-2026-00283 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00283/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-52831
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
NuclioFunction cron trigger event.headers key containing shell metacharacters
- NuclioFunction CR
- controller reconciliation
- Kubernetes CronJob pod /bin/sh -c curl args
reproduction_steps.sh No fixed-version bypass or materially distinct same-root-cause variant was confirmed for Nuclio cron-trigger shell command injection. The fixed commit removes the shell sink from the centralized Kubernetes CronJob generation path and uses exec-form curl with --data-raw.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-52831
Nuclio's Kubernetes controller generated Kubernetes CronJob containers for cron triggers by concatenating attacker-controlled cron event data into a single shell command and storing it as Args: ["/bin/sh", "-c", curlCommand]. A malicious event.headers key containing an unescaped double quote and shell separators breaks out of the intended curl --header "key: value" quoting and appends arbitrary shell commands. In this run, a real Nuclio controller binary processed a real NuclioFunction Kubernetes custom resource, generated a real Kubernetes CronJob, and a CronJob-derived pod executed attacker-controlled echo and id commands.
- Package/component affected: Nuclio Kubernetes controller, specifically Kubernetes CronJob-based cron trigger generation in
pkg/platform/kube/functionres/lazy.go. - Affected versions: Nuclio versions at or before the vulnerable parent of fixed commit
3356b86a8bfab3f960aa420310ebff765df9dede; the ticket states Nuclio 1.15.27 and earlier are affected. - Risk level and consequences: Critical. A user who can create or update a Nuclio function with a malicious cron trigger can cause the cluster to create a CronJob whose pod runs arbitrary shell commands in the CronJob container context. This is product-path remote/API-triggered command execution via the Kubernetes/Nuclio API boundary.
Impact Parity
- Disclosed/claimed maximum impact: Code execution / RCE through Nuclio cron trigger event headers or body in Kubernetes CronJob-based cron trigger processing.
- Reproduced impact from this run: Code execution in real Kubernetes CronJob pods generated by the Nuclio controller. The vulnerable pod logs include the attacker marker
CVE_PRODUCT_RCEandidoutput (uid=100(curl_user) ...). - Parity:
full - Not demonstrated: No attempt was made to chain from CronJob-container command execution to broader cluster privilege escalation. The reproduced impact matches the claimed command execution at the affected product boundary.
Root Cause
In the vulnerable code path, Nuclio decoded cron trigger attributes from a NuclioFunction resource and built a shell command string for invoking the function:
- User-controlled headers were formatted as
--header "<headerKey>: <headerValue>"and appended into one string. - The final curl invocation was embedded in
Args: []string{"/bin/sh", "-c", curlCommand}for the CronJob container. - Because header keys were not escaped for shell context, a key such as
X-Exploit"; echo CVE_PRODUCT_RCE; id; echo "terminates the intended quoted header and injects shell commands. - The vulnerable parent commit used
/bin/sh -c; the fixed commit switches to exec-form invocation,Command: []string{"curl"}, passes each curl argument as a separate argv entry, and uses--data-rawfor the body so no shell interprets attacker data.
The fixed commit used for negative control is 3356b86a8bfab3f960aa420310ebff765df9dede ([Security] Fix cron trigger shell injection). The reproduction script checks out and builds 3356b86a8bfa^ for the vulnerable path and 3356b86a8bfa for the fixed path, and verifies the relevant patch hunk before running.
Reproduction Steps
- Use
bundle/repro/reproduction_steps.sh. - The script:
- Reuses the prepared Nuclio repository cache when available.
- Resolves the vulnerable commit as
3356b86a8bfa^and the fixed commit as3356b86a8bfa. - Builds the real Nuclio
cmd/controllerbinary for both commits. - Creates fresh kind Kubernetes clusters for two vulnerable attempts and two fixed attempts.
- Starts the real Nuclio controller binary inside each kind control-plane container.
- Applies real
NuclioFunctionandNuclioProjectcustom resources. - Waits for the controller to generate a Kubernetes CronJob from the malicious cron trigger.
- Creates a manual Job from that CronJob and captures the real pod logs.
- Expected evidence:
- Vulnerable attempts: CronJob JSON contains
args[0] == "/bin/sh"andargs[1] == "-c", and pod logs containCVE_PRODUCT_RCEanduid=. - Fixed attempts: CronJob JSON contains
command: ["curl"], does not contain/bin/shor-c, and pod logs contain only the stub HTTP responseokwith no attacker marker.
- Vulnerable attempts: CronJob JSON contains
Evidence
bundle/logs/reproduction_steps.log— top-level run log for the final successful run.bundle/repro/runtime_manifest.json— structured runtime manifest showingentrypoint_kind="endpoint",service_started=true,healthcheck_passed=true, andtarget_path_reached=true.bundle/logs/vulnerable_attempt1/cronjob_pretty.jsonandbundle/logs/vulnerable_attempt2/cronjob_pretty.json— vulnerable controller-generated CronJobs. They show the generated container args include/bin/sh,-c, and an injected shell command string containingecho CVE_PRODUCT_RCE; id.bundle/logs/vulnerable_attempt1/pod.logandbundle/logs/vulnerable_attempt2/pod.log— real CronJob-derived pod logs. Key excerpt:
CVE_PRODUCT_RCE
uid=100(curl_user) gid=101(curl_group) groups=101(curl_group)
bundle/logs/fixed_attempt1/cronjob_pretty.jsonandbundle/logs/fixed_attempt2/cronjob_pretty.json— fixed controller-generated CronJobs. They showcommand: ["curl"]and discrete args containing the malicious strings as data, not shell syntax.bundle/logs/fixed_attempt1/pod.logandbundle/logs/fixed_attempt2/pod.log— fixed negative control pod logs. They containokand do not containCVE_FIXED_SHOULD_NOT_RUN.- Environment details captured in
bundle/logs/reproduction_steps.loginclude Go, Docker, kind, resolved commits, and built controller binary metadata.
Recommendations / Next Steps
- Keep the fixed exec-form approach: never pass user-controlled cron trigger header keys, header values, or body through
/bin/sh -c. - Pass curl and all arguments as a command/argv vector, not a shell command string.
- Use
--data-rawor an equivalent safe body handling mechanism to avoid file expansion or shell substitution primitives. - Add regression tests that exercise malicious header keys, header values, and bodies through the controller/CronJob generation path and assert no shell is present in the resulting pod spec.
- Upgrade Nuclio deployments to a version containing fixed commit
3356b86a8bfab3f960aa420310ebff765df9dedeor later.
Additional Notes
- Idempotency confirmation:
bundle/repro/reproduction_steps.shwas run successfully twice consecutively after the final fixes. - The script uses real Kubernetes objects and a real Nuclio controller binary, but it starts the controller directly in the kind control-plane container rather than installing the full Helm chart. This preserves the relevant production boundary: Kubernetes API/CRD watch -> Nuclio controller reconciliation -> generated Kubernetes CronJob -> real CronJob pod execution.
- A small busybox HTTP stub is deployed only so fixed exec-form curl has a reachable function service target and can complete without retry timeout. It is not used to simulate the vulnerable controller or CronJob generation behavior.
Variant Analysis & Alternative Triggers for CVE-2026-52831
No distinct fixed-version bypass or materially different same-root-cause variant was confirmed. The parent vulnerability was reachable because Nuclio's Kubernetes controller converted untrusted cron trigger event.headers and event.body into a single /bin/sh -c string for a generated Kubernetes CronJob. The fixed commit 3356b86a8bfab3f960aa420310ebff765df9dede removes that shell sink in the centralized CronJob generation path by invoking curl directly with discrete argv entries and using --data-raw for body content. Bounded variant analysis tested header-key, header-value/body, body-@, and header-@ candidates; all were covered or did not produce command execution on the fixed target.
Fix Coverage / Assumptions
The original fix relies on this invariant: no attacker-controlled cron trigger header key, header value, or body may be interpreted by a shell while constructing or running the generated Kubernetes CronJob invocator container.
The fix explicitly covers:
pkg/platform/kube/functionres/lazy.go,(*lazyClient).generateCronTriggerCronJobSpec.- The Kubernetes cron-trigger path:
NuclioFunction.spec.triggers[*].kind == "cron"-> controller reconciliation -> generated KubernetesCronJob-> invocator container. - Header keys and values, now appended as literal
curlargv pairs:"--header", fmt.Sprintf("%s: %s", headerKey, value). - Body values, now appended as
"--data-raw", eventBodyinstead of shell-writing to a temp file and using--data '@/tmp/eventbody.out'.
The fixed code does not attempt to sanitize or reject metacharacters. Instead, it removes the interpreter that made metacharacters dangerous. That is sufficient for the parent command-injection root cause. One non-shell curl behavior remains in theory (--header @file), but the tested Nuclio formatting makes the entire header argument @/path: value; curl errors while trying to open that exact string and no OS command executes.
Variant / Alternate Trigger
Tested candidates and entry points:
Header key with quote/semicolon shell metacharacters
- Entry point: Kubernetes/Nuclio API creation or update of a
NuclioFunctioncustom resource with a cron trigger. - Path:
pkg/platform/kube/functionres/lazy.go:generateCronTriggerCronJobSpec. - Result: parent trigger on vulnerable parent, not a fixed-version bypass. The fixed commit treats the complete header as one argv value after
--header.
- Entry point: Kubernetes/Nuclio API creation or update of a
Body with command substitution or shell separators
- Entry point: same
NuclioFunctioncron trigger attributes,event.body. - Path: same function.
- Result: not a fixed-version bypass. The fixed commit passes the body literally via
--data-raw; no shell sees$(id)or separators.
- Entry point: same
Body beginning with
@/pathto use curl file-load semantics- Entry point: same cron trigger
event.body. - Path: same function.
- Result: not a fixed-version bypass. The fixed commit explicitly uses
--data-raw, covered by the regression test.
- Entry point: same cron trigger
Header key beginning with
@/pathto use curl--header @filesemantics- Entry point: same cron trigger
event.headersmap key. - Path: same function and curl argv semantics.
- Result: not a command-execution bypass. The local probe showed curl fails with
curl_exit=26while trying to open/etc/passwd: marker; no request reaches the server and no marker command executes.
- Entry point: same cron trigger
- Package/component affected by the parent bug: Nuclio Kubernetes controller, cron-trigger CronJob generation in
pkg/platform/kube/functionres/lazy.go. - Affected versions as tested: vulnerable parent
82b9b64ee9bc0c7d99447b5890ef85973fee4e36; ticket states Nuclio 1.15.27 and earlier are affected. - Fixed version as tested:
3356b86a8bfab3f960aa420310ebff765df9dede. - Risk level and consequences for the parent: critical RCE in the CronJob container context when an actor can create/update a malicious cron-trigger NuclioFunction.
- Variant-stage consequence: no additional fixed-version RCE was reproduced.
Impact Parity
- Disclosed/claimed maximum impact for the parent: arbitrary OS command execution / RCE from malicious cron trigger headers or body.
- Reproduced impact from this variant run: no fixed-version command execution; the variant script confirmed the vulnerable parent still contains the shell sink and the fixed commit does not, then executed bounded candidate probes.
- Parity:
nonefor a new variant/bypass. - Not demonstrated: no code execution on the fixed commit; no alternate production entry point outside Kubernetes cron-trigger CronJob generation was found.
Root Cause
The parent root cause was unsafe composition of attacker-controlled cron trigger data into a shell command string stored as Args: []string{"/bin/sh", "-c", curlCommand}. The fixed commit is 3356b86a8bfab3f960aa420310ebff765df9dede ([Security] Fix cron trigger shell injection (#4139)). It changes the sink from shell-form execution to exec-form curl:
- Vulnerable parent:
headersAsCurlArgand body shell fragments are concatenated intocurlCommand, then run through/bin/sh -c. - Fixed commit:
Command: []string{"curl"}andArgs: curlArgsensure untrusted strings are data argv entries.
Because the same underlying bug depends on shell interpretation, and the fixed centralized path removes that interpreter for all cron event headers/body, the same root cause could not be reached by the tested alternate inputs.
Reproduction Steps
- Use
bundle/vuln_variant/reproduction_steps.sh. - The script:
- Resolves the vulnerable parent and fixed commit in the prepared Nuclio repository.
- Saves patch stats, source excerpts, and a security-policy scan under
bundle/logs/vuln_variant/. - Checks the vulnerable parent for
/bin/sh -ccron-trigger generation. - Checks the fixed commit for
Command: ["curl"], no shell-form args, and--data-rawbody handling. - Records a bounded candidate matrix and runs a local curl
--header '@/etc/passwd: marker'semantics probe.
- Expected result: exit
1, meaning no variant reproduced on the fixed version. This is the intended negative result for this run. The script is idempotent and was run twice successfully.
Evidence
bundle/logs/vuln_variant/reproduction_steps.log— final variant-stage execution log.bundle/logs/vuln_variant/source_identity.txt— exact resolved commits:- vulnerable parent
82b9b64ee9bc0c7d99447b5890ef85973fee4e36 - fixed commit
3356b86a8bfab3f960aa420310ebff765df9dede
- vulnerable parent
bundle/logs/vuln_variant/patch_diff.full— fixed diff showing removal of shell-formcurlCommandand addition of exec-formcurlArgs.bundle/logs/vuln_variant/lazy_vulnerable_excerpt.txtandbundle/logs/vuln_variant/lazy_fixed_excerpt.txt— side-by-side relevant source excerpts.bundle/logs/vuln_variant/candidate_matrix.log— evaluated candidates and rule-out rationale.bundle/logs/vuln_variant/curl_header_at_probe.log— curl@header probe; key excerpt:curl_exit=26, no server request, and no command-execution marker.bundle/vuln_variant/runtime_manifest.json— structured runtime/inspection manifest for the negative validation.
Environment details captured include the repository path, current repository HEAD, fixed commit subject/date, and local curl version.
Recommendations / Next Steps
- Keep the exec-form
curlimplementation and do not reintroduce/bin/sh -cfor generated cron-trigger CronJobs. - Add or keep regression tests covering header values in addition to header keys, body command substitution, and body leading
@values. - Consider adding a regression case for header keys beginning with
@to document curl behavior and prevent future changes from accidentally creating a file-read primitive. - If future features add new cron-trigger invocation modes, require the same invariant: no shell command string may be built from
event.headers,event.body, trigger names, or function names.
Additional Notes
- Idempotency confirmation:
bundle/vuln_variant/reproduction_steps.shwas run twice; both runs completed and exited1without crashing, consistently indicating no fixed-version bypass. - The target repository did not contain a top-level
SECURITY.mdor explicit threat model in the bounded scan. The security boundary used here is the one demonstrated by the parent repro: untrusted function spec data crossing the Kubernetes/Nuclio API boundary into controller-generated Kubernetes workload commands. - This stage produced a negative verdict, not a claimed new vulnerability.
CVE-2026-52831 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-52831
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-52831
FAQ: CVE-2026-52831
How does the CVE-2026-52831 exploit work?
curl --header "key: value" quoting (event.body command substitution is also viable). When Kubernetes runs the generated CronJob, the resulting pod executes the injected shell commands in the CronJob container context — demonstrated in the reproduction with attacker-controlled echo and id commands.Which Nuclio versions are affected by CVE-2026-52831, and where is it fixed?
How severe is CVE-2026-52831?
How can I reproduce CVE-2026-52831?
References for CVE-2026-52831
Authoritative sources for CVE-2026-52831 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.