Lion Cipher Lab
Exploit and fix a second-order (stored) SQL injection — the one a parameterized INSERT does not stop
- AppSec / DevSecOps
- Intermediate
- ~40 min
- Free · no account required
Why it matters
Teams learn 'use parameterized queries' and tick the box — then test only the page where input arrives, see it is safe, and move on. Second-order SQL injection lives in the gap that leaves: the value is stored harmlessly through a parameterized INSERT, passes every input-validation check at the front door, and sits in the database looking like ordinary data until a completely different feature — a profile update, a password change, a report — reads it back and builds a query by gluing it in. Then it fires, often with higher privilege than the page that stored it. Real breaches have ridden…
What you'll learn
- the difference between first-order SQL injection (input hits a query immediately) and second-order / stored SQL injection (input is stored safely, then detonates when a LATER query reads it back)
- building a tiny vulnerable app with Python's built-in sqlite3 and proving a stored username like admin'-- is saved verbatim by a parameterized INSERT yet still owns another account later
- demonstrating real impact: taking over the admin account, and mass-resetting every user's password through one stored payload
- seeing exactly why front-door input validation and 'we only tested the entry point' both miss this bug
- fixing it correctly by parameterizing EVERY query that touches the value — not just the first — and proving the identical payload is now inert
- the honest limits: parameterization is the fix (not blocklists or input-time escaping), and an ORM still injects the moment you drop to raw SQL
Steps in this lab
- First-order vs second-order: the one distinction this whole lab turns on
- When you'd actually meet this — and when you would not
- Set up: confirm Python and make a throwaway folder (no installs)
- Build the app: a schema, a seeded admin, and a SAFE parameterized registration
- Store the malicious username — and watch the parameterized INSERT save it verbatim
- Build the VULNERABLE sink: a password change that reads the stored name back and concatenates it
- Exploit 1: take over the admin account
- Exploit 2: one stored value, every password reset
- Why 'validate and escape on input' is the wrong layer
- Fix it: parameterize the SECOND query too — then replay the exact payload
- Honest limits, and the way a pro leaves it