Path traversal in ReadFile() via container label escapes proc namespace boundary
Author: RyujiyasuCreated Mar 9, 2026Updated Aug 19, 2026
Labelslifecycle/stale
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
- Attacker deploys container with label:
io.cadvisor.metric.steal=../../../../etc/shadow - cAdvisor calls
ReadFile("../../../../etc/shadow", inHostNamespace)for that container path.Joinresolves to host/etc/shadow— file is read by cAdvisor (running as root in a DaemonSet)- 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.
Source: google/cadvisor