CWE data temporarily unavailableCWE data is currently experiencing intermittent outages, which may cause incomplete section loading on CVE pages. We are actively working on a fix.

CVE-2026-104795

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

// 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);
// 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

// 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

// 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

Statement

The extractor runs as a privileged DaemonSet that scans container processes for runtime fingerprinting. When scanning a container, the coordinator performs an incomplete privilege drop — using only seteuid() to change the effective UID while leaving the real UID and saved-set-UID at 0 and retaining all Linux capabilities in the permitted set.

A malicious workload can supply a crafted binary that restores full root privileges via setuid(0), gaining CAP_SYS_ADMIN in the host user namespace and access to the host container runtime socket. This allows a complete container escape with host-level root access.

Mitigation

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.

Common Vulnerability Scoring System (CVSS) Score Details

Info alert:Important note

CVSS scores for open source components depend on vendor-specific factors (e.g. version or build chain). Therefore, Red Hat's score and impact rating can be different from NVD and other vendors. Red Hat remains the authoritative CVE Naming Authority (CNA) source for its products and services (see Red Hat classifications).

CVSS v3 Score Breakdown

Red HatNVDcve.org
Base Score8.8N/A8.8
Attack VectorLocalN/ALocal
Attack ComplexityLowN/ALow
Privileges RequiredLowN/ALow
User InteractionNoneN/ANone
ScopeChangedN/AChanged
ConfidentialityHighN/AHigh
Integrity ImpactHighN/AHigh
Availability ImpactHighN/AHigh

Vector

Red Hat: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

cve.org: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Frequently Asked Questions

Want to get errata notifications? Sign up here.