What this course is
Get oriented before you boot a target with real, working exploits.
Vuln99 merges two prior HackOps Academy projects โ Glacier's own vulnerable_test_app.py and the fuller VulnMart storefront โ into one range: 24 independently-tunable vulnerabilities behind a realistic e-commerce app, purpose-built so its routes, parameter names, and SQL error format match what Glacier's active scanner already looks for. This course and Glacier's are meant to be read together โ Glacier teaches the scanner, this one teaches the target and how the two fit.
127.0.0.1 by default โ keep it that way unless you're on a host with no route to the internet or a shared network.
Installation & setup
One script, one URL, a known state on every boot.
git clone https://github.com/hackops-academy/vuln99.git cd vuln99 ./setup.sh # macOS/Linux โ venv, deps, and run in one step # .\setup.ps1 Windows PowerShell equivalent
Or by hand:
python3 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate pip install -r requirements.txt python run.py
Open http://127.0.0.1:5099. The database is SQLite and reseeds from scratch on every process start โ you always boot into a known, clean state.
./setup.sh --test (or .\setup.ps1 -Test) once right after install โ the pytest smoke suite boots the app and hits every route at every difficulty tier to catch a broken install before you blame the scanner for a bad target.Anatomy of the range
Know the shape of the target before you start pulling triggers.
Vuln99 is a full storefront โ catalog, cart, checkout, reviews, accounts, and an admin panel โ not just a page of isolated bugs. 24 vulnerabilities are wired into that realistic flow, each independently switchable between low/medium/hard from Admin โ Difficulty settings (/admin/settings).
Every vulnerable route that has a hardened counterpart ships a /safe-* sibling โ /safe-search, /safe-product, /safe-read-file, /safe-ping, /safe-go โ the same pattern as Glacier's old test app, useful for confirming a scanner correctly stays quiet on a hardened endpoint instead of just firing on everything.
/ first โ the catalog page doubles as a QA diagnostics panel listing every route in the app, tagged. It's the fastest way to see the full attack surface before you touch the scanner.Difficulty tiers
The same bug, three ways โ pick the lesson you actually want to teach or learn.
| Tier | What it means |
|---|---|
| LOW | Fully vulnerable, no mitigation โ what Glacier's scanner is tuned to detect. |
| MEDIUM | A partial, real-world-common mitigation that's still bypassable (blacklist filtering, naive escaping, rate-limiting mistaken for access control) โ practice filter/WAF bypass here. |
| HARD | Properly fixed โ parameterized queries, allow-lists, real session checks, CSP-safe encoding. |
Bulk "set all to low/medium/hard" buttons live on the same settings screen. Without a valid login, if broken_admin is set to LOW, visit /admin/become-admin-demo to set the forgeable is_admin cookie the low-difficulty check trusts.
SQL injection family
Seven distinct SQLi surfaces โ from a URL parameter to an HTTP header.
| Key | Where | Type |
|---|---|---|
login_sqli | /login | Auth bypass |
search_sqli | /search?q= | UNION-based |
product_sqli | /product?id= | Error-based |
blind_sqli_bool | /blind-product?id= | Boolean-blind |
blind_sqli_time | /slow-product?id= | Time-blind |
recommend_sqli | last_category cookie on /recommend | Cookie-based |
header_sqli | User-Agent/X-Forwarded-For on /category | HTTP header, sink at /admin/logs |
POST /api/active/scan at each URL with confirm: true and compare what it flags against this table. The cookie- and header-based ones won't show up in a URL-only scan โ that's what Repeater and Fuzzer (Module 10) are for.header_sqli manually โ set a crafted User-Agent with curl -A, hit /category, then check /admin/logs as admin to see the raw value land in the database.XSS & CSRF
Reflected, stored, and the request-forgery classic โ right where a scanner and a human diverge.
| Key | Where |
|---|---|
reflected_xss | /search?q= |
stored_xss | Product reviews (/product, /reviews) |
csrf | /checkout |
/search is exactly what Glacier's active scanner's reflection + context probing is built for. Stored XSS in reviews won't show up in a single-request active scan โ submit a review manually, then revisit /product to see it fire, the same way a human tester would./checkout is a good one to walk through by hand in Repeater โ strip the session cookie, replay the request, and see exactly what protection (or lack of it) is actually in place at each difficulty tier.Access control & logic flaws
The bugs no scanner finds without help โ this is where manual testing earns its keep.
| Key | Where |
|---|---|
idor_orders | /orders, /account?id= |
broken_admin | /admin, /admin/settings |
mass_assignment | /account (POST) |
price_tamper | /cart |
role field, edit a cart's price parameter before checkout.File & command vulnerabilities
Where the scanner's path-traversal and command-injection checks earn their keep.
| Key | Where |
|---|---|
path_traversal | /read-file |
file_upload | /account/avatar |
command_injection | /ping, /ping-slow |
/read-file and /ping / /ping-slow are the exact route shapes Glacier's path-traversal/LFI and reflected+blind command-injection checks were tuned against โ a clean active scan here is a good regression test any time you modify active/scanner.py. file_upload is manual-only; there's no automated check for it in Glacier today.SSRF, XXE, SSTI & deserialization
The admin-only advanced labs โ highest impact, easiest to under-scope.
| Key | Where |
|---|---|
ssrf | /admin/import-image |
xxe | /admin/import-orders |
ssti | /admin/email-preview |
insecure_deserialization | "Remember me" on /login (pickle) |
All four are admin-gated โ you'll need a valid admin session (or the /admin/become-admin-demo shortcut from Module 03) to reach them. None of these have automated Glacier checks; they're deliberately manual-only labs.
Redirects & misc findings
The smaller findings that round out a full report.
| Key | Where |
|---|---|
open_redirect | /go |
weak_crypto | /register (unsalted MD5) |
verbose_errors | /product, /version |
/go is the exact shape Glacier's open-redirect check (Location-header verification) expects โ a reliable one to confirm the scanner's still behaving correctly after any change. weak_crypto and verbose_errors are Glacier's passive-scanner territory, not active โ run a passive pass over a full walkthrough to catch both.Running Glacier against Vuln99
Where the two courses actually meet.
Point Glacier's proxy and active scanner at http://127.0.0.1:5099/ with the injection-family and broken_admin vulnerabilities set to LOW (the default). Route paths, parameter names (username/password, q, id, file, host, url), and the SQL error format (sqlite3.OperationalError) all match what Glacier's check_* functions expect out of the box.
# from Glacier's course, Module 04 POST /api/spider/start { "start_url": "http://127.0.0.1:5099/" } # from Glacier's course, Module 05 โ confirm scope is localhost before running this POST /api/active/scan { "url": "http://127.0.0.1:5099/product?id=1", "confirm": true }
john_doe and pass the session cookie via Glacier's auth object (cookie mode โ see Glacier's Module 06) so the spider and active scanner reach the authenticated routes: /account, /orders, /recommend.The combined methodology
Glacier's methodology, run end-to-end against this specific range.
./setup.sh, confirm LOW across the board, log the reseedweak_crypto and verbose_errors hits/account)Multi-VM labs & next steps
Graduating from localhost without losing the safety rails.
If you're running a real isolated lab (a scanner on one VM, Vuln99 on another, both on a private network with no route to the internet), override the bind address:
VULN99_HOST=0.0.0.0 VULN99_PORT=5099 python run.py
run.py prints a warning banner whenever it's bound to anything other than localhost โ treat that banner as a hard stop-and-check, not noise. Every request lands in logs/access.log and every admin action in logs/events.log, both handy for lining up what the app saw against a simultaneous nmap scan or Wireshark capture.
- Run the full Module 11 methodology against Vuln99 at LOW, then again at MEDIUM.
- Diff your two Glacier findings exports and see exactly which findings a real-world mitigation would have hidden from an automated scan alone.
- Once this feels routine, go back to Glacier's own course Module 11 and run its full methodology against Vuln99 as the final exercise.