Description
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
Published: 2026-10-14
Score: 8.8 High
EPSS: n/a
KEV: No
Impact: Escalation to host root leading to full node compromise
Action: Immediate Patch
AI Analysis

Impact

The insights-runtime-extractor runs as a privileged DaemonSet that scans every running container on the node. Its scanning routine forks a child, enters the target container’s PID and mount namespaces, changes directory to an orphaned file descriptor pointing at the extractor’s root, and then only calls seteuid() to lower its effective UID. This leaves the real UID, saved set‑UID, and all permitted capabilities unchanged. When the auditor later executes the workload’s own binary inside the same mount namespace while still having root real UID and CAP_SYS_ADMIN, the binary can call setuid(0) to regain full root privileges. The process inherits a working directory that points back to the extractor container’s filesystem, which contains the host CRI‑O socket, giving the attacker unrestricted access to the host’s container runtime. The impact is therefore a complete privilege escalation to the host, allowing control of the node, data exfiltration, and further lateral movement.

Affected Systems

Red Hat OpenShift Container Platform 4 is affected, as the platform deploys insights-runtime-extractor as a privileged DaemonSet with hostPID enabled. No specific version information beyond the platform release is provided, but any installation containing the default collector daemon is vulnerable.

Risk and Exploitability

The CVSS score of 8.8 indicates a high severity vulnerability. The EPSS score is not available, but the absence of a KEV listing does not diminish its potential risk, because the exploitation path is available to any tenant capable of scheduling a pod. The likely attack vector is from an unprivileged tenant creating a pod that runs a malicious binary whose entry point matches a recognized fingerprint pattern; the DaemonSet will automatically scan the pod, execute the binary under root real UID, and obtain host‑level privileges. The exploit requires only the ability to schedule a pod, which is a common entitlement in OpenShift environments.

Generated by OpenCVE AI on October 14, 2026 at 14:35 UTC.

Remediation

Vendor Workaround

The vulnerability requires a code fix to implement a complete privilege drop (setting all three UID/GID fields, clearing all Linux capability sets, and setting `PR_SET_NO_NEW_PRIVS`) before executing any fingerprint binary.


OpenCVE Recommended Actions

  • Apply the official Red Hat patch that performs a full privilege drop, including setting the real UID, clearing all capability sets, and enabling PR_SET_NO_NEW_PRIVS before executing any fingerprint binary.
  • Re‑configure the insights‑runtime‑extractor DaemonSet to run without hostPID or as a non‑root user so it cannot enter other containers’ namespaces.
  • Remove or restrict the ability for tenants to schedule pods that could match the versioned‑executable patterns, or enforce image signing and scanning to prevent malicious binaries from being deployed.

Generated by OpenCVE AI on October 14, 2026 at 14:35 UTC.

Tracking

Sign in to view the affected projects.

Advisories

No advisories yet.

History

Wed, 14 Oct 2026 11:00:00 +0000

Type Values Removed Values Added
Description 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
Title Insights-runtime-extractor: insights-runtime-extractor: container escape via privileged binary execution
First Time appeared Redhat
Redhat openshift
Weaknesses CWE-250
CPEs cpe:/a:redhat:openshift:4
Vendors & Products Redhat
Redhat openshift
References
Metrics cvssV3_1

{'score': 8.8, 'vector': 'CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H'}


Subscriptions

Redhat Openshift
cve-icon MITRE

Status: PUBLISHED

Assigner: redhat

Published:

Updated: 2026-10-14T15:26:53.109Z

Reserved: 2026-10-02T13:07:13.820Z

Link: CVE-2026-104795

cve-icon Vulnrichment

No data.

cve-icon NVD

Status : Received

Published: 2026-10-14T11:16:59.937

Modified: 2026-10-14T11:16:59.937

Link: CVE-2026-104795

cve-icon Redhat

No data.

cve-icon OpenCVE Enrichment

Updated: 2026-10-14T14:45:02Z

Weaknesses
  • CWE-250

    Execution with Unnecessary Privileges