Web Interfaces
Authentication Forms
Protected Pages
- Basic Auth Required
- Session Required (logged_in: true)
- 2FA Session Required (2fa_logged_in: true)
- Secret Header Required
- Secret Cookie Required
Vulnerable Tools
All Web Endpoints
| Endpoint | Method | Auth Required | Description |
|---|---|---|---|
| / | GET | None | Home / index page |
| /web/login | GET, POST | None | Simple login form — SQL Injection Bypass |
| /web/welcome-simple | GET | Session: logged_in=True | Post-login welcome page — Reflected XSS |
| /web/login-2fa | GET, POST | None | Login with TOTP 2FA |
| /web/welcome-2fa | GET | Session: 2fa_logged_in=True | Post-2FA welcome page — Reflected XSS |
| /web/welcome-basic-auth | GET | Basic Auth | Protected by HTTP Basic Auth |
| /web/welcome-header | GET | secret-header: my-secret-header | Protected by secret header |
| /web/welcome-cookie | GET | secret-cookie=my-secret-cookie | Protected by secret cookie |
| /web/logout | GET | None | Clears current session |
| /web/ping | GET | None | Network ping — Command Injection via host param |
| /web/users | GET | None | User search — SQL Injection via search param |
| /web/guestbook | GET | None | Visitor guestbook — Reflected XSS via name param |
| /web/graphql | GET | None | GraphQL query interface (HTML form) |
| /web/oauth2/login | GET | None | OAuth2 flow landing page |
| /web/oauth2/authorize | GET, POST | None | OAuth2 consent screen — Open Redirect, No CSRF |
| /web/oauth2/callback | GET | None | OAuth2 callback with interactive token exchange |
| /web/oauth2/profile | GET | Bearer Token | OAuth2 protected profile — Token in Query String |
OAuth2 Testing Guide
This lab implements two OAuth2 grant types: Authorization Code (user login via browser + API token exchange) and Client Credentials (pure API, machine-to-machine, no user involved). Multiple intentional vulnerabilities are present for pentest practice.
How the Authorization Code Flow Works
How Client Credentials Works (API-only)
No browser, no user, no redirects. The application authenticates as itself with a single API call and gets a token back. Used for machine-to-machine communication.
Try it: Client Credentials (curl)
# Step 1: Get a token with client credentials curl -X POST http://localhost:5000/api/v1/oauth2/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "client_credentials", "client_id": "vulapp-client-001", "client_secret": "super-secret-client-secret", "scope": "read profile" }' # Response: # { # "access_token": "a1b2c3d4e5f6...", # "token_type": "Bearer", # "expires_in": 3600, # "scope": "read profile" # }
# Step 2: Use the token to access protected resources curl http://localhost:5000/api/v1/oauth2/userinfo \ -H "Authorization: Bearer <ACCESS_TOKEN>" # Response: # { # "sub": "service-account-vulapp-client-001", # "username": "service-account-vulapp-client-001", # "email": "service-account-vulapp-client-001@vulapp.local", # "scope": "read profile" # }
# Try it: request any scope you want (vulnerability #7)
curl -X POST http://localhost:5000/api/v1/oauth2/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "vulapp-client-001",
"client_secret": "super-secret-client-secret",
"scope": "admin write delete"
}'
OAuth2 Endpoints
| Endpoint | Method | Type | Description |
|---|---|---|---|
| /web/oauth2/login | GET | Web | Landing page with flow overview |
| /web/oauth2/authorize | GET/POST | Web | Authorization + consent screen |
| /web/oauth2/callback | GET | Web | Receives auth code, interactive token exchange |
| /web/oauth2/profile | GET | Web | Protected resource (Bearer token or ?token=) |
| /api/v1/oauth2/token | POST | API | Token endpoint (authorization_code + client_credentials) |
| /api/v1/oauth2/userinfo | GET | API | User profile (requires Bearer token) |
Try it: Authorization Code (curl)
After completing steps 1-3 in the browser above, copy the authorization code from the callback URL and run:
# Step 4: Exchange the authorization code for an access token curl -X POST http://localhost:5000/api/v1/oauth2/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "authorization_code", "code": "<AUTH_CODE_FROM_STEP_3>", "client_id": "vulapp-client-001", "client_secret": "super-secret-client-secret", "redirect_uri": "/web/oauth2/callback" }' # Response: # { # "access_token": "a1b2c3d4e5f6...", # "token_type": "Bearer", # "expires_in": 3600, # "scope": "read profile" # }
# Step 5: Use the token to access protected resources curl http://localhost:5000/api/v1/oauth2/userinfo \ -H "Authorization: Bearer <ACCESS_TOKEN>" # Response: # { # "sub": "admin", # "username": "admin", # "email": "admin@vulapp.local", # "scope": "read profile" # }
Intentional Vulnerabilities
-
1
Open Redirect —
redirect_uriis never validated. An attacker can change it to any URL to steal authorization codes. -
2
No CSRF Protection — The
stateparameter is passed through but never validated server-side. - 3 Auth Code Replay — Authorization codes are not invalidated after use and can be exchanged for tokens multiple times.
-
4
No Client Secret Validation — The token endpoint accepts any value (or none) for
client_secret. -
5
Token in Query String —
/web/oauth2/profile?token=...is accepted, leaking tokens in logs and Referer headers. - 6 Predictable Auth Codes — Codes are generated with MD5(username + timestamp), making them guessable.
-
7
Unrestricted Scopes (Client Credentials) — The client_credentials grant accepts any
scopevalue without validation. Requestadminorwrite deleteand it will be granted.
Drawing Tools
API Documentation
All API Endpoints
| Endpoint | Method | Auth Required | Description |
|---|---|---|---|
| /api/tools/echo | GET, POST, PUT, DELETE, PATCH | None | Echoes all request data (headers, params, body, cookies, session) |
| /api/tools/otp | GET, POST | None | TOTP code generator — params: seed_b32 or seed_hex |
| /api/v1/get-token | POST | Credentials (JSON body) | Authenticates and returns a Bearer token |
| /api/v1/get-token-form | POST | Credentials (form data) | Authenticates and returns a Bearer token |
| /api/v1/is-valid-token | GET, POST | Bearer Token | Validates the provided Bearer token |
| /api/v1/header-cookie | GET | Secret Header + Cookie | Protected by secret header and cookie |
| /api/v1/header-cookie-auth | GET | Basic Auth + Header + Cookie | Protected by Basic Auth, secret header, and cookie |
| /api/v1/users/<user_id> | GET | None | User lookup by ID — SQL Injection |
| /api/v1/graphql | POST | Secret Header | GraphQL API — SQLi, Introspection, exposes password field |
| /api/v1/graphql/schema | GET | None | GraphQL schema via introspection |
| /api/v1/oauth2/token | POST | Client credentials | OAuth2 token endpoint (authorization_code + client_credentials) |
| /api/v1/oauth2/userinfo | GET | Bearer Token | OAuth2 user profile |
| /api/v1/mle/ | GET | None | MLE — returns static info encrypted as a JWE token (RSA-OAEP + A256GCM) |
| /api/v1/mle/ | POST | None | MLE — decrypts the incoming JWE token and returns an encrypted echo response |