CVE-2026-104795 in OpenShift Container Platform
Summary
by MITRE • 10/14/2026
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
If you want to get the best quality for vulnerability data then you always have to consider VulDB.
Analysis
by VulDB Data Team • 10/14/2026
The insights-runtime-extractor DaemonSet, operating with elevated privileges including hostPID access and a mounted host CRI-O socket, exhibits critical privilege escalation vulnerabilities stemming from improper process isolation and unsafe binary execution practices. The core technical flaw lies in the user switching mechanism within the extractor coordinator, which relies exclusively on the seteuid system call to drop privileges for scanned container processes. This approach fails to modify the real UID or saved-set-UID, both of which remain at zero throughout the scanning operation. Consequently, while the effective UID is changed to match the target container's non-root user, the process retains its root-level capabilities and permission sets in the host namespace. The security guard designed to verify that privileged permissions are not present inspects only the effective capability set, allowing it to pass even when significant privileges remain active due to the unmodified real and saved UIDs. This oversight is further exacerbated by a logic flaw where this privilege check is entirely skipped for containers running as UID zero, creating an immediate path to exploitation if such containers exist on the node.
The vulnerability is compounded by how the extractor handles executable fingerprinting. When scanning container processes, the system retrieves the command line arguments from /proc/<pid>/cmdline and extracts the first argument, argv[0], which represents the binary path executed within the workload. This value is directly controlled by the tenant running the pod and is passed verbatim to a function that invokes exec.Command with the --version flag. Because the scanning process operates in the target container's mount namespace but retains root privileges on the host, executing this untrusted binary effectively runs attacker-controlled code as root within the context of the extractor’s environment. The executed process inherits an orphaned file descriptor pointing to the extractor’s root filesystem, which includes a path-mounted CRI-O socket from the host. This allows any arbitrary commands or binaries specified by the workload to interact directly with the container runtime interface on the node.
From an operational impact perspective, this vulnerability enables full cluster compromise for any tenant capable of scheduling pods within the affected namespace. An attacker can craft a malicious executable that calls setuid(0) upon execution, thereby restoring full root privileges and CAP_SYS_ADMIN capabilities in the host user namespace despite the initial privilege drop attempt by the scanner. Once elevated to root on the node, the attacker gains access to the CRI-O socket, which provides control over all containers running on that specific node. This includes the ability to start new privileged pods, inspect or modify existing container configurations, and potentially escalate privileges further within the cluster environment. The attack is triggered automatically by the DaemonSet’s scanning routine, requiring no additional interaction from the attacker beyond deploying a pod with a specifically crafted binary whose name matches known runtime patterns such as node, python, java, or MainThread.
This vulnerability aligns closely with CWE-250, which describes execution with unnecessary privileges, and CWE-78, involving improper neutralization of special elements used in an OS command. In the context of the MITRE ATT&CK framework, this scenario maps to T1610, where attackers drop or deploy a payload on a system that is then executed by another process for privilege escalation. The use of hostPID and shared namespaces also reflects aspects of container escape techniques found under T1611, although here the primary vector is not breaking out of the container but rather abusing the scanning tool’s privileged context to execute code with elevated permissions. To mitigate this risk, it is essential to implement proper privilege dropping by setting all UID fields using setresuid or similar mechanisms that clear real and saved UIDs alongside the effective UID. Additionally, capability sets should be explicitly dropped before executing any external binaries. The execution of untrusted paths must be prevented entirely; instead, fingerprinting logic should rely on static analysis or safe metadata extraction rather than invoking arbitrary executables provided by workloads. Finally, restricting hostPID access and avoiding mount namespace sharing with workload containers would significantly reduce the attack surface for such privilege escalation vectors.