Post

Root-Me Xmas - Santa's Magic Sack

Challenge

Upon connecting to the https://xmas.root-me.org/challenges web page and selecting the Santa’s Magic Sack challenge, we are presented with the following infos :

Generous Santa

Upon reading the challenge description, we understand that we will be face with a web game, knowing this the chance of it being about manipulating the game data with the browser’s developer tools is high.

Let’s see what this game is about.

Go to https://day3.challenges.xmas.root-me.org/.

User registration

We enter a random nickname and we start the game.

Game

The format of the game is a simple catcher game where we have to catch the gifts that fall from the sky.

After playing a bit with it until the end of the timer we are presented with the following screen :

Game Over

And just after we get to the leader scoreboard :

Leaderboard

The top player is santa with a score of 133337 points, has stated in the challenge description, we need to beat this score to get the flag.

Exploitation

First reflex is to check the game’s source code, we can do this by opening the browser’s developer tools and going to the sources tab.

Looking at the network tab, we can see that the game is making requests to the server, we can see that the game is making a POST request to the /api/scores with a weird looking payload.

API scores

I’ve tried to pass it to cyberchef to see if it was encoded in some way, but I did not find anything.

When opening the debugger, we can observe that the game is using a javascript file called index-DHkGdvNB.js, let’s take a look at it.

Game JS

Since we have noticied that the game is making a POST request to the /api/scores endpoint, we can search for this string in the debugger.

We find the following code :

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

Okaaaay, so the game is encrypting the data with AES and a secret key, then it is hashing the data with SHA256 and sending it to the server.

The function Vd is the one that is sending the score to the server. We need to know when and where this function is called.

Upon searching we arrive at the following code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const f = async() => {
    p.current = !0,
    h.current &&
    cancelAnimationFrame(h.current);
    const v = s.current;
    m(!0);
    try {
      const C = await Vd(e, v);
      C.isNewRecord &&
      C.flag &&
      y(C.flag)
    } catch (C) {
      console.error('Error submitting score:', C)
    }
    setTimeout(() => {
      m(!1),
      t(v)
    }, 5000)
  };

The line const C = await Vd(e, v); is calling the function Vd with the player’s name and the score.

If we break before the function get executed and change the score to 133338, we should be able to get the flag.

Let’s try this 🥷 !

variable

We set the breakpoint at line 15788 and we change the value of the s.current variable to 133338 and we let the game run.

And we get the flag 🚩 !

flag

This post is licensed under CC BY 4.0 by the author.