TUCTF 2023 Writeups

Posted on 2023-12-03 in English

I’ll be sharing some solutions to the challenges I solved at TUCTF 2023

Bludgeon the booty

This challenge involved connecting to a server and rotating a combination lock.
Because of the constant connection issues during the CTF, I found that building a new connection every time was more reliable (a new combination is generated on connect).
You may need to restart this program multiple times.

const net = require("net");

class Semaphore {
    /**
     * Creates a semaphore that limits the number of concurrent Promises being handled
     * @param {*} maxConcurrentRequests max number of concurrent promises being handled at any time
     */
    constructor(maxConcurrentRequests = 1) {
        this.currentRequests = [];
        this.runningRequests = 0;
        this.maxConcurrentRequests = maxConcurrentRequests;
    }

    /**
     * Returns a Promise that will eventually return the result of the function passed in
     * Use this to limit the number of concurrent function executions
     * @param {*} fnToCall function that has a cap on the number of concurrent executions
     * @param  {...any} args any arguments to be passed to fnToCall
     * @returns Promise that will resolve with the resolved value as if the function passed in was directly called
     */
    callFunction(fnToCall, ...args) {
        return new Promise(async (resolve, reject) => {
            this.currentRequests.push({
                resolve,
                reject,
                fnToCall,
                args,
            });
            await this.tryNext();
        });
    }

    async tryNext() {
        if (!this.currentRequests.length) {
            return;
        } else if (this.runningRequests < this.maxConcurrentRequests) {
            let { resolve, reject, fnToCall, args } =
                this.currentRequests.shift();
            this.runningRequests++;
            let req = fnToCall(...args);
            req.then((res) => resolve(res))
                .catch((err) => reject(err))
                .finally(async () => {
                    this.runningRequests--;
                    await this.tryNext();
                });
        }
    }
}

function clientele(numbero) {
    return new Promise((resolve) => {
        const client = new net.Socket();
        // nc chal.tuctf.com 30002
        client.connect(30002, "chal.tuctf.com", function () {
            console.log("Connected");
        });

        function rotateWheel(id) {
            console.log("Rotated wheel " + id);
            client.write("1\n");
            client.write(id + "\n");
            client.write("+\n");
            // Rotated wheel
        }

        function initializeTo(w1, w2, w3, w4) {
            // Rotate wheel from 0 positions to w1, w2, w3, w4
            for (let i = 0; i < w1; i++) {
                rotateWheel(1);
            }
            for (let i = 0; i < w2; i++) {
                rotateWheel(2);
            }
            for (let i = 0; i < w3; i++) {
                rotateWheel(3);
            }
            for (let i = 0; i < w4; i++) {
                rotateWheel(4);
            }
        }

        let initial = true;

        client.on("data", function (data) {
            if (initial) {
                initial = false;
                initializeTo(
                    numbero % 10,
                    Math.floor(numbero / 10) % 10,
                    Math.floor(numbero / 100) % 10,
                    Math.floor(numbero / 1000) % 10,
                );
                resolve();
            }
            if (data.toString().match(/TUCTF/)) {
                console.log(data.toString());
                resolve(data.toString());
                client.destroy();
            }
        });
    });
}

async function main() {
    let smp = new Semaphore(10);

    for (let i = 9999; i > 0; i--) {
        await smp.callFunction(clientele, i);
    }
}

main();

Hacker-Typer

I solved this challenge using the browser console:

function handleNext(obj) {
    console.log(obj);
    fetch("https://hacker-typer.tuctf.com/check_word", {
        credentials: "include",
        headers: {
            Accept: "*/*",
            "Content-Type": "application/x-www-form-urlencoded",
            "Sec-Fetch-Dest": "empty",
            "Sec-Fetch-Mode": "cors",
            "Sec-Fetch-Site": "same-origin",
            "Sec-GPC": "1",
        },
        referrer: "https://hacker-typer.tuctf.com/",
        body: "word=" + encodeURIComponent(obj["next_word"]),
        method: "POST",
        mode: "cors",
    })
        .then((res) => res.json())
        .then((onj) => {
            setTimeout(() => handleNext(onj), 1);
        });
}
handleNext({ next_word: "ENTER_FIRST_WORD_HERE" });