✅ Day 03 - Santa's Magic Sack

  • WEB HACKING GAME
  • Date de résolution : 03/12/2024

Reconnaissance

Le chall commence par un jeu consistant à récupérer des cadeaux dans une hotte qui est bougé par l'utilisateur grâce aux flêches du clavier.
L'objectif est d'entrer dans le top 1 du scoreboard...

Capture
Le jeu est écrit en Javascript et interagit avec un back end server grâce à des APIs.
En particulier, on remarque l'appel à une API qui semble prometteuse :

1POST https://day3.challenges.xmas.root-me.org/api/scores
2{data:U2FsdGVkX1+aXbnHtRPR5L3Hu74Dl1T4EUBMetXr+eaZLuwjtzfAMUJE/YwqxnNxr2BLiPjB31e5IG4JjkoralCSX3i6fUvHYUqEWpzh1qqqeGEqb9n9EEbR7CAAF2BB8Oo3+bgO1aPORFdyzLf0qI1azYK3grvSCsCl34JDxlOL6BZtH9WcpNiQhVuMhR5X}

Exploit

Le décodage base64 de cette valeur data montre une chaine de bytes débutant par : Salted__ ce qui fait penser à un chiffrement symétrique.
Plongeons nous maintenant dans le code Javascript en cherchant des informations sur le chiffrement utilisé.

 1var Md = hf.exports;
 2const gf = Rf(Md)
 3  , Ud = "S4NT4_S3CR3T_K3Y_T0_ENCRYPT_DATA";
 4function Wd(e) {
 5    const t = JSON.stringify(e);
 6    return gf.AES.encrypt(t, Ud).toString()
 7}
 8function $d(e, t) {
 9    const r = Math.floor(Math.random() * 9) + 1
10      , n = `${e}-${t}-${r}`;
11    return {
12        checksum: gf.SHA256(n).toString(),
13        salt: r
14    }
15}
16async function Vd(e, t) {
17    const {checksum: r, salt: n} = $d(e, t)
18      , l = Wd({
19        playerName: e,
20        score: t,
21        checksum: r,
22        salt: n
23    });
24    try {
25        return await (await fetch("/api/scores", {
26            method: "POST",
27            headers: {
28                "Content-Type": "application/json"
29            },
30            body: JSON.stringify({
31                data: l
32            })
33        })).json()
34    } catch (i) {
35        return console.error("Error submitting score:", i),
36        {
37            success: !1
38        }
39    }
40}
41async function Qd() {
42    try {
43        return await (await fetch("/api/scores")).json()
44    } catch (e) {
45        return console.error("Error fetching scores:", e),
46        []
47    }
48}

On trouve les informations suivantes :

  • Le chiffrement est AES avec la clé en clair : S4NT4_S3CR3T_K3Y_T0_ENCRYPT_DATA
  • La composition du token avant chiffrement est : {playername, score, checksum, salt}
  • Le calcul d'un checksum ne permet pas de modifier la valeur score uniquement...

A ce stade, je n'ai pas retenu l'idée de continuer le reverse engineering mais plutôt de modifier l'execution du programme grâce au debugger chrome.
Voici le mode opératoire réalisé :

  • Placer un breakpoint ligne 73, colonne 34975 (juste avant l'appel à l'API)
  • Dans la console, on execute la fonction Vd("totoiste", score: 100000000000000000)
  • On va chercher le flag dans le résultat de la requête API POST ...
FLAG

The flag is : RM{S4NT4_H0PE_Y0U_D1DN'T_CHEAT}