Xmas-Rootme - (03) Generous Santa

25/12/2024

Day 3 !!! here we get a web game and need to beat a incredibly high score


Today's challenge was a fun one, we have a web game :

image 1

We need to catch as many gifts as possible and at the end, there is a scoreboard with all the best scores.

image 2

Of course by the time I write the write up many incredibly funny people put their score 🥸 but initially only Santa's score was really high and playing the game legitimately barely gives you 900 points (trust me I did a lot of attemps 🤓)

First intuiton was to look at the network request made to update the score and just change the score in it.

image 3

Unfortunately the data is encrypted and not encoded so we cannot just change it and send it. Lets take a look at the code and try to see how it works. The js file is only 16 000 so it shouldn't be so long to read 😢. Fortunately for us, only the sent data interest us so we just need to look at the fetches of the code (and there is only 4 of them 🥳).

async function Vd(e, t) { const { checksum: r, salt: n } = $d(e, t); const 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 } } }

This function is the function that interest us since it is the one sending the results. The username and score are passed to the Wd function together with a checksum and salt generated from the $d function, let's take a look at them.

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 } }

Bingo, we have everything we need, the encryption key, the salt generator, and the checksum generator. Now we just need to change the t value (score) before sending and send the score. To do so I just put a breakpoint before Wd(), to make sure all the imports and variables are up to date, and then paste this code in the console.

function Wd(e) { const t = JSON.stringify(e); return gf.AES.encrypt(t, 'S4NT4_S3CR3T_K3Y_T0_ENCRYPT_DATA').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); const 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 } } } Vd("atalata", 9999999).then(data => console.log(data));

And we get a response with the flag :)

image 4