Practice range. VulnShop breaks on purpose so scanners have something to find. Keep it on 127.0.0.1 — never put it on the internet.
Vulnshop. Merch for people who read the source

Security lab

15 vulnerabilities · indexed and runnable

Click any link to see the vulnerable endpoint. Use the forms below to fire a payload — responses come back on the same page.

Web vulnerabilities (11 auto-detected by scanners)

GET

SQL Injection

The search q parameter is concatenated directly into the SQL query with no parameter binding. A quote ends the string literal.

/search?q=shirt
GET

Reflected XSS + SSTI

The name parameter on /greet is rendered as template source, so both HTML and Jinja2 expressions execute.

/greet?name=guest
GET

Command Injection

The host parameter is passed to sh -c "ping -c 3 {host}" without escaping. Semicolons and pipes run arbitrary commands.

/api/ping?host=127.0.0.1
GET

Path Traversal

The file parameter is joined to /app/files/ with no check for .. sequences. Read any accessible file.

/download?file=readme.txt
GET

Open Redirect

The url parameter is passed to Flask's redirect() with no validation. External URLs work just as well.

/redirect?url=/
POST

XXE (XML External Entity)

The /api/import endpoint parses XML with resolve_entities=True, allowing entity expansion and file disclosure.

/api/import (POST)

Data exposure (4 real but unguarded)

GET

IDOR (Insecure Direct Object Reference)

/api/orders/{id} returns any order by id alone, never checking if you own it. Try any integer: 1, 2, 3, 10, 100.

/api/orders/1
GET

Unauthenticated Admin Panel

/admin requires no login and dumps the full user and order table. It is also listed in robots.txt under Disallow as a hint.

Open panel
GET

Exposed Secrets & Backups

/.env and /backup.zip are routed endpoints that serve fake secrets. Real apps expose these accidentally.

GET

Stack Trace Disclosure

/boom raises an error and renders the full Python traceback as HTML. Production code leaks these in 500 responses.

Trigger error

Configuration issues (4 found by reading the code)

SAST / Code Review

Plaintext Passwords

User passwords are stored and compared as plaintext in the database. No hashing, no salt. See app.py login route and the users table schema.

SAST / Code Review

Hardcoded Secrets

The Flask session signing key is a hardcoded string in app.py and also printed in /.env. Real session cookies can be forged offline.

SAST / Code Review

Insecure Session Cookie

The session cookie is set with no Secure, HttpOnly, or SameSite flags. It is sent over HTTP and readable from JavaScript.

SAST / Code Review

Missing Security Headers

No response includes X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, or Strict-Transport-Security. Scanners flag all of them.

Safe to exploit: VulnShop binds to 127.0.0.1:5000 only and is meant to stay in Docker. Tear down with docker-compose down -v when done. All passwords and secrets shown here are intentional; changing them would break the demo.