# Hack the Box - Zetta

htb, walkthrough, writeup, ftp, rsync, postgres, brute-force, sql, ipv6

# Introduction

Let me just start with - what a box! Zetta was to this point the most complex machine I have completed and I enjoyed every second of it. I personally compare the difficulty of this machine to Bankrobber, which was rated as Insane, despite Zetta being marked as Hard.

Starting with an FTP FXP IPv6 leak, to an rsync brute-force and abuse we get user access to the machine. Once on, we chain custom crafted syslog messages using logger with a postgres command injection to pivot user access. Finally, a dubious password policy leads to using discovered credentials and adapting them to the root password for system level access.

Without any further delay, let us step through it!

# Initial Recon

We start with our regular NMAP scan to see what we are working with.

Alright, an FTP, SSH service and a web Nginx service. Let's start Burp and take a look at what is offered on the web page first.

Taking a look at the main page it seems to be a business offering for managed storage. If we go to the bottom of the page we find something quite interesting - a set of credentials!

If we refresh the page the credentials change. That seems potentially odd, so a bit of digging and sure enough we find the section of code in the page that is generating these "credentials" - seemingly the same 32 random characters for username and password.

Alright, so these are probably not true credentials, however it seems an interesting angle to pursue. Let's try using the information to log in to the FTP service.

While it is excellent that we are in, there unfortunately doesn't seem to be anything directly available to us. The interesting part really comes down to the metnion of FXP transfers. Back on the main web page where we found the credentials there was also mention of "We support native FTP with FXP enabled. We also support RFC2428". I wasn't as familiar with FXP so took the opportunity to do a little external reading with the RFC docs.

This document provides a specification for a way that FTP can communicate data connection endpoint information for network protocols other than IPv4
...
The EPRT command allows for the specification of an extended address for the data connection

Interesting, so interpreting this another way there is a possibility that this machine is also configured to use IPv6. Since we don't have a way to know what the IPv6 address is as is, we should be able to leverage the EPRT command to connect back to our own IPv6 address using the FTP server's IPv6 interface, leaking the address to us. Let's start Wireshark on our end and give this a shot.

And if we check our Wireshark capture.

Excellent, we have the server address of dead:beef::250:56ff:fea2:4d93. Note that this IPv6 address seems to change on every reboot, so on subsequent attempts it was required to redo this portion.

# User exploitation

Now that we have the IPv6 address, let's attempt to rerun our NMAP scan and see if anything else pops out.

Oh ho! We have an rsync service available on IPv6 where it was not on IPv4. What happens if we poke around there.

This is looking more promising, excellent. Unfortunately trying to enumerate all of the listed modules leads us to access denied. At this point I started scratching my head a bit until I realized that the modules were all the standard directories in a Unix machine, however /etc was missing... Hum, let's see if that is just a coincidence.

Bingo! Ok let's see if we can grab a copy of /etc and sync it locally for further analysis.

Once we have the files locally we take a look at the more obvious spots - passwd, ftpusers, backups, but it is only once we look at the rsyncd.conf file do we really notice something interesting.

Another hidden directory! We also have the user provided on top of it, excellent. Unfortunately, we don't have the password and trying to access /home/roy results in access denied.

With the username however, let's see if we can script up a simple bruteforce to iterate through the rockyou password list. Who knows, we might get lucky.

And once we let is run for awhile... we get lucky!

With roy's credentials, we can now sync and upload files to /home/roy.

Well we can see our user.txt flag present! Also, there is an interesting .tudu.xml file. I mention it as it will become useful later on. As we need to proceed past just the user flag, instead of downloading the files locally let's upload our ssh authorized_user's file to allow SSH connection.

And just like that, we have our user access. Despite the amount of effort so far, this is just the beginning for Zetta!

# Root exploitation

Now remember when I mentioned that tudu file? Well if we do a quick search we can find that "TuDu is a comand line interface to manage hierarchical todo". Excellent, let's run it and see what is in it.

If we follow along, we can pretty much mentally map the workflow that got us here so far - HTTP server -> FTP -> Rsync. Taking a bit of a leap here, but let's make an assumption that our next step will have something to do with Syslog.

Alright, let's see if we can find any configs or other tidbits to help point us in the right direction. During the search we did remember that there was mention of .git repositories in the rsync.conf file.

We end up finding /etc/rsyslog.d has a .git repo configured. We also have enough entitlements to clone it to a location where we have access to the files.

Ok, so if we understand the config above properly, it looks like all local7.info syslog messages are triggering this pgsql.conf rule and ultimately being forwarded to a postgres instance.

If we poke around a bit more, we can see where postgres stores its own logs. If you are following my mindset, we're trying to map the data model of these logs so we can start testing different approaches.

Ok, great, we now have any idea of what syslog messages get sent to posgres, local7.info, and we also have the location where the logs get forwarded to based on the rsyslog.d conf, postgresql-11-main.log. Thanks to that last location also now understand the format that these logs are being inserted into the postgres instance.

With all this information we have a good idea of how to proceed. We need to send our own custom syslog message, where we will attempt to execute a SQL injection through the postgres insert statement.

For the first part, the easiest way would be to use a utility like logger. We can pass it an attribute -p local7.info to ensure our message get's picked up by the postgres rule.

As far as the SQL injection payload, there are several ways to do this. During my investigations I figured out where the user postgres's homedirectory and SSH directory was located, therefore I decided to go with the ssh/authorized_keys route.

It took a lot of different tweaks and attempts to get the right character escapes, but finally I was able to get successful execution with the following.

And because why not, I then crafted another payload to cat the file and see if it was properly updated.

Great! Now let's see if this was successful by attempting to SSH in.

One step closer... almost there, hopefully.

That seems suspiciously like the format of the password we saw in the .psql_history. Interesting...very interesting. Let's see if it can be as easy as replacing the userid portion to "root" and attempting log in as root using the password sup3rs3cur3p4ass@root.