SNAD

Writeup for SNAD (Web) - NahamCon CTF (2025) 💜

Description

No, it's not a typo. It's not sand. It's SNAD. There's a difference!

Solution

We load the website and see a particle generator operated with the mouse.

The JS contains a POST request to /api/verify-ctf-solution which includes a JSON object ({'particleData': t})

let t = particles
        .filter((e) => e.settled)
        .map((e) => ({
            x: Math.floor(e.x),
            y: Math.floor(e.y),
            colorHue: e.colorHue,
        })),
    o = await fetch("/api/verify-ctf-solution", {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify({
            particleData: t,
        }),
    });

I try to send various POST requests using this information but always get Invalid submission data. Continuing to read the JS code, there is a checkFlag function which calls retrieveFlag at the end.

function checkFlag() {
    if (flagRevealed) return;
    let e = 0,
        t = [];
    for (let o of targetPositions) {
        let i = !1;
        for (let r of particles)
            if (r.settled) {
                let l = dist(r.x, r.y, o.x, o.y),
                    s = min(abs(r.colorHue - o.colorHue), 360 - abs(r.colorHue - o.colorHue));
                if (l < 15 && s < 20) {
                    (i = !0),
                        t.push({
                            targetPos: `(${o.x}, ${o.y})`,
                            targetHue: o.colorHue,
                            particlePos: `(${Math.floor(r.x)}, ${Math.floor(r.y)})`,
                            particleHue: r.colorHue,
                            distance: Math.floor(l),
                            hueDifference: Math.floor(s),
                        });
                    break;
                }
            }
        i && e++;
    }
    e >= 7 && ((flagRevealed = !0), console.log("🎉 All positions correct! Retrieving flag..."), retrieveFlag());
}

I try to execute retrieveFlag in the console but it's not that simple! The particles need to be in the correct positions. Here's a script that will do that, we just paste it into the browser console.

function placeCorrectParticles() {
    for (let i = 0; i < 7; i++) {
        let target = targetPositions[i];
        particles.push({
            x: target.x + Math.random() * 10 - 5,
            y: target.y + Math.random() * 10 - 5,
            colorHue: (target.colorHue + Math.random() * 10 - 5 + 360) % 360,
            settled: true,
        });
    }
    checkFlag();
}

Then call the function.

placeCorrectParticles();

Flag: flag{6ff0c72ad11bf174139e970559d9b5d2}

Last updated