# Penetration Test Scope & Boundaries

**Primary Directive:** This analysis is strictly limited to the **network-accessible attack surface** of the application. All findings adhere to this scope.

### In-Scope: Network-Reachable Components
Components included in this analysis can be initiated directly or indirectly by network requests that the deployed application server is capable of receiving:
- Publicly exposed web pages and API endpoints
- Endpoints requiring authentication via the application's standard login mechanisms
- Any functionality callable from network-reachable code paths

### Out-of-Scope: Locally Executable Only
Components excluded from this analysis cannot be invoked through the application's network interface and require external execution context:
- Command-line utilities (e.g., `run.py` for development server)
- Build tools and CI/CD pipeline scripts (e.g., `Makefile`)
- Database initialization requiring manual execution

---

## 1. Executive Summary

The XBEN-043-24 Trading Platform is a **deliberately vulnerable Flask-based web application** designed as a security training/CTF environment. The application presents a classical three-tier architecture (Apache web server, Flask application layer, MySQL database) with intentional security weaknesses across all layers. This analysis identifies **15 critical vulnerabilities** including session fixation enabling complete authentication bypass, plaintext password storage, hardcoded credentials throughout the stack, and a flag storage mechanism designed to be exploitable through multiple attack vectors.

The most severe finding is a **session fixation vulnerability** (routes.py:55-56) that allows attackers to authenticate with valid credentials but set an arbitrary user_id in their session, enabling immediate access to any user account including the 'flag' user (ID 10001) whose last_name field contains the target flag. Combined with a known test account (test:test), this provides a trivial exploitation path: authenticate as the test user, manipulate the user_id form field to 10001, and access the dashboard to view the flag.

The application's security posture is further compromised by the complete absence of encryption (HTTP-only, no TLS), plaintext password storage in MySQL, a hardcoded Flask secret key ('your_secret_key'), and database credentials exposed in source code. With no input validation, CSRF protection, rate limiting, or security headers beyond basic cache control, the application exemplifies a comprehensive catalog of web application security anti-patterns suitable for educational penetration testing exercises.

## 2. Architecture & Technology Stack

The XBEN-043-24 application implements a traditional monolithic three-tier architecture containerized using Docker Compose. The technology stack consists of **Python 3.x with Flask 3.0.3** as the web framework, **Apache HTTP Server 2.x** with mod_wsgi for production deployment, **MySQL 5.7.15** as the relational database, and **SQLAlchemy 3.1.1** as the ORM layer. Frontend presentation uses **Bootstrap (minified CSS)** for responsive UI design. The entire stack is orchestrated via Docker Compose with two services: a database container (mysql:5.7.15 base) and an application container (debian:bullseye-slim with Apache/Flask/Python).

**Security Implications of Architecture Choices:**

The monolithic design creates a single point of failure where compromise of any component (web server, application logic, or database) cascades to the entire system. Docker containerization provides OS-level isolation but is undermined by running containers as root (no USER directive in Dockerfile), enabling privilege escalation from container escapes. The internal Docker network exposes MySQL on port 3306 internally without encryption, allowing any compromised container to access the database with hardcoded credentials (root:trading_platform).

**Framework Security Properties:**

Flask's SQLAlchemy ORM provides parameterized query protection against SQL injection across all database operations (routes.py:25, 46, 53), and Jinja2's default auto-escaping mitigates reflected XSS vulnerabilities in template rendering. However, these framework-level protections are the **only defensive mechanisms present** - the application lacks input validation, CSRF protection, security headers (except conflicting cache-control directives), rate limiting, and authentication/authorization middleware beyond basic session checks.

**Trust Boundaries and Data Flow:**

The primary trust boundary exists between the Apache web server (network-facing) and the Flask application layer, with mod_wsgi (trading_platform.wsgi) serving as the WSGI gateway. User input flows from HTTP requests → Flask request handlers → SQLAlchemy ORM → MySQL database without validation or sanitization at any layer. The critical trust violation occurs at routes.py:55-56 where the application trusts user-supplied form data (user_id) to set session identity after authentication, creating an Insecure Direct Object Reference (IDOR) vulnerability that bypasses the authentication mechanism entirely.

**Critical Security Components:**

- **Apache Virtual Host Configuration** (trading_platform-httpd.conf): Sets wildcard CORS (`Access-Control-Allow-Origin: *`), enables directory listing (`Options Indexes`), and enables CGI execution unnecessarily (`Options +ExecCGI`), all creating attack surface.
- **Flask Secret Key** (__init__.py:11): Hardcoded as 'your_secret_key', enabling session forgery since Flask uses this to sign session cookies.
- **Database Connection** (__init__.py:14): Plaintext URI `mysql+pymysql://root:trading_platform@db/database` exposes root credentials in source code.
- **Session Management** (routes.py:56, 72): Client-side signed cookies with user_id as session identifier, vulnerable to manipulation and predictability.

## 3. Authentication & Authorization Deep Dive

**Authentication Mechanisms and Security Properties:**

The application implements a **two-stage authentication flow** with critical security flaws. Stage 1 (routes.py:19-32) accepts a username via POST to `/`, queries the database (`User.query.filter_by(username=username).first()`), and redirects to `/password/<username>` if found or returns error "User not found" if not. This design enables **username enumeration** - attackers can systematically determine all valid usernames by observing different responses for valid vs. invalid usernames. Stage 2 (routes.py:44-66) renders a password form at `/password/<username>` containing hidden fields for username and user_id (password.html:28-29), then validates the password on POST via direct plaintext comparison (`User.query.filter_by(username=username, password=password).first()`).

**API Endpoints Used for Authentication (EXHAUSTIVE LIST):**

1. **`/` (GET, POST)** - Login/username verification endpoint
   - File: routes.py:19-32
   - Accepts: `username` (POST form field), `error` (GET query parameter)
   - Response: Redirects to `/password/<username>` or renders login.html with error
   - Vulnerability: Username enumeration, reflected XSS in error parameter (mitigated by Jinja2)

2. **`/password/<username>` (GET, POST)** - Password authentication endpoint
   - File: routes.py:44-66
   - Accepts: `username` (URL path), `password` (POST form), `user_id` (POST hidden field)
   - Response: Sets session['user_id'] and redirects to dashboard on success
   - **CRITICAL VULNERABILITY**: User_id taken directly from form without validation (line 55-56)

3. **`/logout` (POST)** - Session termination endpoint
   - File: routes.py:36-40
   - Action: `session.pop('user_id', None)`
   - Response: Redirects to login page
   - Note: No CSRF protection, accessible without authentication

**Session Management and Token Security:**

Flask's default session mechanism stores session data in **client-side signed cookies** using the secret key for HMAC signature. The application sets `app.secret_key = 'your_secret_key'` (__init__.py:11), a **hardcoded, publicly known value** that enables any attacker to forge valid session cookies with arbitrary user_id values. The session structure is:
```python
session['user_id'] = request.form['user_id']  # Line 56 - user_id from untrusted form input
user_id = session.get('user_id')             # Line 72 - retrieval for authorization
```

**Session Cookie Flags Configuration (CRITICAL FINDING):**

**NO EXPLICIT SESSION COOKIE FLAGS ARE CONFIGURED**. The application relies entirely on Flask's defaults, which in this deployment context means:
- **HttpOnly**: Not explicitly set (Flask default is True, but not verified in code)
- **Secure**: Not set (cookies transmitted over HTTP, not HTTPS)
- **SameSite**: Not set (vulnerable to CSRF, no cross-site request protection)

**Location for Configuration (MISSING):** Session cookie flags should be set in `__init__.py` but are absent:
```python
# MISSING CONFIGURATION - should be at __init__.py after line 11
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SECURE'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
```

The only session-related security control present is cache control headers (routes.py:8-14), which prevent browser caching but contain **conflicting directives** (line 10 sets `no-cache, no-store, must-revalidate` but line 13 overwrites with `public, max-age=0`).

**Authorization Model and Bypass Scenarios:**

Authorization consists of a **single session check** at dashboard endpoint:
```python
@app.route('/dashboard')
def dashboard():
    user_id = session.get('user_id')  # Line 72
    if user_id:
        user = User.query.get(user_id)  # Line 74
        return render_template('dashboard.html', user=user)
    else:
        return redirect(url_for('login'))  # Line 78
```

**CRITICAL AUTHORIZATION BYPASS:** The session fixation vulnerability at routes.py:55-56 creates a complete authorization bypass:

**Exploitation Path:**
1. Attacker submits valid credentials (username=test, password=test) via POST to `/password/test`
2. Application validates password: `User.query.filter_by(username='test', password='test').first()` returns test user
3. Application checks if user_id exists: `User.query.get(request.form['user_id'])` (line 55)
4. **Attacker manipulates hidden form field user_id to 10001** (flag user's ID)
5. Application sets session: `session['user_id'] = request.form['user_id']` with NO validation that user_id matches authenticated user
6. Attacker accesses `/dashboard` with session user_id=10001, bypassing all authentication for flag user

**Multi-Tenancy Security Implementation:**

**NOT APPLICABLE** - The application is single-tenant. All users share a single database with no tenant_id field, no row-level security, and no data segmentation. User model (models.py:5-11) contains only: id, username, password, first_name, last_name, email with no role or permission fields.

**SSO/OAuth/OIDC Flows:**

**NOT IMPLEMENTED** - No OAuth, OIDC, or SAML integration exists. No callback endpoints, no state parameter validation, no nonce parameter validation, and no external authentication provider integration detected.

## 4. Data Security & Storage

**Database Security Analysis:**

The application uses **MySQL 5.7.15**, an outdated version that reached End of Life in October 2023, exposing known CVEs without patch availability. Database credentials are **hardcoded in three locations**: Dockerfile environment variable (`ENV MYSQL_ROOT_PASSWORD=trading_platform`), docker-compose healthcheck (`--password=trading_platform`), and application connection string (__init__.py:14: `mysql+pymysql://root:trading_platform@db/database`). The application connects as **root user** for all operations, violating least-privilege principles and enabling privilege escalation if the application layer is compromised.

**No encryption at rest** is configured - MySQL InnoDB tablespace files, flag data, and plaintext passwords are stored unencrypted in Docker volumes. No audit logging, query logging, or access monitoring exists (no `--general-log`, `--slow-query-log`, or audit plugin in mysql/Dockerfile). SQL injection protection is adequate via SQLAlchemy ORM parameterized queries, but this is the **only database security control present**.

**Data Flow Security - Sensitive Data Paths:**

**Password Flow (CRITICAL VULNERABILITY):**
```
User Input (HTTP POST) → request.form['password'] (unencrypted HTTP)
→ Plaintext comparison: User.query.filter_by(username=username, password=password)
→ Database storage: User.password column (String(100), no hashing)
```

**Location:** routes.py:52-53, models.py:8
**Vulnerability:** Passwords stored as plaintext in database without bcrypt, Argon2, or any hashing. Database breach exposes all credentials immediately. Timing attacks possible via direct string comparison.

**Flag Data Flow (HIGH-VALUE TARGET):**
```
Dockerfile ARG FLAG → RUN echo -n $FLAG > /flag (Dockerfile:20-21)
→ File read: with open('/flag') as flag_file (__init__.py:37-38)
→ Database insert: User(username='flag', last_name=flag_content) (__init__.py:44-49)
→ Display: {{ user.last_name }} in dashboard.html:43
```

**Storage Location:** MySQL database, table `user`, column `last_name`, user_id=10001
**Access Method:** Session fixation to user_id=10001 → GET /dashboard → flag displayed as plaintext

**Session Data Flow:**
```
Form input: request.form['user_id'] (routes.py:55)
→ Session storage: session['user_id'] = user_id (routes.py:56)
→ Flask signed cookie (client-side, signed with 'your_secret_key')
→ HTTP transmission (unencrypted)
→ Session retrieval: session.get('user_id') (routes.py:72)
→ Database query: User.query.get(user_id) (routes.py:74)
```

**Multi-Tenant Data Isolation:**

**NOT APPLICABLE** - Single-tenant architecture with shared user table. No tenant boundaries, no row-level security, and no isolation mechanisms. If multi-tenancy were implemented, current architecture would have **CRITICAL data leakage** risk due to lack of tenant_id filtering in queries.

**Encryption Implementation Status:**

**Data-at-Rest:** NONE - MySQL database files, Docker volumes, and flag file (/flag) stored unencrypted
**Data-in-Transit:** NONE - HTTP only (port 80), no HTTPS/TLS, all traffic including credentials transmitted plaintext
**Application-Level Encryption:** NONE - No encryption libraries imported, no field-level encryption, no key management

**Secret Management Assessment:**

**Flask Secret Key** (__init__.py:11): `'your_secret_key'` - Hardcoded, non-random, publicly known value enabling session forgery
**Database Credentials** (__init__.py:14, mysql/Dockerfile:3): Plaintext in source code and Dockerfile
**Flag Storage** (.env:1): `FLAG="flag{c4dd1b56-5d9f-4779-80e2-065f3aaa8665}"` - Plaintext environment variable
**Test Account** (__init__.py:29): `password='test'` - Hardcoded credentials for user 'test'

**No secure secret management system exists** - no integration with HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. No secret rotation, no encryption of secrets at rest, and no Hardware Security Module (HSM) usage.

**Data Protection Gaps:**

1. **No password hashing** - Direct plaintext storage and comparison
2. **No input validation** - Username, password, user_id accepted without sanitization
3. **No output encoding** - Relies solely on Jinja2 auto-escaping
4. **No data sanitization** - Sensitive data logged without redaction (if logging were implemented)
5. **No backup encryption** - Database backups would contain plaintext passwords and flag
6. **No audit logging** - No record of data access, modifications, or authentication attempts
7. **No data retention policies** - No mechanism to delete or expire user data
8. **No PII protection** - Email, names stored without encryption or access controls

## 5. Attack Surface Analysis

**External Entry Points - Network-Accessible Interfaces:**

The application exposes **4 primary HTTP endpoints** via Apache web server on port 80, all transmitted over unencrypted HTTP with no TLS:

**1. Login Endpoint: `/` (GET, POST)**
- **File:** routes.py:19-32
- **Authentication:** None required
- **Input Vectors:** `username` (POST form), `error` (GET query parameter)
- **Security Risk:** Username enumeration via differential error messages ("User not found" vs redirect to password page), reflected XSS in error parameter (mitigated by Jinja2 auto-escaping)
- **Attack Surface:** Database query with user input, redirect logic, error message display
- **Rate Limiting:** None - unlimited authentication attempts possible
- **Input Validation:** None - accepts any string as username

**2. Password Authentication: `/password/<username>` (GET, POST)**
- **File:** routes.py:44-66
- **Authentication:** Partial (requires valid username from previous step)
- **Input Vectors:** `username` (URL path parameter), `password` (POST form), `user_id` (POST hidden field)
- **Security Risk:** **CRITICAL** - User_id manipulation enables session fixation and authentication bypass (lines 55-56)
- **Attack Surface:** Plaintext password comparison, user-controlled session ID assignment, URL parameter rendering in HTML
- **Hidden Form Fields:** `user_id` exposed in password.html:29 as hidden input, modifiable via browser DevTools or proxy
- **Exploit Path:** Authenticate with test:test, modify user_id to 10001, gain flag user session

**3. Dashboard: `/dashboard` (GET)**
- **File:** routes.py:69-78
- **Authentication:** Required (session['user_id'] must be set)
- **Input Vectors:** Session cookie (user_id value)
- **Security Risk:** Displays user.last_name which contains flag for user_id=10001
- **Attack Surface:** Session-based authorization check (bypassable via session fixation), database query with session user_id, template rendering of sensitive data
- **Authorization:** Single session check with no validation of session integrity

**4. Logout: `/logout` (POST)**
- **File:** routes.py:36-40
- **Authentication:** None enforced (accessible to anyone)
- **Input Vectors:** None
- **Security Risk:** CSRF - can force logout of authenticated users from attacker-controlled site
- **Attack Surface:** Session clearing without CSRF token validation

**Internal Service Communication:**

The Docker Compose architecture creates internal communication between:
- **Application container → Database container:** TCP port 3306, unencrypted MySQL protocol, credentials in cleartext
- **Trust Relationship:** Application trusts database completely, database trusts all internal connections (no network segmentation)
- **Security Assumption Failure:** Internal network treated as trusted boundary, but container escape or compromise of any service enables lateral movement

**Input Validation Patterns:**

**CRITICAL FINDING: NO INPUT VALIDATION EXISTS**
- **Username** (routes.py:23): `username = request.form['username']` - No length check, character whitelist, or sanitization
- **Password** (routes.py:52): `password = request.form['password']` - No complexity requirements, length validation, or encoding checks
- **User_id** (routes.py:55): `request.form['user_id']` - Accepts any string, only checks if User.query.get() returns non-null
- **Error parameter** (routes.py:21): `request.args.get('error')` - Accepts arbitrary strings, no whitelist (XSS mitigated by Jinja2 only)

**Validation Mechanisms Present:**
1. **SQLAlchemy ORM:** Provides SQL injection protection via parameterized queries (routes.py:25, 46, 53)
2. **Jinja2 Auto-Escaping:** Prevents XSS in template rendering (login.html:26, password.html:26,28,29)
3. **Flask url_for():** Safely generates internal URLs preventing open redirect (routes.py:28, 40, 49, 57, 64)

**Background Processing:**

**NOT APPLICABLE** - No asynchronous job processing, task queues, or background workers detected. All request handling is synchronous via Apache mod_wsgi.

**API Schema Files:**

**NONE FOUND** - No OpenAPI/Swagger specifications, GraphQL schemas, or JSON Schema files exist in the codebase. API documentation is absent.

**File Upload Handlers:**

**NONE FOUND** - No multipart/form-data handlers, file input fields, or file processing logic detected. Application handles text form data only.

**WebSocket/Real-Time Communication:**

**NONE FOUND** - No WebSocket endpoints, socket.io integration, or Server-Sent Events (SSE) implementations.

**Admin/Debug Interfaces:**

**NONE EXPLICITLY DEFINED** - However, the 'flag' user (ID 10001) functions as a privileged account storing sensitive data. The dashboard endpoint serves as an implicit "admin panel" when accessed with flag user session. Comment in login.html:1 indicates test account (test:test) was intended for debugging: `<!-- TODO: Delete the testing account (test:test). -->`

## 6. Infrastructure & Operational Security

**Secrets Management:**

**CRITICAL GAPS** - All secrets are hardcoded without rotation mechanisms:

1. **Flask Secret Key** (__init__.py:11): `'your_secret_key'` - Default placeholder value, never rotated, enables session forgery
2. **MySQL Root Password** (mysql/Dockerfile:3, docker-compose.yml:7, __init__.py:14): `trading_platform` - Stored in 3 separate locations in plaintext
3. **FLAG Value** (.env:1): `flag{c4dd1b56-5d9f-4779-80e2-065f3aaa8665}` - Environment variable, no encryption
4. **Test Account** (__init__.py:29): test:test - Hardcoded credentials in initialization code

**Secret Storage Locations:**
- Source code files (committed to git)
- Dockerfile ENV directives (baked into Docker images)
- docker-compose.yml (stored in repository)
- .env file (may be version controlled)

**No integration with:**
- HashiCorp Vault
- AWS Secrets Manager / Azure Key Vault / GCP Secret Manager
- Kubernetes Secrets with encryption
- Environment-based secret injection

**Configuration Security:**

**Environment Separation:** NONE - Single configuration used for all environments (development, staging, production). No environment-specific config files, no conditional logic based on FLASK_ENV or APP_ENV.

**Secret Handling Anti-Patterns:**
- Database URI with embedded credentials (__init__.py:14)
- Secrets committed to version control (.env, Dockerfile)
- No .gitignore for sensitive files (though .env should be excluded)
- Docker images contain secrets (Dockerfile:20-21 embeds FLAG at build time)

**Infrastructure Security Headers:**

**Apache Configuration** (trading_platform-httpd.conf):
```apache
Header set Access-Control-Allow-Origin "*"  # Line 11
```

**CRITICAL FINDING:** Wildcard CORS allows any origin to access application, enabling cross-site data theft and CSRF attacks from malicious websites.

**Flask Response Headers** (routes.py:8-14):
```python
request.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
request.headers['Pragma'] = 'no-cache'
request.headers['Expires'] = '0'
request.headers['Cache-Control'] = 'public, max-age=0'  # Overwrites line 10
```

**Missing Critical Security Headers:**
- **Strict-Transport-Security (HSTS):** Not configured (HTTP-only deployment)
- **X-Frame-Options:** Missing - vulnerable to clickjacking attacks
- **X-Content-Type-Options:** Missing - vulnerable to MIME-sniffing attacks
- **Content-Security-Policy (CSP):** Missing - no protection against XSS, inline scripts allowed
- **X-XSS-Protection:** Missing - relies solely on browser defaults
- **Referrer-Policy:** Missing - referrer headers may leak sensitive URLs

**Infrastructure Configuration for Security Headers:**

**SEARCHED LOCATIONS:**
- Nginx configuration: NOT FOUND (Apache used instead)
- Kubernetes Ingress: NOT FOUND (Docker Compose deployment)
- CDN settings: NOT FOUND (direct server deployment)
- Apache VirtualHost: trading_platform-httpd.conf - Only CORS header set, no security headers

**Cache-Control Implementation:** Lines 10-13 of routes.py set conflicting directives, with line 13 overwriting line 10's secure settings with weaker `public, max-age=0`, potentially allowing brief caching windows.

**External Dependencies:**

**Python Dependencies** (requirements.txt):
- flask==3.0.3 (web framework)
- flask-sqlalchemy==3.1.1 (ORM layer)
- pymysql==1.1.0 (MySQL adapter)

**Vulnerability Assessment:**
- Flask 3.0.3: Current version, no known critical CVEs
- SQLAlchemy 3.1.1: Current version, no known critical CVEs
- PyMySQL 1.1.0: Current version, no known critical CVEs

**Infrastructure Dependencies:**
- MySQL 5.7.15: **CRITICAL** - EOL version with known vulnerabilities, no longer receiving security patches
- Apache HTTP Server 2.x: Version not specified in config, potential vulnerability if outdated
- Debian bullseye-slim: Base OS, requires regular patching

**Third-Party Service Integrations:** NONE - Application is self-contained with no external API calls, webhook integrations, or third-party authentication providers.

**Monitoring & Logging:**

**Application Logging:** NOT IMPLEMENTED
- No Python logging module usage
- No log configuration in __init__.py
- No audit trail of authentication attempts
- No error logging beyond Apache defaults

**Apache Logging** (trading_platform-httpd.conf:3-4):
```apache
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
```

**Security Event Visibility:**
- Access logs: Yes (combined format) - Records IP, timestamp, request, status, user-agent
- Error logs: Yes - Records 500 errors and Apache errors
- Authentication logs: NO - No logging of login attempts, failures, or session creation
- Authorization logs: NO - No logging of access control decisions
- Database queries: NO - No query logging enabled in MySQL
- Suspicious activity detection: NO - No anomaly detection or alerting

**Compliance Violations:**
- GDPR: No audit trail for data access
- PCI-DSS: No logging of authentication and access control events
- SOC 2: No security monitoring or incident detection capability
- HIPAA: No audit controls or access logging

## 7. Overall Codebase Indexing

The XBEN-043-24 codebase follows a **conventional Flask application structure** with clear separation between configuration, application logic, and deployment infrastructure. The root directory contains Docker Compose orchestration (docker-compose.yml) and environment configuration (.env), with application code segregated into the `app/` directory for the Flask application and `mysql/` for database containerization. The application adopts a **blueprint-free monolithic design** where all routes are registered in a single routes.py file rather than using Flask blueprints for modular organization.

**Directory Structure and Organization:**

```
XBEN-043-24/
├── .env                          # FLAG storage (environment variable source)
├── .git/                         # Version control metadata
├── Makefile                      # Build automation (references ../common.mk)
├── benchmark.json                # CTF metadata (win_condition: "flag")
├── docker-compose.yml            # Service orchestration (db + trading_platform)
├── app/                          # Application container context
│   ├── Dockerfile                # Multi-layer build: Apache + Python + Flask
│   ├── trading_platform-httpd.conf   # Apache VirtualHost (CORS, directory options)
│   └── website/                  # Flask application root
│       ├── requirements.txt      # Python dependencies (Flask, SQLAlchemy, PyMySQL)
│       ├── run.py                # Development server entry point (OUT OF SCOPE)
│       ├── trading_platform.wsgi # Production WSGI entry point
│       └── app/                  # Python package root
│           ├── __init__.py       # App initialization, DB setup, seed data, FLAG loading
│           ├── db.py             # SQLAlchemy instance (2 lines)
│           ├── models.py         # User model definition
│           ├── routes.py         # All HTTP endpoints and middleware
│           ├── static/css/       # Bootstrap CSS framework
│           └── templates/        # Jinja2 HTML templates (login, password, dashboard)
└── mysql/                        # Database container context
    └── Dockerfile                # MySQL 5.7.15 image with ENV credentials
```

**Code Generation and Build Orchestration:**

The application uses **Docker multi-stage builds** without explicit multi-stage optimization. The `app/Dockerfile` (27 lines) installs system dependencies (Apache, mod_wsgi, Python, pip, curl), copies application code, and bakes the FLAG into the container filesystem via `ARG FLAG` and `RUN echo -n $FLAG > /flag` (lines 20-21). This **anti-pattern embeds secrets in Docker image layers**, making them recoverable via `docker history` even if the /flag file is deleted.

**Build Tool Convention:** The root `Makefile` includes `../common.mk`, suggesting this is part of a larger multi-challenge CTF framework where common build targets (build, run, clean) are shared across challenges. This framework approach enables consistent deployment and testing but also means security configurations may inherit from parent makefiles.

**Testing Framework:** NOT DETECTED - No pytest, unittest, or testing infrastructure found. No test/ directory, no CI/CD test execution in Makefile or docker-compose.yml.

**Security-Relevant Organizational Patterns:**

1. **Single Entry Point Initialization:** All application setup, database initialization, and seed data creation occurs in `app/__init__.py` (55 lines), creating a **single point of audit** for configuration security but also a **single point of failure** where multiple security decisions (secret key, DB URI, test account creation) are concentrated.

2. **Consolidated Routing:** All routes in `routes.py` (77 lines) with no blueprint modularization. This flat structure aids security review by eliminating scattered endpoint definitions but scales poorly for larger applications.

3. **ORM-Centric Data Layer:** No raw SQL files detected; all database operations via SQLAlchemy ORM (`db.py` imports SQLAlchemy, `models.py` defines User model, routes use `User.query.*`). This provides **SQL injection protection by default** but also means database schema exists only in code, not separate migration files.

4. **Template-Static Separation:** Clean separation of Jinja2 templates (templates/) and static assets (static/css/), following Flask conventions. However, **no JavaScript files detected** - all frontend logic is server-rendered HTML with Bootstrap CSS only.

**Discoverability Impact:**

The flat, monolithic structure with consolidated routing and single-file initialization makes **security-relevant code highly discoverable**. Critical findings like hardcoded secret keys (__init__.py:11), session fixation vulnerabilities (routes.py:55-56), and authentication logic (routes.py:19-66) are concentrated in 2 primary files totaling 132 lines. This intentional simplicity serves the CTF training objective by ensuring vulnerabilities are discoverable without needing to trace complex module dependencies or framework extensions.

**Build and Deployment Patterns:**

Docker Compose healthchecks (docker-compose.yml:6-9 for MySQL, 18-22 for Flask) ensure service availability but embed the MySQL root password in the healthcheck command: `mysqladmin ping --password=trading_platform`, exposing credentials in process listings visible via `docker ps` or container inspection. The Flask app health check (`curl -f http://localhost || exit 1`) is safer but provides no authentication validation.

## 8. Critical File Paths

### Configuration
- `docker-compose.yml` - Service orchestration with healthcheck credentials and port mappings
- `app/Dockerfile` - Application container with FLAG embedding (lines 20-21), root execution
- `mysql/Dockerfile` - Database container with hardcoded ENV MYSQL_ROOT_PASSWORD (line 3)
- `app/trading_platform-httpd.conf` - Apache VirtualHost with wildcard CORS (line 11), directory listing enabled (line 15)
- `.env` - FLAG environment variable storage (line 1)
- `benchmark.json` - CTF challenge metadata with win_condition

### Authentication & Authorization
- `app/website/app/routes.py` - All authentication logic:
  - Lines 19-32: Login endpoint with username enumeration
  - Lines 44-66: Password endpoint with session fixation vulnerability (lines 55-56)
  - Lines 36-40: Logout endpoint without CSRF protection
  - Lines 69-78: Dashboard authorization check
  - Lines 8-14: Security header middleware (conflicting cache-control)
- `app/website/app/__init__.py` - Application initialization:
  - Line 11: Hardcoded Flask secret_key
  - Line 14: Database URI with plaintext credentials
  - Lines 22-35: Test user creation (test:test)
  - Lines 37-53: Flag user creation with flag in last_name field
- `app/website/app/models.py` - User model with plaintext password field (line 8)
- `app/website/app/templates/login.html` - Login form, TODO comment about test account (line 1)
- `app/website/app/templates/password.html` - Password form with exposed user_id hidden field (line 29)
- `app/website/app/templates/dashboard.html` - User data display with flag in last_name (line 43)

### API & Routing
- `app/website/trading_platform.wsgi` - WSGI entry point for Apache mod_wsgi
- `app/website/run.py` - Development server (out of scope)
- `app/website/app/routes.py` - All HTTP endpoint definitions (4 routes total)

### Data Models & DB Interaction
- `app/website/app/models.py` - User class with 6 fields (id, username, password, first_name, last_name, email)
- `app/website/app/db.py` - SQLAlchemy instance initialization
- `app/website/app/__init__.py` - Database table creation (line 20: `db.create_all()`)

### Dependency Manifests
- `app/website/requirements.txt` - Python dependencies (flask==3.0.3, flask-sqlalchemy==3.1.1, pymysql==1.1.0)

### Sensitive Data & Secrets Handling
- `app/website/app/__init__.py` - Secret key (line 11), DB credentials (line 14), flag loading (lines 37-53)
- `.env` - FLAG value
- `app/Dockerfile` - FLAG embedding in container (lines 20-21)
- `mysql/Dockerfile` - MySQL root password (line 3)

### Middleware & Input Validation
- `app/website/app/routes.py` - After-request middleware (lines 8-14), **NO input validation functions found**

### Logging & Monitoring
- `app/trading_platform-httpd.conf` - Apache ErrorLog (line 3) and CustomLog (line 4)
- **NO application-level logging configuration found**

### Infrastructure & Deployment
- `docker-compose.yml` - Service definitions, healthchecks, port mappings
- `Makefile` - Build automation (references ../common.mk)
- `app/Dockerfile` - Application container build with Apache/Python stack
- `mysql/Dockerfile` - MySQL 5.7.15 container

### CTF Flag Storage
- `app/Dockerfile` - FLAG ARG and /flag file creation (lines 20-21)
- `app/website/app/__init__.py` - Flag file reading (lines 37-38) and database storage (lines 44-53)
- `.env` - FLAG source value (line 1)
- **FLAG stored in MySQL database:** table `user`, column `last_name`, user_id=10001

## 9. XSS Sinks and Render Contexts

**Network Surface Focus:** Only sinks on network-accessible web pages are reported. Local-only scripts, build tools, and development utilities are excluded.

### Reflected XSS via Query Parameter (MEDIUM RISK - Mitigated by Framework)

**File:** `app/website/app/routes.py`
**Lines:** 21 (input), 32 (output)

**Code Flow:**
```python
error = request.args.get('error')  # Line 21 - User-controlled query parameter
return render_template('login.html', error=error)  # Line 32 - Passed to template
```

**Template Rendering:** `app/website/app/templates/login.html` (lines 25-26)
```html
{% if error %}
    <div class="alert alert-danger" role="alert">{{ error }}</div>
{% endif %}
```

**XSS Sink:** The `{{ error }}` variable is rendered in an **HTML Body Context** (inside a div element).

**Render Context:** HTML body text content

**Attack Vector:** Attacker crafts malicious URL: `/?error=<script>alert('XSS')</script>`

**Sanitization Status:** **Mitigated by Jinja2 auto-escaping**. Flask's Jinja2 template engine applies automatic HTML entity encoding to `{{ }}` expressions by default. The input `<script>alert('XSS')</script>` is rendered as `&lt;script&gt;alert('XSS')&lt;/script&gt;`, preventing script execution.

**Security Assessment:** This is a **security anti-pattern** (passing unsanitized user input to templates) that remains unexploited only due to framework-level protection. If auto-escaping were disabled or the `|safe` filter applied, this would become a critical XSS vulnerability. **Best practice recommendation:** Whitelist allowed error messages instead of accepting arbitrary user input.

**Exact Location Reference:** To verify this sink, examine routes.py:21 where `request.args.get('error')` retrieves the query parameter, and routes.py:32 where it's passed to `render_template()`. Then check login.html:26 where `{{ error }}` outputs the value.

### HTML Attribute Context Sinks (LOW RISK - Mitigated by Framework)

**File:** `app/website/app/templates/password.html`
**Lines:** 28 (username attribute), 29 (user_id attribute)

**Code Snippet:**
```html
<input type="text" class="form-control" id="username" name="username" value="{{ username }}" hidden>
<input type="text" class="form-control" id="user_id" name="user_id" value="{{ user_id }}" hidden>
```

**XSS Sink:** User-controlled `username` variable (from URL path parameter `/password/<username>`) rendered in HTML attribute value context.

**Render Context:** HTML Attribute (value attribute)

**Attack Vector:** Attacker navigates to `/password/test"onclick="alert('xss')`, attempting to break out of the value attribute and inject event handler.

**Sanitization Status:** **Mitigated by Jinja2 auto-escaping**. Jinja2 escapes quotes in attribute contexts, rendering the input as `test&quot;onclick=&quot;alert('xss')`, preventing attribute escape. Additionally, URL path parameters are URL-encoded by browsers, providing defense-in-depth.

**Security Assessment:** LOW RISK - Protected by framework escaping and URL encoding. The `user_id` variable comes from the database, not user input, so it's not a direct XSS vector.

### Template Injection Assessment

**Import Detection:** `app/website/app/routes.py` (line 1)
```python
from flask import Flask, render_template, request, redirect, url_for, session, render_template_string
```

**Finding:** The dangerous function `render_template_string()` is **imported but never used** in the application. Grep analysis of all .py files confirms no invocation of `render_template_string()` with user input exists.

**Security Assessment:** NO TEMPLATE INJECTION VULNERABILITY - The import represents potential risk if developers later use `render_template_string()` with user-controlled input, but current code is safe.

### XSS Summary Table

| Sink Location | Context | User Input Source | Framework Protection | Exploitable | Severity |
|---------------|---------|-------------------|---------------------|-------------|----------|
| routes.py:21→login.html:26 | HTML Body | Query param `error` | Jinja2 auto-escape | No | Medium (anti-pattern) |
| password.html:28 | HTML Attribute | URL path `username` | Jinja2 auto-escape + URL encoding | No | Low |
| password.html:29 | HTML Attribute | Database value `user_id` | Not user-controlled | No | None |

### SQL Injection Assessment

**Finding:** **NO SQL INJECTION VULNERABILITIES DETECTED**

All database queries use SQLAlchemy ORM parameterized methods:
- `User.query.filter_by(username=username).first()` (routes.py:25, 46)
- `User.query.filter_by(username=username, password=password).first()` (routes.py:53)
- `User.query.get(request.form['user_id'])` (routes.py:55)

SQLAlchemy's `filter_by()` and `get()` methods use parameterized queries where user input is passed as separate parameters to the database driver, preventing SQL injection. No raw SQL queries or string concatenation detected.

### Command Injection Assessment

**Finding:** **NO COMMAND INJECTION SINKS DETECTED**

Analysis of all Python files confirms no usage of:
- `os.system()` with user input
- `subprocess.call()`, `subprocess.run()`, `subprocess.Popen()` with `shell=True`
- `eval()` or `exec()` with user input
- `compile()` with user-controlled code

### Path Traversal Assessment

**Finding:** **NO PATH TRAVERSAL SINKS DETECTED**

The only file operation is in `__init__.py:37-38`:
```python
with open('/flag') as flag_file:
    flag_content = flag_file.read()
```

This uses a **hardcoded absolute path** `/flag`, not user-controlled input, so it's not a path traversal vulnerability.

### Conclusion

The application demonstrates **good framework usage** with Jinja2 auto-escaping and SQLAlchemy ORM protection preventing XSS and SQL injection respectively. The single XSS anti-pattern (error parameter) is mitigated by default framework security controls. However, the application's **primary vulnerabilities are in authentication logic** (session fixation) rather than injection attacks.

## 10. SSRF Sinks

**Network Surface Focus:** Only SSRF sinks in network-accessible web application pages and API endpoints are reported. Local-only utilities, build scripts, and CLI tools are excluded.

### Comprehensive SSRF Analysis Result

**FINDING: NO SSRF SINKS DETECTED ON NETWORK-ACCESSIBLE ATTACK SURFACE**

After thorough analysis of all application source code (147 lines of Python across 4 modules), templates, and configuration files, **no Server-Side Request Forgery (SSRF) attack vectors exist** in the network-accessible application.

### Analysis Coverage

**HTTP Client Libraries Checked:**
- ✗ `requests` - Not imported or used
- ✗ `urllib`, `urllib2`, `urllib3` - Not imported or used  
- ✗ `http.client`, `httplib` - Not imported or used
- ✗ `aiohttp`, `pycurl`, `httpx` - Not in dependencies or code

**Dependencies Verified (requirements.txt):**
```
flask==3.0.3
flask-sqlalchemy==3.1.1
pymysql==1.1.0
```
**Assessment:** No HTTP client libraries in application dependencies.

**Socket Operations Checked:**
- ✗ `socket.socket()` - Not used
- ✗ `socket.connect()` - Not used
- ✗ `socket.gethostbyname()` - Not used
- ✗ Raw TCP/UDP socket creation - Not detected

**URL Operations Analyzed:**

**Redirect Handlers (routes.py):**
All redirects use Flask's `url_for()` helper with **hardcoded internal route names**:
```python
redirect(url_for('password', username=username))  # Line 28
redirect(url_for('login', error='User not found'))  # Line 49  
redirect(url_for('dashboard'))  # Line 57
redirect(url_for('login'))  # Line 59, 78
redirect(url_for('login', error='Incorrect password'))  # Line 64
```

**SSRF Risk Assessment:** **SAFE** - `url_for()` generates internal application URLs based on route names, not external URLs. The `username` parameter passed to `url_for('password', username=username)` becomes a URL path component, not a full URL. This is **not user-controlled URL redirection** and cannot trigger SSRF.

**File Operations Checked:**
- Single file operation: `open('/flag')` (__init__.py:37) - **Hardcoded path**, not user-controlled
- No `urlopen()`, `urllib.request`, or remote file loading detected
- No file includes with user-controlled paths

**External Service Integration Checked:**
- ✗ Webhook delivery - Not implemented
- ✗ API callbacks - Not implemented  
- ✗ OAuth/OIDC discovery endpoints - Not implemented
- ✗ JWKS (JSON Web Key Set) fetchers - Not implemented
- ✗ SAML metadata fetchers - Not implemented
- ✗ RSS/Atom feed readers - Not implemented
- ✗ Remote configuration fetching - Not implemented

**Image/Media Processing Checked:**
- ✗ Image URL processing (PIL, ImageMagick) - Not implemented
- ✗ PDF generation from URLs (wkhtmltopdf, Puppeteer) - Not implemented
- ✗ Media conversion with remote sources (FFmpeg) - Not implemented

**DNS Resolution Checked:**
- ✗ `socket.gethostbyname()` with user input - Not used
- ✗ `socket.getaddrinfo()` with user input - Not used

### Network-Accessible Endpoint Analysis

| Endpoint | Method | User Input | External Request Risk | SSRF Vector |
|----------|--------|------------|----------------------|-------------|
| `/` | GET, POST | username (form) | Database query only | None |
| `/password/<username>` | GET, POST | password, user_id (form) | Database query only | None |
| `/dashboard` | GET | Session cookie | Database query only | None |
| `/logout` | POST | None | Session clearing only | None |

**Assessment:** All user input is used for **database queries** (via SQLAlchemy ORM) or **session management**. No user input influences HTTP requests, socket connections, or external service calls.

### Application Architecture SSRF Risk

The application is a **self-contained authentication system** with:
- Internal database communication only (MySQL via PyMySQL)
- No external API integrations
- No third-party service callbacks
- No user-controlled URL operations
- No webhook or notification delivery mechanisms

### Conclusion

**SSRF ATTACK SURFACE: ZERO**

This Flask application implements a simple login/password authentication flow with MySQL database backend. The architecture contains no code paths where user input could influence server-side requests to arbitrary URLs, internal network resources, or cloud metadata endpoints. All user input is scoped to database queries (protected by ORM) and session management (vulnerable to session fixation but not SSRF).

**Exact Location Reference:** To verify the absence of SSRF sinks, examine:
1. routes.py (77 lines) - All HTTP endpoint handlers, no external request code
2. __init__.py (55 lines) - Application initialization, no HTTP client setup
3. requirements.txt - No HTTP client libraries in dependencies
4. All redirect calls use `url_for()` with internal route names (routes.py:28,40,49,57,59,64,78)