Skip to content

Applications

Each application runs as 4 load-balanced Docker containers behind nginx upstream pools, accessible through the nginx reverse proxy on the VM. Sticky sessions ensure stateful applications (Juice Shop, DVWA, VAmPI, CSD Demo) route consistently. All applications are intentionally vulnerable and designed for security testing.

Replace <ORIGIN> with http://<PUBLIC_IP> in all examples below. After deploying via Terraform, get the IP with terraform output -raw public_ip.

PathApplicationHTTP MethodExpected Response
/Landing pageGET200 HTML with links to all apps
/healthHealth checkGET200 JSON {"status":"healthy","component":"origin-server",...}
/juice-shop/Juice ShopGET200 HTML (Angular SPA, ~75 KB)
/juice-shop/rest/products/search?q=Juice Shop APIGET200 JSON {"status":"success","data":[...]} (36 products)
/dvwa/DVWAGET302 redirect to /dvwa/login.php
/dvwa/login.phpDVWA loginGET200 HTML login form
/dvwa/setup.phpDVWA setupGET200 HTML (first-run database init)
/vampi/VAmPIGET200 HTML (API docs)
/vampi/users/v1VAmPI APIGET200 JSON {"users":[...]}
/vampi/users/v1/registerVAmPI APIPOST200 JSON {"status":"success",...}
/vampi/users/v1/loginVAmPI APIPOST200 JSON {"auth_token":"...","status":"success"}
/httpbin/gethttpbinGET200 JSON with request details
/httpbin/posthttpbinPOST200 JSON with posted data
/httpbin/headershttpbinGET200 JSON {"headers":{...}}
/httpbin/status/:codehttpbinGETReturns the specified HTTP status code
/whoami/whoamiGET200 plain text with hostname, IP, all headers
/csd-demo/CSD DemoGET200 HTML checkout form with attack panel
/csd-demo/dashboardCSD attacker viewGET200 HTML showing exfiltrated data
/csd-demo/healthCSD healthGET200 JSON {"status":"healthy","component":"csd-demo",...}
/csd-demo/exfil/logCSD exfil logGET200 JSON array of captured data
/dvga/DVGA GraphiQLGET200 HTML (GraphiQL IDE)
/dvga/graphqlDVGA GraphQL APIPOST200 JSON GraphQL response
/restaurant/RESTaurantGET200 (redirects to docs)
/restaurant/docsRESTaurant SwaggerGET200 HTML (FastAPI Swagger UI)
/restaurant/openapi.jsonRESTaurant OpenAPIGET200 JSON OpenAPI spec
http://<PUBLIC_IP>:8888crAPIGET200 HTML (SPA)
http://<PUBLIC_IP>:8888/identity/api/auth/signupcrAPI signupPOST200 JSON
http://<PUBLIC_IP>:8888/identity/api/auth/logincrAPI loginPOST200 JSON {"token":"..."}
Path/juice-shop/
Imagebkimminich/juice-shop:latest
Instances4 (ports 3001-3004), sticky via hash $cookie_token, proxy cache (60 s TTL)
Resources2 CPU / 1 GiB RAM per instance
FrameworkNode.js / Angular
Projectowasp.org/www-project-juice-shop

OWASP Juice Shop is the most modern and actively maintained vulnerable web application. It covers the entire OWASP Top 10 with 100+ challenges in a realistic e-commerce application.

ScenarioCategoryAttack Vector
SQL Injection login bypassWAF' OR 1=1-- in the login email field
Reflected XSSWAF, CSDScript injection via search parameter
DOM-based XSSCSDPayload in URL fragment
Broken authenticationWAFBrute force login, JWT manipulation
API abuseAPI SecurityUnauthorized access to /api/ endpoints
Sensitive data exposureAPI SecurityAccessing user data without authorization
CSRFWAFCross-site request forgery on profile changes
Terminal window
curl -s "http://<PUBLIC_IP>/juice-shop/" -o /dev/null -w "%{http_code}"
Path/dvwa/
ImageCustom dvwa-fpm:latest (php-fpm + nginx, built from ghcr.io/digininja/dvwa:latest)
Instances4 (ports 8101-8104), sticky via hash $cookie_PHPSESSID
Resources0.5 CPU / 256 MiB RAM per instance
DatabaseShared MariaDB 10.11 (dvwa-db container, 1 CPU / 768 MiB)
FrameworkPHP 8 / php-fpm / MariaDB
Credentialsadmin / password

DVWA is the industry standard for WAF testing. It has adjustable security levels (Low, Medium, High, Impossible) that progressively add input validation and output encoding.

ScenarioCategorySecurity Level
SQL InjectionWAFLow: trivial ' OR 1=1#, High: blind SQLi
Command InjectionWAFLow: ; ls, High: filtered characters
File InclusionWAFLow: direct path traversal, High: sanitized
XSS (Reflected)WAF, CSDLow: basic <script>, High: encoded bypass
XSS (Stored)WAF, CSDLow: persistent script in guestbook
File UploadWAFLow: PHP shell upload, High: extension filtering
Brute ForceBot DefenseAutomated login attempts

Set the security level at /dvwa/security.php after logging in:

  • Low — No input validation. Every attack works. Good for WAF signature demonstrations.
  • Medium — Basic filtering. Some attacks require encoding or bypass techniques.
  • High — Strong filtering. Only advanced techniques succeed. Good for showing WAF limitations.
  • Impossible — Fully secure implementation. Demonstrates proper defensive coding.

DVWA requires a one-time database setup after first deployment:

  1. Navigate to http://<PUBLIC_IP>/dvwa/setup.php
  2. Click Create / Reset Database
  3. Log in with admin / password
Path/vampi/
Imageerev0s/vampi:latest with gunicorn entrypoint (4 workers)
Instances4 (ports 5101-5104), sticky via ip_hash (SQLite per instance)
Resources0.5 CPU / 512 MiB RAM per instance
FrameworkPython / Flask / gunicorn
Projectgithub.com/erev0s/VAmPI

VAmPI is purpose-built for testing the OWASP API Security Top 10. It provides a realistic REST API with deliberate vulnerabilities. Each instance runs gunicorn with 4 workers and its own SQLite database. The ip_hash sticky session ensures registration and login from the same client IP always hit the same instance.

ScenarioOWASP API Top 10Method
Broken Object Level Authorization (BOLA)API1Access other users’ data by manipulating IDs
Broken AuthenticationAPI2Weak token handling, no rate limiting
Excessive Data ExposureAPI3API returns more data than the client needs
Mass AssignmentAPI6Modify admin fields via unexpected parameters
SQL InjectionAPI8Injection through API parameters
Improper Assets ManagementAPI9Undocumented API endpoints
Terminal window
# Register a new user
curl -X POST "http://<PUBLIC_IP>/vampi/users/v1/register" \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test123","email":"test@test.com"}'
# Login
curl -X POST "http://<PUBLIC_IP>/vampi/users/v1/login" \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"test123"}'
# List users (excessive data exposure)
curl "http://<PUBLIC_IP>/vampi/users/v1"
Path/httpbin/
Imagekennethreitz/httpbin:latest with gunicorn CMD override (-w 4 -k gevent --timeout 30)
Instances4 (ports 8201-8204), round-robin (stateless)
Resources0.5 CPU / 256 MiB RAM per instance
FrameworkPython / Flask / gunicorn + gevent
Projecthttpbin.org

httpbin is a simple HTTP request/response service useful for basic API demos, testing request headers, and verifying proxy behavior.

EndpointPurpose
/httpbin/getReturns GET request data (headers, args, origin)
/httpbin/postReturns POST request data (body, form, JSON)
/httpbin/headersReturns request headers
/httpbin/ipReturns origin IP
/httpbin/user-agentReturns User-Agent header
/httpbin/status/:codeReturns specified HTTP status code
/httpbin/delay/:secondsDelays response by N seconds
/httpbin/anythingReturns anything passed in request
Terminal window
# Check what headers the origin sees (useful for verifying F5 XC header injection)
curl -s "http://<PUBLIC_IP>/httpbin/headers" | jq .
# Test a specific HTTP status code
curl -s -o /dev/null -w "%{http_code}" "http://<PUBLIC_IP>/httpbin/status/403"
Path/whoami/
Imagetraefik/whoami:latest
Instances4 (ports 8082-8085), round-robin (stateless)
Resources0.25 CPU / 64 MiB RAM per instance
FrameworkGo
Projectgithub.com/traefik/whoami

whoami is Traefik’s lightweight request echo server. It displays every detail of the incoming HTTP request as the origin sees it — hostname, IP addresses, all headers, method, and URL. This is the single most important diagnostic tool when verifying that F5 XC is injecting the correct headers.

Use CaseWhat to Look For
Verify F5 XC header injectionX-Forwarded-For, True-Client-IP, X-Volterra-* headers
Confirm client IP visibilityX-Real-IP vs RemoteAddr
Debug WAF false positivesCompare request headers before/after F5 XC
Validate bot defense taggingX-Volterra-Bot-Type, X-Volterra-Bot-Verified headers
Check TLS terminationX-Forwarded-Proto shows https when TLS terminates at F5 XC
Terminal window
# Basic request -- see what the origin receives
curl "http://<PUBLIC_IP>/whoami/"
# Simulate request through F5 XC (with injected headers)
curl "http://<PUBLIC_IP>/whoami/" \
-H "X-Forwarded-For: 203.0.113.50" \
-H "True-Client-IP: 203.0.113.50" \
-H "X-Forwarded-Proto: https"

Example output:

Hostname: 534c5084e169
IP: 127.0.0.1
RemoteAddr: 172.17.0.1:55118
GET / HTTP/1.1
Host: 20.12.78.159
User-Agent: curl/8.5.0
Accept: */*
True-Client-Ip: 203.0.113.50
X-Forwarded-For: 203.0.113.50, 10.0.0.1
X-Forwarded-Proto: https
X-Real-Ip: 104.219.105.84

DVGA (Damn Vulnerable GraphQL Application)

Section titled “DVGA (Damn Vulnerable GraphQL Application)”
Path/dvga/
Imagedolevf/dvga:latest
Instances4 (ports 5201-5204), sticky via ip_hash (SQLite per instance)
Resources0.5 CPU / 256 MiB RAM per instance
FrameworkPython / Flask / GraphQL
Projectgithub.com/dolevf/Damn-Vulnerable-GraphQL-Application

DVGA is purpose-built for testing GraphQL-specific vulnerabilities. It provides a GraphiQL IDE at /dvga/ for interactive query exploration and a GraphQL API endpoint at /dvga/graphql. Each instance uses its own SQLite database, so ip_hash sticky sessions ensure consistent state.

ScenarioCategoryAttack Vector
GraphQL InjectionAPI SecurityMalicious queries via string interpolation
Denial of ServiceAPI SecurityDeeply nested queries, batch queries, resource exhaustion
Authorization BypassAPI SecurityAccessing unauthorized data through GraphQL
Information DisclosureAPI SecurityIntrospection queries revealing schema details
Batching AttackAPI SecurityMultiple operations in a single request
Terminal window
# GraphiQL UI (interactive IDE)
curl -sf "http://<PUBLIC_IP>/dvga/" -o /dev/null -w "%{http_code}"
# Introspection query -- enumerate the full schema
curl -s "http://<PUBLIC_IP>/dvga/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name fields { name } } } }"}'
# List pastes (example query)
curl -s "http://<PUBLIC_IP>/dvga/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"{ pastes { title content } }"}'
# Create a paste (mutation)
curl -s "http://<PUBLIC_IP>/dvga/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"mutation { createPaste(title:\"test\", content:\"hello\", public:true) { paste { title } } }"}'

RESTaurant (Damn Vulnerable RESTaurant API Game)

Section titled “RESTaurant (Damn Vulnerable RESTaurant API Game)”
Path/restaurant/
ImageCustom build from theowni/Damn-Vulnerable-RESTaurant-API-Game
Instances4 (ports 8301-8304), round-robin (shared PostgreSQL)
Resources0.5 CPU / 256 MiB RAM per instance
DatabaseShared PostgreSQL 15.4 (restaurant-db container, 0.5 CPU / 512 MiB)
FrameworkPython / FastAPI / PostgreSQL
Credentialsadmin / password (PostgreSQL)
Projectgithub.com/theowni/Damn-Vulnerable-RESTaurant-API-Game

RESTaurant is a gamified vulnerable REST API covering the OWASP API Security Top 10 2023. It uses FastAPI with automatic Swagger UI documentation at /restaurant/docs. All 4 instances share a single PostgreSQL database, so round-robin load balancing works without sticky sessions.

ScenarioOWASP API Top 10 2023Method
Broken Object Level Authorization (BOLA)API1Access other users’ orders by manipulating IDs
Broken AuthenticationAPI2Weak token handling, credential stuffing
Broken Object Property Level AuthorizationAPI3Mass assignment on user profile fields
Unrestricted Resource ConsumptionAPI4No rate limiting on endpoints
Broken Function Level Authorization (BFLA)API5Access admin endpoints as regular user
Server Side Request Forgery (SSRF)API7Manipulate server-side URL requests
Security MisconfigurationAPI8Verbose error messages, default credentials
Terminal window
# Swagger UI
curl -sf "http://<PUBLIC_IP>/restaurant/docs" -o /dev/null -w "%{http_code}"
# OpenAPI spec
curl -s "http://<PUBLIC_IP>/restaurant/openapi.json" | jq .info
# BOLA -- access another user's order (after authentication)
curl -s "http://<PUBLIC_IP>/restaurant/orders/1" \
-H "Authorization: Bearer <token>"
# BFLA -- attempt admin action as regular user
curl -s -X POST "http://<PUBLIC_IP>/restaurant/admin/users" \
-H "Authorization: Bearer <user_token>" \
-H "Content-Type: application/json"
Port8888 (dedicated — not path-prefixed)
Imagescrapi/crapi-web, crapi/crapi-identity, crapi/crapi-community, crapi/crapi-workshop, PostgreSQL, MongoDB, MailHog
Instances7 microservices (single instance each)
Resources~3.0 CPU / ~2.0 GiB RAM total
FrameworkReact SPA + Java/Go/Python microservices
Projectgithub.com/OWASP/crAPI

crAPI is the OWASP flagship project for API security testing. It runs as 7 microservices on a dedicated port (8888) because the React SPA hardcodes its API paths and cannot be served behind a path prefix. The NSG allows inbound traffic on port 8888.

MailHog captures all emails sent by crAPI (account verification, password reset). Access MailHog via SSH tunnel on port 18025.

CategoryVulnerabilities
BOLA (Broken Object Level Authorization)Access other users’ vehicles, orders, and reports
BFLA (Broken Function Level Authorization)Escalate to admin, access restricted endpoints
Mass AssignmentModify protected fields (role, balance) via API
SSRF (Server Side Request Forgery)Manipulate server-side URL fetching
JWT ManipulationForge or modify JWT tokens for privilege escalation
NoSQL InjectionInject queries into MongoDB-backed endpoints
Excessive Data ExposureAPI returns sensitive user data
Terminal window
# Verify crAPI is running
curl -sf "http://<PUBLIC_IP>:8888" -o /dev/null -w "%{http_code}"
# Sign up a new user
curl -s -X POST "http://<PUBLIC_IP>:8888/identity/api/auth/signup" \
-H "Content-Type: application/json" \
-d '{"name":"Test User","email":"test@example.com","number":"1234567890","password":"Test1234!"}'
# Log in
curl -s -X POST "http://<PUBLIC_IP>:8888/identity/api/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"Test1234!"}'
# Access MailHog (via SSH tunnel for email verification)
# ssh -L 18025:localhost:18025 azureuser@<PUBLIC_IP>
# Then open http://localhost:18025 in your browser
Port 8888 -> crapi-web (React SPA + nginx)
-> crapi-identity (Java, user auth, JWT)
-> crapi-community (Go, forums, posts)
-> crapi-workshop (Python, vehicle service)
-> crapi-postgres (PostgreSQL)
-> crapi-mongo (MongoDB)
-> crapi-mailhog (email capture, port 18025)
Demo Use CasePrimary AppSecondary App
WAF — SQL InjectionDVWAJuice Shop
WAF — XSSDVWAJuice Shop
WAF — Command InjectionDVWA
API Security — BOLAVAmPI
API Security — Auth bypassVAmPIJuice Shop
API Security — Data exposureVAmPIhttpbin
Bot Defense — Brute forceDVWAJuice Shop
Bot Defense — ScrapingJuice Shop
Client-Side Defense — DOM XSSJuice Shop
Client-Side Defense — Stored XSSDVWAJuice Shop
Client-Side Defense — Card SkimmerCSD Demo
Client-Side Defense — FormjackerCSD Demo
Client-Side Defense — KeyloggerCSD Demo
Client-Side Defense — CryptominerCSD Demo
Client-Side Defense — DOM HijackCSD Demo
API Security — GraphQL injectionDVGA
API Security — GraphQL DoSDVGA
API Security — OWASP API Top 10 2023RESTaurantcrAPI
API Security — BFLARESTaurantcrAPI
API Security — Mass assignmentcrAPIRESTaurant
API Security — SSRFcrAPIRESTaurant
API Security — JWT manipulationcrAPI
API Security — NoSQL injectioncrAPI
Basic connectivity testinghttpbin
Request diagnosticswhoamihttpbin
Header injection verificationwhoami