Web Exploitation
Broken Access Control
Information Disclosure
Template Injection
Récapitulatif complet du lab Compete
Ce document résume les difficultés rencontrées, les fausses pistes, les découvertes utiles et la chaîne d’exploitation
qui a mené jusqu’au flag.
Résultat final : obtention d’un RCE via une
Server-Side Template Injection dans quiz_description, puis lecture du flag utilisateur
dans /home/local.txt.
Objectif du lab
Le lab annonçait une chaîne de vulnérabilités à exploiter :
fuite d’informations, contournement d’authentification, traitement de templates, puis exécution de commandes.
Flag trouvé
flag_943be91e_3ae2_43d8_b0f3_ed4980fdd34d
Le \n vu dans la sortie n’était qu’un retour à la ligne, pas une partie du flag.
1. Les premières difficultés rencontrées
Découverte de plusieurs endpoints sans exploitation immédiate
- Tu as trouvé
/admin, /data, .htaccess, .htpasswd et /server-status.
- Beaucoup de ces chemins répondaient en
403 ou en redirection, donc ils confirmaient l’existence de ressources, sans offrir un accès direct utile.
- On savait que ces découvertes servaient surtout à la cartographie de l’application.
Fausses pistes autour du client-side
- Le panneau admin contenait beaucoup de JavaScript pour gérer les modales et l’interface.
- Au début, il était tentant de penser que la logique du quiz était surtout côté client.
- En réalité, les formulaires faisaient bien des
POST vers le serveur.
Test d’injection JavaScript non concluant
- Tu as essayé de casser la structure des données stockées avec un payload du type
";console.log(...);//.
- Mais le contenu était correctement échappé dans le JSON, donc cette piste ne permettait pas le RCE.
Confusion initiale sur le “template processing”
- Le code HTML récupéré côté client montrait des
htmlEscape() et des htmlspecialchars().
- Donc les payloads classiques comme
{{7*7}} n’étaient pas exécutés dans les vues normales.
- Il fallait trouver où le traitement du template se faisait réellement côté serveur.
2. Les découvertes importantes
Accès admin obtenu
Une étape clé a été la connexion en tant qu’admin. Sans cet accès, la création de quiz et la zone vulnérable
n’auraient pas été atteignables.
Découverte du stockage interne dans /data
quizzes.json
results.json
users.json
Cela a confirmé que l’application stockait son état dans des fichiers JSON, ce qui a beaucoup aidé pour suivre
l’effet des payloads.
Mise en évidence du paramètre file=
En interceptant une requête, on a vu une URL du type :
/?file=quiz.php&quiz_id=...&action=quiz
Cela a révélé une inclusion locale de fichiers, et donc une surface d’attaque pour lire le code source.
Lecture du code source avec php://filter
Grâce à php://filter/convert.base64-encode/resource=..., tu as pu récupérer le code source
de quiz.php, puis de admin.php.
C’était le tournant décisif de l’exploitation.
3. Ce que le code a révélé
Le point critique se trouvait dans admin.php lors de la création d’un quiz :
if ($_POST['quiz_action'] === 'add_quiz') {
...
$description = $_POST['quiz_description'] ?? '';
$compiled_description = $smarty->fetch('string:' . $description);
...
}
Et la classe “Smarty” simplifiée contenait ceci :
$template = preg_replace_callback('/{php}(.*?){\/php}/s', function($matches) {
ob_start();
eval($matches[1]);
$output = ob_get_clean();
return $output;
}, $template_string);
À partir de là, la vulnérabilité était claire :
le champ quiz_description était évalué
et tout code entre {php} et {/php} passait dans un eval().
4. La chaîne d’exploitation finale
Étape 1 — Vérification de l’exécution de commande
Payload utilisé dans quiz_description :
{php}system('id');{/php}
Résultat observé dans la description compilée :
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Le RCE était confirmé.
Étape 2 — Recherche du flag
Une première recherche avec find / -name "*flag*" a renvoyé énormément de faux positifs
comme irqflags, page-flags, etc.
Cela a montré qu’il fallait être plus ciblé dans les chemins ou se référer aux consignes du lab.
Étape 3 — Indice fourni par la plateforme
Le panneau du lab précisait que sur Linux :
- le flag utilisateur est dans
/home/local.txt
- le flag root est dans
/root/proof.txt
Étape 4 — Lecture du flag utilisateur
Payload utilisé :
{php}system('cat /home/local.txt 2>/dev/null');{/php}
La sortie a ensuite été enregistrée dans la description du quiz, ce qui a permis de récupérer directement le flag.
5. Difficultés techniques rencontrées, en résumé
- Beaucoup de bruit dans les endpoints : plusieurs ressources existaient mais restaient bloquées en
403.
- Fausses pistes côté client : l’interface donnait l’impression que la logique du quiz était surtout front-end.
- Échappement HTML/JSON : les essais d’injection JavaScript et de PHP brut ne menaient pas au RCE.
- Confusion sur le moment d’exécution : le payload ne s’exécutait pas à l’affichage, mais lors de la création du quiz.
- Résultats de recherche trop larges : la commande
find non ciblée produisait beaucoup de faux positifs.
6. Ce qui a permis d’aboutir
- Observer les paramètres réellement envoyés au serveur.
- Lire le code source au lieu de se limiter à l’interface.
- Identifier la logique réelle du “template processing”.
- Comprendre que la syntaxe attendue était
{php}...{/php} et non <?php ... ?>.
- Valider l’exécution avec
system('id') avant de viser le flag.
7. Conclusion
La réussite ne vient pas d’un seul bug, mais d’un enchaînement logique :
reconnaissance → accès admin → lecture du code source →
identification d’une SSTI/RCE dans le champ description → lecture du flag.
La difficulté principale a été de distinguer les pistes décoratives ou secondaires des indices vraiment exploitables.
Le moment décisif a été la lecture de admin.php, qui a révélé l’eval().