Insights-runtime-extractor: insights-runtime-extractor: container escape via privileged binary execution
- Published
- Last modified
- Where the record stands: published, rejected by its publisher, or superseded by another id.What do these mean? →
- published
- Whether CISA lists it in its Known Exploited Vulnerabilities catalog: proof that attackers are using it.What do these mean? →
- Not listed in CISA KEV
- How many real-world setups can hit the bug, from the vendor's advisory and CISA data. It does not know your setup.What do these mean? →
- Specific setups only
- Fix
- Fixed in 4.21.39
- Whether working attack code is public (Exploit-DB, Metasploit, Nuclei or GitHub). Public code makes attacks easier.What do these mean? →
- None on Exploit-DB, Metasploit or GitHub
- Exploitation: none
- Automatable: no
- Technical impact: total
Summary
The insights-runtime-extractor runs as a privileged DaemonSet (`hostPID: true`) that scans container processes for runtime fingerprinting. When scanning a container, the coordinator: 1. fork()s a child process 2. setns() into the target container's PID and mount namespaces 3. fchdir() back to an orphaned FD pointing at the extractor's own root filesystem (where `/crio.sock` is hostPath-mounted) 4. Calls **only `seteuid(target_uid)`** — changing the effective UID but leaving the real UID and saved-set-UID at 0 and **retaining the permitted capability set** 5. The `check_no_privileged_perms()` guard inspects only the Effective capability set (so it passes), and is skipped entirely when the target runs as uid 0 6. The `VersionExecutable` fingerprint invokes `fpr_kind_executable` with `process.command_line[0]` — the target's argv[0], read from `/proc/<pid>/cmdline` and fully controlled by the workload 7. `fpr_kind_executable` calls `exec.Command(executable, "--version").Output()` — executing the workload's own binary inside the workload's mount namespace while ruid=0 in the host user namespace The executed process can call `setuid(0)` to restore full root with `CAP_SYS_ADMIN`, and inherits the orphaned cwd which resolves into the extractor container's filesystem containing the host CRI-O socket. Affected Code 1. Insufficient privilege drop (Rust — extractor coordinator) File: `extractor/src/insights_runtime_extractor.rs` ```rust // Lines 139-146 — seteuid-only drop, guard skipped for root containers join_process_namespaces(process.pid)?; change_dir(current_dir); switch_user(*process.uid)?; if *process.uid != 0 { perms::check_no_privileged_perms() .expect("Must not have privileged permissions to run fingerprints"); } fingerprint::run_fingerprints(&config, out_dir, &process); ``` ```rust // Lines 182-185 — only effective UID changed; ruid/suid stay 0 fn switch_user(uid: u32) -> Result<(), ScannerError> { debug!("... Becoming user: {uid}"); seteuid(uid.into()).map_err(|e| ScannerError::Errno(e)) } ``` 2. Execution of untrusted binary (Go — fingerprint) File: `fingerprints/pkg/utils/file.go` ```go // Lines 75-80 — executes a path taken verbatim from workload's argv[0] func GetExecutableVersionOutput(executable string) (string, error) { out, err := exec.Command(executable, "--version").Output() if err != nil { return "", err } return string(out), nil } ``` 3. Workload-controlled path forwarded (Rust — version_executable) File: `version_executable.rs` ```rust // Lines 29-34 — workload-controlled command_line[0] forwarded to fingerprint return Some(vec![ fpr_kind_executable, out_dir.to_string(), String::from(&process.command_line[0]), String::from(&version_executable.runtime_kind_name), ]); ``` Attack Pattern Attacker: Any tenant able to schedule a pod (unprivileged, any namespace) Victim: The node running the insights-runtime-extractor DaemonSet Trigger: Automatic — the DaemonSet scans all container processes on the node Attack sequence: 1. Attacker creates a pod whose process name matches a configured versioned-executable pattern (`node`, `python`, `python3`, `MainThread`, or contains `java`) 2. The pod's main binary (at the path matching argv[0]) is a malicious executable that calls `setuid(0)` to restore root 3. When the insights operator triggers a scan, the DaemonSet forks, enters the attacker's mount namespace, and executes the attacker's binary with ruid=0 4. The attacker's code regains full root with `CAP_SYS_ADMIN` in the host user namespace 5. The inherited cwd contains the host CRI-O socket → full node compromise
Timeline
- CVE published
- CISA SSVC assessment
Exploitation: none · Automatable: no · Technical impact: total
- Record last updated
Affected versions
- openshift4/insights-runtime-extractor-rhel9
Version range not reported
- openshift4/insights-runtime-extractor-rhel9
Version range not reported
- openshift4/insights-runtime-extractor-rhel9
Version range not reported
- openshift4/insights-runtime-extractor-rhel9
Version range not reported
- openshift5/insights-runtime-extractor-rhel9
Version range not reported
References 6
access.redhat.com
bugzilla.redhat.com
Questions about CVE-2026-104795
What should I do about it?
Update to Red Hat OpenShift Container Platform 4.21.39, the release Red Hat lists as fixing this issue. The facts do not give fixed versions for 4.19, 4.20, 4.22, or 5, so check Red Hat's advisory for those branches.Source: access.redhat.com
Is it being exploited?
It is not in CISA's Known Exploited Vulnerabilities catalog, and CISA's SSVC assessment does not mark exploitation as active. That does not mean it has not been attacked: vendors and researchers can report attacks before CISA lists them. No public exploit code is tracked.Source: cve.now feed data