Skip to content

Wani CTF 2024: Bad Worker

TL;DR

  • Offline-capable Blazor WebAssembly app uses a service worker.
  • Service worker rewrites requests for FLAG.txt to DUMMY.txt.
  • Logic executes entirely client-side.
  • Breakpoint set before fetch(request) in service worker.
  • Manually overwrite request back to FLAG.txt in debugger.
  • Resume execution to retrieve the flag.

Description

We created a web application that works offline.

Recon

GithubPages site with two endpoints; /counter and /fetchdata.

The counter page contains a button (Click me), which increments a counter.

The second endpoint has a button saying Fetch FLAG.txt. However, if we click the button, it returns FLAG{This is not the flag!!}.

Solution

Checking the debugger (devtools), there is a folder _framework containing files like blazor.webassembly.js and dotnet.wasm.

<script src="_framework/blazor.webassembly.js"></script>
<script>
    navigator.serviceWorker.register("service-worker.js");
</script>

I visited the Application tab and see the service-worker.js script is running.

Service worker registered in application tab

It also says I should open about:debugging for some reason, so I do that 😃

Firefox about:debugging page showing service worker

When I click inspect, it allows me to view the service-worker.js source code.

Inspecting service-worker.js source code

Here's the interesting part.

async function onFetch(event) {
    let cachedResponse = null;
    if (event.request.method === "GET") {
        const shouldServeIndexHtml = event.request.mode === "navigate";
        let request = event.request;
        if (request.url.toString().includes("FLAG.txt")) {
            request = "DUMMY.txt";
        }
        if (shouldServeIndexHtml) {
            request = "index.html";
        }
        return fetch(request);
    }

    return cachedResponse || fetch(event.request);
}

Thankfully, you don't need to be an expert in JavaScript to conclude that our request for "FLAG.txt" is being replaced with "DUMMY.txt".

I set a breakpoint at the following line.

return fetch(request);

Notice that request = "DUMMY.txt, since it was changed on line 58.

Debugger showing request rewritten to DUMMY.txt

I switch to the console and set request to "FLAG.txt"

Manually overriding request variable to FLAG.txt

Now, if we go back to the debugger and resume execution, we'll find that the correct flag was delivered to the webpage.

Correct flag returned after resuming execution

Flag: FLAG{pr0gr3ssiv3_w3b_4pp_1s_us3fu1}