google / google/cadvisor

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

Open
#3,851 2 comments 0 reactions 0 assignees View on GitHub
lifecycle/stale
Dominant language
Go
Stars
19.4k
Forks
2.5k
Avg merge
9h 7m
Merged PRs (30d)
1

Description

## 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**
```go
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:**
```go
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.