Practice Environment
A hands-on practice ground for QA engineers. Each scenario contains intentionally planted bugs, vulnerabilities, and edge cases. No accounts. No scores. No hints beyond the scenario page. Your job is to find what's broken.
Login form vulnerable to classic SQLi bypass. Find the input that breaks authentication.
↗A bio field that sanitizes one XSS vector but not another. Find the one that executes.
A URL-fetch endpoint with no destination validation. Reach the loopback admin panel, cloud metadata service, internal LAN host, and local filesystem.
↗A file download endpoint with no path sanitization. Escape the base directory using ../ sequences, URL-encoded dots, and a null-byte extension bypass.
An XML parser with external entity resolution enabled. Declare a SYSTEM entity pointing at a local file or cloud metadata endpoint — the parser resolves it and returns the contents.
A card preview reads the URL hash and writes it to the page via innerHTML. Script tags are partially stripped — find the event-handler vectors that bypass the filter.
A metrics widget processes the same input through eval(), new Function(), and parseFloat(). No HTML tags needed - find which execution contexts run arbitrary JavaScript and which are safe.
Auth tokens in localStorage, a role flag the page trusts without a server check, and PII cached in plain text. Find which storage patterns are safe and which create exploitable vulnerabilities.
User input lands inside a style attribute and a <style> block. A semicolon extends the attribute; a closing brace escapes the rule entirely. A third field validates before applying.
A session endpoint that calls pickle.loads() on a user-supplied cookie. Craft a payload with __reduce__ to execute OS commands — then verify the JSON endpoint handles it safely.
Two forms — one protected, one not. Craft a forged request and find the unprotected one.
↗A post-login redirect endpoint with no URL validation. Find the inputs that escape to an external domain — including the protocol-relative bypass that defeats naive prefix checks.
↗A password-reset endpoint that trusts the Host header without validation. Inject a foreign domain and watch it appear in the reset link — and bypass it via X-Forwarded-Host.
↗Conflicting Content-Length and Transfer-Encoding headers desync a proxy and backend. Watch CL.TE and TE.TE payloads poison the backend buffer — then see the hardened endpoint reject them both.
↗Session fixation, no expiry, session ID in URL, and logout that leaves the server-side session alive. Postman-testable.
↗A login endpoint with no rate limit — hammer it 50+ times without ever getting blocked.
↗A logout that skips session.flush() — the session data persists after logout.
↗Switch between users and find which delete and admin endpoints skip role and ownership checks.
↗You are Bob. Change the card ID in the request and see whose data you can access. Enumerate IDs 1–5.
↗Two user-creation endpoints — one accepts all fields blindly. Inject is_admin, role, or account_balance to escalate privileges.
↗Password hash in API response, admin credentials in HTML comments, tokens in a profile endpoint, and a leaky 500 error.
↗DEBUG on, admin exposed, missing security headers, and open ALLOWED_HOSTS. Six separate configuration failures to find.
↗Two API endpoints — one public, one authenticated. Inspect CORS headers. Find the wildcard on the private endpoint, the origin reflection, and the null-origin bypass.
↗Four endpoints with different framing policies. Check which headers are missing, then watch the unprotected page load inside a live iframe.
↗A div-based dropdown with 4 planted bugs — JS errors, outside-click, form value, and keyboard nav.
↗A Terms & Conditions modal with broken close button and focus trap issues.
Extension-only validation that misses MIME type, empty files, and script-injected filenames.
↗Six endpoints — some return mismatched status codes vs body content. Find the 200 that means failure.
↗Submit the same form twice — one endpoint creates duplicates, one uses a token to deduplicate.
↗Click publish twice rapidly — the buggy endpoint has no lock and produces duplicate records.
↗Error responses that leak stack traces, file paths, DB strings, and secret key prefixes.
↗A save endpoint that silently drops the status field — only part of the record is updated.
↗Failed logins and access violations that produce no log entries — invisible to monitoring.
↗Update a profile name — one endpoint skips cache busting, leaving the cached view stale.
↗A list endpoint with no pagination — returns all 500 records in one ~50 KB response.
↗