INTRODUCTION

I participated in the NahamCon CTF, with AfriCC-mini-team1. Since I didn’t capture many files and screenshots during the event, this write-up will be brief and cover only one challenges I tackled.

SNAD

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

Solution

Upon visiting the site, we notice an interactive particle system that's responsive to mouse movement.

Inspecting the JavaScript code reveals a POST request made to /api/verify-ctf-solution with the following payload:


let t = particles
    .filter((e) => e.settled)
    .map((e) => ({
        x: Math.floor(e.x),
        y: Math.floor(e.y),
        colorHue: e.colorHue,
    }));

let o = await fetch("/api/verify-ctf-solution", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
    },
    body: JSON.stringify({
        particleData: t,
    }),
});
                    

Initially, manual POST requests return Invalid submission data. Digging deeper, we find a checkFlag() function that validates particle positions:


function checkFlag() {
    if (flagRevealed) return;
    let e = 0, t = [];
    for (let o of targetPositions) {
        let i = false;
        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 = true;
                    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;
                }
            }
        if (i) e++;
    }
    e >= 7 && ((flagRevealed = true), console.log("🎉 All positions correct! Retrieving flag..."), retrieveFlag());
}
                    

Turns out the key is aligning particles to specific targetPositions with matching colorHue. To spoof this:

Exploit Script

In the browser console, paste this:


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();
}
placeCorrectParticles();
                    

After executing the script, the console logs the flag:

Flag: flag{6ff0c72ad11bf174139e970559d9b5d2}