Puzzleshot #026 - Quack Attack

raw data→duckdb→quack→duckdb→result

If a duck quacks in the forest…

Last puzzle Quack let a writer and a dashboard share one DuckDB database. This puzzle we eavesdrop on the conversation between them.

The setup:

Run the writer from last puzzle’s solution, token and all, and run the dashboard query against it. Quack listens on port 9494 by default.

The challenge:

Round 1: Capture the loopback traffic while the dashboard runs its query. Wireshark works, and so does tcpdump. A filter on tcp port 9494 cuts out the noise. Can you find the token in the capture?

Round 2: Look at every request in the conversation, not just the first one. Is the token in all of them? If not, how does the server know who’s asking?

Bonus: Can you read the SQL the dashboard sent? What about the rows that came back?

Things to consider:

Only capture traffic you’re allowed to look at. Your own machine is the right place for this one.

Reveal solution

This puzzle we listened in on a dashboard talking to a Quack server. Tested with DuckDB 2.0.0 on a Mac, capturing with Wireshark on the loopback interface with the filter tcp port 9494.

Round 1 - the token:

Pick any packet and use Follow TCP Stream. The conversation is plain HTTP/1.1: a series of POST requests to /quack with a binary body. The first one looks like this:

POST /quack HTTP/1.1
Host: localhost:9494
User-Agent: duckdb/v2.0.0(osx_arm64) cli ...
Content-Type: application/octet-stream

(binary framing around the strings my_token, v2.0.0, osx_arm64 and a UUID)

The token is right there in the body, readable as plain text, along with the client’s version and platform. So yes, the token travels in cleartext.

Round 2 - the later requests:

The token isn’t in them. The server’s reply to that first request includes a 32-character hex string that looks like a session ID, and every later request carries that string in its body instead of the token. That’s how the server knows who’s asking.

The catch is that the session ID is cleartext too. Someone who joins the capture late never sees the token, but they see the session ID on every request. I didn’t test whether the server would accept that ID from a brand-new connection. That’s the obvious next experiment, on your own machine.

Bonus - the SQL and the results:

Both are readable. The dashboard’s query shows up verbatim in a request body, and the response carries the column names and the values. The CLI also sends a couple of metadata queries of its own when it connects, so you’ll see SQL you never typed, and the table’s CREATE TABLE statement comes back in one of the responses. Requests are application/octet-stream and responses are application/vnd.duckdb, binary framing around plain strings.

The things to consider:

What would someone on the same network see? Everything above: the token, the session ID, the SQL and the results, if the traffic crosses a network without encryption. Loopback hides nothing, and a network wouldn’t either.

Does one cleartext request give them what they need to impersonate the dashboard? Every request carries a credential, the token in the first and the session ID in the rest. Whether the session ID can be replayed from elsewhere is something to investigate further.

What would TLS in front of Quack change? All of it would become unreadable on the wire. Quack leaves encryption to you, and its docs have a reverse proxy guide.

The takeaway:

Quack over HTTP behaves like any other plain HTTP service. Keep it on localhost, or put TLS in front before it goes anywhere else.