CEH study materials
CEH study resources

The Login Form That Answers in Two Seconds

CEH · Put your knowledge to work in context.

All case studies

The Login Form That Answers in Two Seconds

During a two-hour window against a production e-commerce site (no staging available, rate limit of five requests per second agreed in the rules of engagement), you are testing a login form and a product page. A single quote in the username returns exactly the same generic "invalid credentials" page as a wrong password, byte-for-byte. Sending a five-second database sleep in the username field delayed the response on 2 of 10 attempts; the other 8 came back in roughly the same 200 ms as a normal failure. Baseline p95 latency is 190 ms with occasional 1.5 s spikes from the payment provider call. A WAF returns 403 for classic tautology payloads. Separately, /product?id=17 returns 4,102 bytes and /product?id=17' returns 5,891 bytes.

1. Is the login form injectable? Explain how you would decide, given the noise.

Read the worked answer

Current evidence is suggestive but not conclusive - 2 of 10 delays is not proof.

The distractors are real: the baseline already has 1.5 s outliers from a downstream payment call, so a handful of slow responses can be produced by the environment alone. A 5 s delay landing inside a 1.5 s noise band is distinguishable, but only if it is repeatable and tied to the payload.

How to decide within the rules:

  1. Characterise the baseline first. Send 20 benign failed logins and record the distribution. If you ever see a 5 s response without a sleep payload, the signal is not clean and you must change the test.
  2. Use differential testing. Pair each sleep payload with a control payload of the same length and same character classes that contains no sleep. If the sleep version is consistently slower than its own control, the effect is the payload.
  3. Vary the delay. Try 2 s and 8 s. A real injection scales with the requested delay; network jitter does not.
  4. Respect the rate limit. Twenty to forty carefully paired requests at five per second is seconds of traffic - far safer than a fuzzer, and far more defensible in the report.
  5. Prefer the boolean channel if one exists. Confirming via a sleep is slower and noisier than confirming via a stable content difference.

Conclusion wording: "Time-based injection is indicated at the login form; confirmation requires the differential testing above." Do not write "confirmed" on 2/10.

2. `/product?id=17` changes size with a trailing quote. How do you interpret that, and what is the minimum proof of impact you should collect?

Read the worked answer

That is a much stronger signal than the login form, and it should become your primary test.

A 1,789-byte difference between 17 and 17' at the same endpoint means the parameter is being interpreted in a way that changes query behaviour - either an error page or a different result set. That is the boolean channel you want, and it is easier to control than timing.

Method:

  • Establish the two stable states (true/false or normal/error) and measure them several times to confirm the byte counts are repeatable, not cached or randomised.
  • Test a pair of conditions that must differ logically, e.g. a comparison that is true versus the same comparison made false. If the response tracks the truth value, you have confirmed injection.
  • Keep payloads inside the agreed rate limit; no UNION dump, no sqlmap --dump, no table extraction.

Minimum proof of impact:

  1. Database user and version - establishes the privilege context.
  2. Confirmation that read access extends to a table you can name but do not dump - one row, one column at most, chosen to be non-sensitive.
  3. A screenshot of the two differing responses and the exact requests. That is the whole report: it proves execution of injected SQL without exfiltrating customer data.

Stop there. The remaining exploitation steps add risk and courtroom exposure, not evidence.

3. The WAF is blocking classic payloads. What do you do, and where is the line?

Read the worked answer

The WAF shapes your technique but does not change your mandate - and it is itself a finding.

Within the agreed rules:

  • Start with encoding and syntax variation (case, comments, whitespace, alternate encodings, equivalent operators) at a low request rate. This is normal testing, not evasion-for-its-own-sake.
  • Do not use automated tamper-script sweeps unless the rules of engagement explicitly cover WAF evasion. Thousands of blocked requests is both a rate-limit problem and an operational disturbance.
  • Do not attempt to disable, bypass via origin IP, or attack the WAF itself. Reaching the origin directly around the WAF would both breach scope and invalidate the test - you would be proving a vulnerability that customers cannot actually reach.

Where the line is:

  • Every request must be one you could explain to the client's SOC verbatim. If you would not want it in the log, do not send it.
  • Report the blocking behaviour as intelligence: which payload classes are caught, and - critically - the fact that the WAF block does not fix the bug. The underlying query is still concatenating input; the fix is parameterised queries.
  • Also report that the WAF clearly does not stop everything, since your time-based attempts reached the database at all.

Recommendation in the report: parameterise every query, treat the WAF as a speed bump, and add server-side input validation. Retest after the code fix, not after a WAF rule change.