cxrlos
← Security

Web application security

Testing a lead endpoint, and tooling for it

This is the web side: a test I wrote to check whether an unauthenticated lead-capture endpoint was injectable, the hardening that came out of it, and a recon toolkit I built for landing pages. The target was a funnel I was authorized to test, and every row the probe submitted was the literal string xxxxx, so it was easy to find and delete afterward.

Getting a probe past the bot wall

The endpoint sat behind a host-level anti-bot JavaScript challenge, which a plain curl or requests script can't clear, so a normal scanner just bounces off it and reports nothing. The harness drives a real Chromium, lets the challenge solve itself, then fires the probes with fetch() from inside the page so they carry the same cookie and user-agent the browser earned. It sends a clean baseline first, then error-based and time-based probes on the fields that reach the database.

The time-based probe is the clean signal. If a payload that appends a SLEEP makes the response come back about five seconds slower than the baseline, the input is reaching a query unescaped. The harness measures the baseline latency, runs each probe, and stops at the first clear result to keep the number of submitted rows down.

SLEEP_SECS = 5
def payloads(base):
    return [
        ("error-quote", base + "'"),                          # 500 / SQL error vs 200 baseline
        ("time-AND",    base + "' AND SLEEP(%d)-- -" % SLEEP_SECS),
        ("time-OR",     base + "' OR SLEEP(%d)-- -"  % SLEEP_SECS),
        ("time-concat", base + "'||(SELECT SLEEP(%d))||'" % SLEEP_SECS),
    ]
# A ~5s delay over baseline on a SLEEP probe is the time-based signal;
# a 500 / SQL error where the baseline was 200 is the error-based one.
run_test.py: the probe set (values redacted to the test marker)

The part that actually matters: the fix

Knowing an input might reach a query only helps if it leads to a fix, so the write-up that went with the harness is a remediation plan: parameterized queries so input can't change the shape of a statement, a least-privilege database user so one endpoint can't read every table, server-side validation and anti-automation on the form instead of leaning on the client challenge, and rotating any credentials that were exposed. The probe is throwaway. The fix is the point.

reconkit: recon and QA for landing pages

The other piece is reconkit, a toolkit I built for checking marketing funnels. It has two halves. The outside-in half runs on any domain and is passive: it grades DNS and email auth (SPF, DMARC, DKIM, CAA), grades HTTP security headers, fingerprints the stack, and on a deeper pass looks for exposed source maps and JS secrets. The inside-out half is for my own pages. It drives a real headless browser to see what actually happens at runtime, like which tracking pixels fire before and after consent and whether the conversion events are the ones you expect.

The active-scanning piece wraps nuclei in detection-only mode: misconfiguration, exposure, and CVE templates, with the fuzzing, denial-of-service, brute-force, and intrusive ones left out, rate-limited, and behind a prompt that makes you confirm you're authorized. The README is explicit about it: passive recon on anything, active scanning on your own assets only.

The endpoint test ran against a funnel I was authorized to test, with marker-only data. I'm describing the harness I built and the fix I'd apply, not a confirmed data breach. I didn't pull anyone's data. reconkit's active mode is gated to assets you own or are allowed to scan.