SQL Injection: Still Top of the Charts
Days 4 and 5 were about authorization, reading and doing things you should not. Today we switch to injection, where you make the application run code it never meant to run, and we start with the oldest and still one of the highest impact bugs in existence: SQL injection. Decades old, endlessly written about, and still turning up on real programs for real four and five figure bounties, because one forgotten parameter is all it takes.
This is the third vulnerability class in the series. SQLi is worth learning early because it teaches the injection mindset, data being treated as code, which underlies a whole family of bugs, and because the impact is often total: the entire database, every user, every secret, in one finding.
Inside scope only. SQL injection can read and in some cases modify a live database, so you confirm the vulnerability with the lightest possible proof and never dump real user data or alter anything. A time delay or a version string is proof. Exfiltrating a customer table is damage.
What SQL injection actually is
A web app builds SQL queries to talk to its database. When it builds a query by gluing your input directly into the query string, your input can stop being data and start being SQL.
Picture a login that looks up a user by building a query that embeds whatever you typed into the username field. Type a normal username and it works. Type input containing SQL syntax, a quote that closes the string and then more SQL, and the database executes your addition as part of the query. The application meant your input to be a value. The database read it as code. That confusion, data becoming code, is injection, and SQL injection is that confusion aimed at the database.
The reason it is catastrophic: the database holds everything. Credentials, personal data, payment details, session tokens. A query you control can ask the database for any of it. The Grab report put it plainly, the SQL injection granted access to all data in the website’s database, $4,500 bounty. Report: https://hackerone.com/reports/273946
The types you will meet
SQLi comes in flavors by how you get the answer back.
In band, error or union based. The app shows you query results or database errors directly, so you read the answer right in the response. Error based, you make the database emit an error containing the data. Union based, you append a query whose results appear where the app’s results normally show. The fastest to exploit when the app is chatty.
Blind, boolean or time based. The app shows you nothing directly, no results, no errors. You infer the answer indirectly. Boolean based, you craft input that makes the page behave one way if a condition is true and another if false, and read the database one yes or no at a time. Time based, you make the database pause if a condition is true, and read the answer from how long the response takes. Slower, but just as total. The Starbucks finding was time based: the hunter crafted input that delayed the response, confirmed the injection from the pause, and the database version came back. Report: https://hackerone.com/reports/531051
Out of band. The database makes a network request you observe elsewhere, used when the response channel gives nothing. Rarer, but worth knowing exists.
The DoD reports show these are not theoretical: multiple disclosed blind SQLi findings, confirmed with nothing more than a deliberate time delay, because a two second hang that only happens when your condition is true is undeniable proof without touching a single row of real data.
Where to look, pointed at your recon
SQLi lives anywhere input reaches a query, which is almost anywhere. Point this at the parameter rich endpoints your recon surfaced.
Every parameter is a candidate. URL query parameters, POST body fields, JSON values, and crucially the ones people forget: HTTP headers. Several disclosed DoD reports found SQLi in the User-Agent header, because the app logged it into a database without sanitizing. Your recon params.txt is the starting list, and headers are the surface people skip.
The high probability spots:
- Search fields, they almost always build a query from your term
- Filters, sorts, and pagination, id, sort, order, category, often glued into SQL
- Anything taking a numeric ID, the classic WHERE id = clause
- Login and lookup forms
- Any older or forgotten endpoint from your recon, legacy code predates parameterized queries
The forgotten endpoints from Part 1 and 2 are prime, because modern frameworks mostly prevent SQLi by default, so the bugs cluster in old, custom, hand written query code, which is exactly what sits on the forgotten hosts your recon found.
How to hunt it, step by step
Step 1: find input that reaches a query. From your recon, list every parameter, body field, and relevant header on in scope endpoints. These are your test points.
Step 2: probe for the confusion. Send input that would break or change a SQL query if your data is being treated as code. The classic first probe is a single quote, which, if unsanitized, often breaks the query and produces an error or a changed response. A changed behavior from a syntax character is the first signal.
Step 3: confirm safely. Once something looks off, confirm with the lightest proof:
- If errors show, a database error naming SQL syntax confirms it
- If nothing shows, use a time based probe: input that makes the database pause only if it is executing your SQL. A reliable delay that tracks your condition is proof, and it touches no data
Step 4: assess without overreaching. You have proven injection. For the report you need to show impact, but impact proof should be minimal: the database version, the current database name, the current user. These demonstrate full control without exfiltrating anyone’s data. Never dump real user tables. The version string is enough to prove the database is yours to query.
Step 5: on tooling. sqlmap is the standard tool and nearly every disclosed report uses it to confirm and characterize the injection. It automates detection and safe proof like version and current-db retrieval. Learn to probe manually first so you understand what it does, then use it to confirm efficiently, and always keep it to proof, with flags that retrieve the database version or name rather than dumping data.
Writing the SQLi report
The structure that gets SQLi paid:
Title: SQL injection in [parameter] at [endpoint]
Summary:
One sentence: which parameter, which endpoint, what type of SQLi.
Steps to Reproduce:
Send [request] with [probe] in [parameter].
Observe [error / time delay / changed response] confirming injection.
Confirm database type/version via [minimal proof].
(Exact requests, the proof, screenshots. Keep proof to version/current-db.)
Impact:
Full read access to the database: all user data, credentials,
secrets. Note if write access is possible. This is typically
critical severity.
Remediation:
Use parameterized queries / prepared statements so input is
always treated as data, never as SQL.
The strong disclosed reports share a discipline worth copying: they prove the bug with a time delay or a version string, state the impact as full database access, and stop. They do not dump customer data to make the point, because the proof already made it. That restraint is both ethical and what keeps you inside the rules.
The mindset: data versus code
SQLi teaches the injection lens that recurs across bug classes: wherever your input crosses into something that gets executed or interpreted, SQL, OS commands, templates, markup, ask whether the boundary between your data and that code is enforced. SQLi is that question aimed at the database. Command injection aims it at the shell, XSS at the browser. Learn to see the data-becoming-code boundary here and you will spot the whole family.
Your recon found the parameters and the forgotten endpoints. SQLi is one of the first and highest impact things to test on them, and a confirmed one is often a critical finding.
Homework for Day 6
- From your recon
attack_surface.md and params.txt, list in scope test points: parameters, body fields, and headers like User-Agent.
- Probe each with a syntax character and watch for errors or changed behavior. Where nothing shows, try a time based probe.
- For anything that responds, confirm safely with a database version or current-db proof. Stop there, do not dump data.
- Learn sqlmap by running it against a deliberately vulnerable practice target you own, not a live program, to understand what it automates.
- Read all three disclosed reports end to end: Grab full-DB SQLi (https://hackerone.com/reports/273946), Starbucks time-based (https://hackerone.com/reports/531051), and a DoD User-Agent SQLi (https://hackerone.com/reports/313037). For each, note where the input entered the query and how impact was proven minimally.
A confirmed SQL injection on an in scope target is typically critical. Document it with the minimal proof and the parameterized-queries remediation, and write it up in the report shape above.
Day 7 moves to the injection class that runs in the victim’s browser instead of the server’s database, Cross Site Scripting, the most common bug on the web, with a real disclosed report broken down end to end.