Skip to content

Path traversal in ReadFile() via container label escapes proc namespace boundary #3851

Description

@Ryujiyasu

Summary

In manager/container.go, the ReadFile() function uses path.Join with a user-controlled filepath parameter derived from Docker container labels, without any prefix validation. This enables path traversal from within a container's namespace to arbitrary files on the host.

Vulnerable Code

File: cadvisor/manager/container.go:250-272

func (cd *containerData) ReadFile(filepath string, inHostNamespace bool) ([]byte, error) {
    pids, err := cd.getContainerPids(inHostNamespace)
    rootfs := "/"
    if !inHostNamespace {
        rootfs = "/rootfs"
    }
    for _, pid := range pids {
        filePath := path.Join(rootfs, "/proc", pid, "/root", filepath)
        // ↑ NO prefix check: path.Join resolves ".." → escapes proc/PID/root/ boundary
        data, err := os.ReadFile(filePath)

The filepath comes from the io.cadvisor.metric.* container label. With a label value of ../../../../etc/shadow, path.Join resolves to /etc/shadow on the host.

Proof:

path.Join("/", "/proc", "1234", "/root", "../../../../etc/shadow")
// → "/etc/shadow"   ← host shadow file, not container's

path.Join("/rootfs", "/proc", "1234", "/root", "../../../../proc/1/environ")
// → "/proc/1/environ"   ← host init process environment (may contain secrets)

Attack Scenario

  1. Attacker deploys container with label: io.cadvisor.metric.steal=../../../../etc/shadow
  2. cAdvisor calls ReadFile("../../../../etc/shadow", inHostNamespace) for that container
  3. path.Join resolves to host /etc/shadow — file is read by cAdvisor (running as root in a DaemonSet)
  4. Contents leak into cAdvisor logs or error messages

Suggested Fix

After path.Join, validate that filePath still starts with the expected prefix (rootfs + "/proc/" + pid + "/root/"). Reject any path that escapes the container's root namespace.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    lifecycle/staleDenotes an issue or PR has remained open with no activity and has become stale.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions