← Retour à HackTheBox

HackTheBox | Obscure

7 novembre 2026•8 min•Facile

Obscure

Dans Obscure, un attaquant a déposé un webshell PHP sur un serveur Apache grâce à une faille d'upload. On dispose du fichier et d'une capture réseau des deux minutes précédant l'arrêt du service.

Scénario du challenge Obscure et fichiers fournis par HackTheBox

Regarder les fichiers fournis

Après extraction de l'archive je retrouve trois fichiers :

19-05-21_22532255.pcap
support.php
to-do.txt

support.php contient le webshell et le PCAP ses échanges réseau. to-do.txt reprend le scénario.

file 19-05-21_22532255.pcap

La capture est bien un PCAP avec des paquets au format Linux cooked. Le PHP est beaucoup moins lisible. Il contient plusieurs morceaux de chaînes et une suite de str_replace avant de construire une fonction et de l'exécuter.

Retrouver les échanges dans Wireshark

Un premier filtre isole les requêtes HTTP :

http.request

Pour écarter les requêtes vers les images et le JavaScript du site, je cible les accès au webshell :

http.request.uri contains "support.php"

Quatre POST vers /uploads/support.php relient 23.129.64.207 à 10.132.0.2.

Le premier échange se trouve dans le flux tcp.stream eq 1.

Premier échange avec le webshell dans Follow TCP Stream, requête POST en rouge et réponse HTTP en bleu

Le serveur répond 200 OK, mais les corps HTTP sont encodés. L'analyse de support.php doit permettre de retrouver leur contenu.

Comprendre le webshell PHP

Dans support.php, les variables $V, $d, $P, $c et $B contiennent les morceaux du programme. Ils sont concaténés puis la chaîne u) est supprimée :

$u = str_replace('u)', '', $V.$d.$P.$c.$B);

Une autre substitution reconstruit create_function, qui sert ensuite à exécuter le code assemblé.

Pour afficher le programme reconstruit sans l'exécuter je reproduis uniquement la concaténation et le remplacement en Python :

import re
from pathlib import Path

php = Path("support.php").read_text()
parts = dict(re.findall(r"\$(V|d|P|c|B)='([^']*)';", php))
code = "".join(parts[k] for k in ["V", "d", "P", "c", "B"])
print(code.replace("u)", "").replace(";", ";\n"))

Le résultat fait apparaître les valeurs qui vont servir au décodage :

$k = "80e32263";
$kh = "6f8af44abea0";
$kf = "351039f4a7b5";
$p = "0UlYyJHG87EJqEz6";

$k est la clé utilisée par la fonction XOR. $kh et $kf sont les marqueurs qui encadrent les données utiles. $p est un préfixe ajouté aux réponses.

La partie importante du traitement reçu est celle-ci :

@eval(@gzuncompress(@x(@base64_decode($m[1]), $k)));

Le traitement enchaîne un décodage Base64, un XOR avec la fonction x puis une décompression zlib. Le résultat est exécuté par eval.

La clé XOR est répétée sur toute la longueur du message. Elle est présente dans le PHP, ce qui permet de décoder le trafic.

Les réponses prennent le chemin inverse. Le webshell capture la sortie, la compresse, applique le XOR puis l'encode en Base64 avant de l'entourer avec ses marqueurs. La même routine de décodage permet donc de lire les requêtes et les réponses.

Décoder les commandes du PCAP

J'utilise TShark pour extraire les corps HTTP avec leur numéro de trame et leur IP source :

tshark -r 19-05-21_22532255.pcap -Y http.file_data \
  -T fields -e frame.number -e ip.src -e http.file_data

Je récupère le contenu entre les deux marqueurs puis je reproduis le décodage du PHP. Voici les opérations appliquées à chaque message :

payload += b"=" * (-len(payload) % 4)
data = base64.b64decode(payload)
key = b"80e32263"
data = bytes(value ^ key[i % len(key)] for i, value in enumerate(data))
plain = zlib.decompress(data)

Les = complètent le padding Base64 lorsqu'il manque. Le XOR utilise la clé répétée sur toute la longueur du message puis zlib.decompress retrouve le texte d'origine.

Vérifier l'utilisateur courant

La première requête contient :

chdir('/var/www/html/uploads');@error_reporting(0);@system('id 2>&1');

La commande id renvoie ce résultat dans la trame 22 :

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

La réponse confirme que les commandes tournent sous le compte www-data.

Chercher des fichiers dans les dossiers personnels

La requête suivante lance :

ls -lah /home/* 2>&1

La réponse révèle le dossier personnel de developer et un fichier intéressant :

-rw-r--r-- 1 developer developer 1.6K May 21 20:37 pwdb.kdbx

C'est une base KeePass. Ses permissions autorisent la lecture par les autres utilisateurs, donc www-data peut la récupérer même si elle appartient à developer.

La troisième requête change le dossier courant vers /home/developer et affiche le chemin. La réponse confirme :

/home/developer

Récupérer la base KeePass

La dernière commande est :

base64 -w 0 pwdb.kdbx 2>&1

La base est exfiltrée en Base64 dans la réponse du webshell, sans retours à la ligne avec -w 0.

La réponse se trouve dans la trame 464. Une fois le message du webshell décodé, il reste le fichier lui-même en Base64. Je décode cette dernière couche et j'enregistre les octets dans work/pwdb.kdbx.

Je vérifie le fichier reconstruit :

file work/pwdb.kdbx

Retrouver le mot de passe de la base

La base est chiffrée. Je tente de retrouver son mot de passe maître avec John.

J'utilise keepass2john pour extraire les paramètres nécessaires à une attaque hors ligne. Sur mon Mac, John est installé avec Homebrew :

"$(brew --prefix)/share/john/keepass2john" work/pwdb.kdbx \
  > work/keepass.hash

Je tente le dictionnaire password.lst fourni avec John :

john --session=work/keepass \
  --wordlist="$(brew --prefix)/share/john/password.lst" \
  --pot=work/john.pot work/keepass.hash

John the Ripper retrouve le mot de passe chainsaw pour la base pwdb

John retrouve chainsaw dans le dictionnaire. Pour réafficher le résultat :

john --show --pot=work/john.pot work/keepass.hash

Ouvrir la base et récupérer le flag

J'ouvre la base avec KeePassXC et le mot de passe chainsaw :

cd work
open -a KeePassXC pwdb.kdbx

Le groupe Passwords contient une seule entrée, Flag, associée à l'utilisateur artikrh.

Base pwdb.kdbx ouverte dans KeePassXC, entrée Flag dans le groupe Passwords et valeur du mot de passe masquée

Le flag se trouve dans le champ mot de passe, masqué sur la capture.

L'entrée contient aussi un lien vers le projet epinna/weevely3. Il permet de rattacher le webshell à Weevely après l'analyse de son fonctionnement.

Je soumets le flag sur HackTheBox et le challenge est validé.

Validation du challenge Obscure sur HackTheBox le 7 novembre 2026

Ce que je retiens

Le PHP donnait la méthode pour décoder le trafic. En l'appliquant aux échanges HTTP, j'ai pu suivre les commandes de l'attaquant et reconstruire la base KeePass qu'il avait récupérée.

La faille d'upload donne accès au serveur sous www-data. Les permissions de la base permettent ensuite de la lire et son mot de passe se retrouve dans un dictionnaire courant.

Voir aussi d'autres catégories...

Ces thèmes pourraient aussi t'intéresser.