Blind SQL injection against a live storefront with a 40,000-order day
Verdant Retail runs a public storefront behind a WAF that blocks most commodity payloads. On day 2 of a five-day test you confirm a time-based blind SQL injection in /search?q=: the payload ' AND SLEEP(5)-- returns in 5.4 s, while the same request against a static page returns in 0.2 s. The database sits behind the same cluster that serves roughly 40,000 live orders per day, and the client's only instruction is "prove it, don't affect customers". You also note a reflected XSS in the coupon field that the WAF blocks, and an invoice generator at /invoice?file= that resolves a single ../ traversal to /etc/passwd but returns 403 at two levels. Three days remain. A junior tester asks whether to run sqlmap with --dump overnight to "make the report credible".
1. How do you substantiate the blind SQL injection to a sceptical developer without extracting customer data or degrading the service? Specify the requests you would send and your stop conditions.
Read the worked answer
Answer the junior tester first: no --dump. Dumping tables on a production cluster serving live orders is both a data-protection incident and an availability risk, and it adds nothing to the finding.
Preferred proof: out-of-band confirmation. If the DBMS can make an outbound DNS lookup (MSSQL xp_dirtree, Oracle UTL_HTTP, MySQL LOAD_FILE to a UNC path), a single request producing one DNS interaction on your collaborator domain proves code-path execution with near-zero load and zero data extraction. One request, one callback, screenshot, done.
Fallback: bounded inference.
- Reproduce timing three times to exclude coincidence: baseline, injected, baseline.
- Recover only non-sensitive metadata one character at a time with a hard cap:
current_user,db_name(),@@version. Cap at roughly 20-30 requests total. - Prefer boolean-based inference with a 1-2 s delay over 5 s sleeps; shorter delays mean less lock and connection-pool pressure.
Stop conditions, agreed with the NOC before you start:
- Any 5xx rate above baseline, or p95 latency on /search rising above an agreed threshold.
- DBA or NOC request to pause.
- Any query that would traverse customer tables, order tables, or credentials - that is out of bounds regardless of authorisation.
Report content: exact request, timestamp, response times, DBMS version string, and the observation that the WAF did not inspect this parameter. Business impact follows from the DB account's privileges, which you can determine from current_user without touching a single row of customer data.
2. The LFI resolves at one traversal depth but 403s at two. Do you pursue LFI-to-RCE, and how do you decide? What are your safe boundary tests?
Read the worked answer
First classify the sink; RCE is a later question. One depth working and two failing usually means a naive filter stripping ../ once, or normalisation of the path - not a secure implementation. Test with encoding variants (%2e%2e%2f, ....//, ..%2f, double-encoding) using a non-sensitive target file so a success is provable without touching secrets: /etc/hostname, /proc/self/cmdline, or the application's own config path.
Determining read vs include matters more than RCE:
- PHP wrappers:
php://filter/convert.base64-encode/resource=indexdistinguishesinclude()fromreadfile(). A base64 blob of source is a serious finding on its own and requires no writes. - If it is
include(), RCE paths exist (log poisoning,php://input, session files, uploaded avatars).
Decision rule: pursue RCE only if (a) you have written permission to write files or poison logs on this host, and (b) it can be done without user-visible impact. Writing a webshell into a production webroot is a system modification - that is a separate authorisation from "test the app". Log poisoning writes attacker strings into access logs that may feed a SIEM and trigger an incident response mid-engagement.
Safe boundary line I would draw here: prove arbitrary file read up to a sensitive-but-non-secret file, prove wrapper behaviour, and stop. Report the include() sink as high severity with a clear explanation that RCE is reachable. Spend the remaining days on the SQL injection chain, which already proves unauthorised data access.
3. The reflected XSS in the coupon field is blocked by the WAF. What evidence is worth pursuing, and how do you rate a finding you cannot demonstrate executing?
Read the worked answer
Time-box it to about two hours and stop. Reflected XSS behind a blocking WAF is the lowest-value item on this engagement compared with a confirmed SQL injection.
Worth pursuing:
- Determine the reflection context. Send a benign canary string with HTML-significant characters and inspect the raw response: if
<and"come back unencoded anywhere in the body or in a JSON/attribute context, you have demonstrated the missing output encoding. That is the actual defect; the WAF is a compensating control. - Check contexts the WAF does not inspect: JSON request bodies, custom headers reflected into the page, POST-only parameters, and alternate encodings the client accepts.
- If a staging environment exists, test there - execution proven in staging plus unencoded reflection in production is a legitimate High/Medium.
Not worth pursuing: days of WAF bypass fuzzing against a production storefront. That is a different engagement (WAF assessment) and it generates noise in the client's SIEM.
Rating if execution is never proven: report it, but honestly. Do not score it Critical. Rate it Medium at most, describe it as "unencoded output confirmed; script execution not achieved due to WAF filtering", and state that the WAF is not a fix because payload transformation (encoding, context switching, new parameters) is an attacker-controlled variable.
The reasoning to put in the report: the underlying control gap is missing output encoding in the coupon handler. A WAF rule is a signature of past attacks; output encoding is a structural fix. The remediation is the same either way, so the finding does not lose value by being scored conservatively - it gains credibility, which is what makes the client fix it.