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).

Secret file

Path

  1. Exploiter une LFI pour lire le code source de l’application
  2. Exploiter une SQLi afin d’obtenir une information sensible
  3. Utiliser cette information dans un JWT forgé à la main.
  4. 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 :

App files

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:

admin.php
<?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);

index.php
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>";
	}
}

On a donc un cookie de session auth définie automatiquement par l’application.

Ce cookie est un JWT très basique que voici : JWT

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.

Weak key

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.

Auth page

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.

SQLi erreur

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 -- . Role leak

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: files

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

Flag: SHLK{Ic3_Cr3am_L0v3r}