Time Of Check Time Of Use on Docker
How Time Of Check Time Of Use Manifests in Docker
Time Of Check Time Of Use (TOCTOU) vulnerabilities in Docker environments occur when the state of a resource changes between the time it's checked and the time it's used. In containerized applications, this race condition can lead to severe security breaches, particularly when Docker's file system and volume mounting mechanisms are involved.
The most common Docker-specific TOCTOU scenario involves symbolic link attacks during volume mounting. When a container mounts a directory from the host, an attacker can exploit the window between permission checks and actual file access. Consider this vulnerable Dockerfile pattern:
FROM node:18-alpine
# Vulnerable TOCTOU pattern
RUN mkdir -p /app/data && chown -R node:node /app/data
COPY --chown=node:node . /app/data
USER node
WORKDIR /app/data
CMD ["node", "server.js"]An attacker with write access to the host directory before the container starts can create a symlink pointing to a sensitive file. When the container's startup script checks permissions on /app/data, it sees the directory is owned by 'node'. However, by the time the application reads files, the symlink could have been switched to point to /etc/shadow or another protected file.
Another Docker-specific TOCTOU occurs with dynamic volume mounting in orchestration platforms. When Kubernetes or Docker Compose mounts secrets or configuration files, there's a timing gap between when the volume is prepared and when the application reads it. This can be exploited in rolling deployments where old and new container versions coexist:
apiVersion: apps/v1
kind: Deployment
metadata:
name: toctou-example
spec:
replicas: 3
template:
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config
mountPath: /app/config
- name: secret
mountPath: /app/secret
volumes:
- name: config
configMap:
name: app-config
- name: secret
secret:
secretName: app-secretDuring a rolling update, the old container might still be reading from a configMap while the new container has already mounted an updated version. If the application doesn't handle this atomicity, it could read partial or inconsistent configuration data.
Docker build processes also suffer from TOCTOU vulnerabilities. The COPY instruction in Dockerfiles is not atomic, creating windows where intermediate build stages can be manipulated:
FROM alpine:latest
# Vulnerable build step
COPY --chown=root:root . /tmp/build
RUN chmod -R 755 /tmp/build && chown -R root:root /tmp/build
# The window between COPY and chmod is exploitable
RUN /tmp/build/compile.shAn attacker with access to the build context can modify files between the COPY operation and the subsequent permission changes, potentially injecting malicious code that gets compiled and included in the final image.
Docker-Specific Detection
Detecting TOCTOU vulnerabilities in Docker environments requires both static analysis of Dockerfiles and runtime monitoring of container behavior. middleBrick's Docker-specific scanning module examines several key areas that traditional security scanners miss.
For Dockerfile analysis, middleBrick scans for patterns that create TOCTOU windows. The scanner specifically looks for:
| Pattern | Risk Level | Detection Method |
|---|---|---|
| COPY followed by permission changes | High | Static analysis of instruction sequence |
| Volume mounts with relative paths | Medium | Path resolution analysis |
| USER switching without validation | Medium | Privilege escalation path analysis |
| Dynamic configuration loading | High | Runtime behavior analysis |
middleBrick's runtime detection capabilities include monitoring Docker daemon API calls for suspicious patterns. The scanner hooks into container lifecycle events to detect:
docker events --filter 'type=container' --filter 'event=start'During container startup, middleBrick monitors the sequence of mount operations, user switching, and file access patterns. It flags containers that perform file operations in multiple steps without atomicity guarantees.
For orchestration platforms, middleBrick provides Kubernetes-specific TOCTOU detection. It analyzes rolling update configurations and identifies deployment strategies that create timing windows:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vulnerable-deployment
spec:
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # Creates TOCTOU window during updates
type: RollingUpdateThe scanner also detects TOCTOU in Docker Compose files by analyzing service dependencies and startup order. It identifies configurations where services depend on files that might change during initialization:
version: '3.8'
services:
app:
build: .
depends_on:
- database
volumes:
- ./config:/app/config:ro
database:
image: postgres:13
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:middleBrick flags the volume mount pattern where the application might read configuration files while the database is still initializing and potentially modifying related files.
Docker-Specific Remediation
Remediating TOCTOU vulnerabilities in Docker requires architectural changes that eliminate race conditions. The most effective approach is using Docker's built-in atomic operations and security features.
For Dockerfile TOCTOU issues, use multi-stage builds with atomic operations:
FROM alpine:latest AS builder
# Build in a single atomic operation
COPY . /src
WORKDIR /src
RUN chmod -R 755 . && chown -R root:root . && ./build.sh
FROM alpine:latest
COPY --from=builder /src/output /app
RUN chmod -R 755 /app && chown -R root:root /app
# Use non-root user with specific UID
RUN addgroup -g 1001 -S appgroup && adduser -u 1001 -S appuser -G appgroup
USER appuser
WORKDIR /app
CMD ["/app/run.sh"]This pattern ensures that file permissions and ownership are set atomically during the build process, eliminating the window where an attacker could modify files between operations.
For volume mounting TOCTOU, use Docker's read-only mounts and bind propagation controls:
docker run -d \
--name secure-app \
-v /sensitive:/app/data:ro \
-v /host/config:/app/config:ro,z \
-e CONFIG_HASH=$(sha256sum /host/config/app.conf) \
myapp:latestThe :ro flag prevents the container from modifying mounted files, while the :z flag enables SELinux labeling for additional isolation. The CONFIG_HASH environment variable allows the application to verify configuration integrity at runtime.
For orchestration platforms, implement atomic configuration loading using Docker's health checks and readiness probes:
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-deployment
spec:
strategy:
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
type: RollingUpdate
template:
spec:
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
readinessProbe:
exec:
command:
- /app/check_config.sh
initialDelaySeconds: 10
periodSeconds: 5
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: falseThe readiness probe ensures the container only accepts traffic after verifying configuration integrity, preventing TOCTOU scenarios during rolling updates.
For applications that must handle dynamic configuration, implement file locking and atomic read operations:
import os
import fcntl
def read_config_safely(filepath):
"""Read configuration file atomically"""
with open(filepath, 'rb') as f:
# Acquire advisory lock
fcntl.flock(f, fcntl.LOCK_SH)
try:
# Read entire file at once
data = f.read()
# Verify file integrity
if not verify_config(data):
raise ValueError("Config verification failed")
return data
finally:
fcntl.flock(f, fcntl.LOCK_UN)This Python example demonstrates advisory locking to prevent concurrent modifications while reading configuration files, eliminating the TOCTOU window between check and use operations.