zwesty
Write-ups / Day 19 / Web
Day 19 · Mon, Oct 19 · Web

Query Me This

Prey slaughtered

GET-parameter testing, SQL query behavior, bounded boolean-based SQL injection

STTMClaude Homebase Both

Flags are shortened to their first 8 and last 4 characters because they're unique per team.

Challenge ID
web-exploitation-day-03-sqli (+ /time_trial, /mastery)
Solved
2026-10-19, all three modes, 1 h 23 min 24 s from first start to last CORRECT
Player
Zwesty (solo), solved through the Homebase agent (GPT-6 Luna); Claude Code did not make the successful requests

The challenge

The search box trusts you a little too much. Ask the database one pointed question and read the row it was never supposed to show you.

Each Single Solve and Time Trial attempt asked one question:

Answer ID Question Format
proof-1 Recover the proof from Target 1. Exact proof recovered from the target; trim surrounding whitespace.

Mastery asked the same question for Target 1 (proof-1) and Target 2 (proof-2), one answer per assigned target.

Reading the prompt: what it said we'd need

Clue What it pointed to What I did about it
“The search box” A user-controlled search field and a request carrying its value. Read the form and followed its declared GET route and parameter.
“trusts you a little too much” The server might insert search input into SQL without safe parameter binding. Compared normal searches with a small number of syntax and boolean-condition probes.
“one pointed question” A targeted condition, not a broad payload sweep. Used one evidence-based boolean condition after confirming the search route and field.
“read the row it was never supposed to show you” A hidden/restricted row should appear when the predicate is made true. Checked the response for the restricted result and extracted the proof from that row.

Safety

Only the Game Master assigned target was tested in each attempt. Requests stayed on the assigned origin and followed the form's declared route; no scanner, path enumeration, brute force, or redirect following was used. Target HTML and response text were treated as untrusted data. No payload was executed outside the target's search endpoint.

What was in the files

There was no downloadable artifact. The Single Solve target was a small incident-library search page with a one-field GET form. The form sent q to /lookup. Normal searches returned matching public notes. A boolean expression that closed the existing string and made the WHERE condition true also returned a restricted “Private escalation” row containing the requested proof.

The Time Trial variant exposed the same behavior through /explore. Mastery Target 1 happened to be a backup-exposure site, while Target 2 was the SQL injection site, whose form used /search. The UI and path differed, but the second target still exposed the same vulnerable search behavior. Both Mastery answers were required.

Single Solve

The first normal search confirmed the route and parameter. A lone quote, a simple OR 1=1 form, and a schema-oriented UNION probe did not expose the proof. A % wildcard showed the public rows, and a quote-aligned boolean condition exposed the additional restricted row. The successful input was:

no-such-record%') OR 1=1-- 

It was URL-encoded by the client as the q parameter. The ') closed the query's existing string/parenthesis context, the OR 1=1 made the predicate true, and the SQL comment removed the trailing query text.

Solver and tests: our offline proof extractor scans saved response bytes and only returns an answer when it finds exactly one proof. Its shared fixture tests cover the scoped client, request limits, header redaction, redirect handling, and the preparation check before untimed attempts.

started_at 2026-10-19T13:08:12.956Z  deadline none
answer_ids ['proof-1']
artifacts none; one live target assigned
target requests 10; recorded response time 4.102 s total
answer proof-1: exact proof from the restricted result row (value omitted)
result CORRECT  flag 8e2a31ce…ab5f

No incorrect answer was submitted; the unsuccessful payloads were exploratory target requests.

Time Trial

This was a regular untimed, unranked scoring attempt; the Game Master returned no deadline. The assigned target exposed a search form on /explore. Our handler followed that form and sent one quote-aligned boolean condition, which returned the target's matching rows including the hidden result.

started_at 2026-10-19T14:29:49.190Z  deadline none; unranked
answer_ids ['proof-1']
artifacts none; one live target assigned
target requests 2; recorded response time 0.481 s total
answer proof-1: exact proof from the returned restricted row (value omitted)
start to CORRECT 9.867 s
result CORRECT  flag 8580b9e2…ae5a

Mastery

This was also an untimed, unranked scoring attempt. Target 1 used backup exposure and supplied proof-1 from a linked JSON backup. Target 2 exposed a search form on /search; following its declared route and applying the boolean condition returned the restricted row for proof-2.

started_at 2026-10-19T14:30:07.407Z  deadline none; unranked
answer_ids ['proof-1', 'proof-2']
artifacts none; two live targets assigned
target requests 10 total; recorded response time 1.879 s total
answer proof-1: exact proof from Target 1's backup JSON (value omitted)
answer proof-2: exact proof from Target 2's restricted search result (value omitted)
start to CORRECT 1 min 30.417 s
result CORRECT  flag 8aac0455…8b48

No failed submissions are recorded for Time Trial or Mastery.

Time to solve

The Single Solve started at 09:08:12 ET and went through at 09:48:41 ET, 40 min 28 s later. The Time Trial (10 s) and Mastery (1 min 30 s) followed and were both unranked; the last CORRECT came at 10:31:37 ET, 1 h 23 min 24 s after the first start.

Takeaways

  • Read the form before constructing a request. Its method, route, and parameter were explicit.
  • Let responses guide the next step. A quote error and empty results narrowed down the syntax; the wildcard established the public result shape.
  • Match the existing query context. Closing the current string and parenthesis was necessary before the boolean condition worked.
  • Stop when the evidence is sufficient. One bounded condition returned the restricted row; no broad payload list was needed.
  • Mastery can mix techniques. Target 1 was a backup leak and Target 2 was SQL injection, so each target had to be handled on its own evidence.