Customer confidential
ACME Customer Portal
RAVN Security
RAVN SECURITY
Customer confidential · Draft
Application Security Assessment · ACME Customer Portal

One night was enough to read the whole customer database.

Penetration test — executive and technical report
6
Findings proven
1
Critical
9.8
Highest CVSS
47 minutes
Time to proof
1,284 endpoints enumerated · 38 subdomains · 12 open ports 6 proven
Customer
ACME Corp (example)
Test period
2026-06-08 – 2026-06-12
Issued by
RAVN Security
Report date
2026-06-12
1. Executive summary

Executive summary

The one-sentence version

A single unfiltered parameter on the order search endpoint gave full read access to every customer record, order and stored payment token.

Between 2026-06-08 and 2026-06-12 we attempted to break into the ACME Customer Portal, starting with nothing more than the address of the website and no account of any kind.

Within the first hour we could read the customer database in full — names, order history and stored card tokens — through a single search box that had been left able to ask the database questions it was never meant to answer. Separately, any customer who logged in could read other customers' invoices simply by changing a number in the address bar.

Neither of these needed special tools or inside knowledge, and neither left an alert anywhere. Both are fixable, and the order to fix them in is on the next page.

1.1. Scope and approach

We tested ACME Customer Portal the way a real attacker would approach it, from two starting positions:

In total, 6 vulnerabilities were identified: 1 rated critical, 4 rated high, 1 rated medium. Identifying such weaknesses was the primary objective of the assessment.

1.1.1. How the engagement ran

How the engagement ran
  1. 01 [AI] Map the surface 1 284 pages and addresses found across 38 subdomains
  2. 02 [AI] Probe parameters 12 possible weaknesses flagged for a person to check
  3. 03 [AI] Discard the noise 7 of them did not hold up and were discarded
  4. 04 [HUM] Exploit by hand the search box could be made to query the database directly
  5. T+00:47 [HUM] Data access proven customer and order tables read in full
  6. T+02:15 [HUM] Cross the tenant boundary invoices belonging to other customers returned
Figure 1: The machine enumerated and discarded; a person did the exploiting. Only the steps that produced a finding carry a time, because only those have one.
1.2. Results and blast radius

What it reached

In total, 6 vulnerabilities were identified and proven.

Severity distribution — 6 findings
1 critical4 high1 medium
6
Total findings
1
Critical
4
High
1
Medium / Low
CVSS per finding
3.3.1 SQL injection in the order search endpoint 9.8
3.3.2 IDOR exposes other tenants' invoices 8.1
3.3.3 No rate limiting on the authentication endpoint 7.5
3.3.4 Stored XSS in the support ticket view 7.4
3.3.5 Exposed .git directory reveals source and a live API key 7.2
3.3.6 Session cookie set without Secure and SameSite attributes 5.4
Figure 2: Scored on CVSS 3.1. The scale is fixed at 0–10, so these bars are comparable with any other report using it.
Reachable from one entry point — 6 of 6
  • SQL injection in the order search endpoint CWE-89 · Critical
  • IDOR exposes other tenants' invoices CWE-639 · High
  • No rate limiting on the authentication endpoint CWE-307 · High
entry point portal.acme-example.test
  • Stored XSS in the support ticket view CWE-79 · High
  • Exposed .git directory reveals source and a live API key CWE-538 · High
  • Session cookie set without Secure and SameSite attributes CWE-1004 · Medium
Figure 3: One way in, and everything on either side of it was reachable once through. Colour is severity.

1.3. Fix order

Ordered by what removes the most risk first, not by how hard it is.

1Replace the string concatenation in the order search query with parameterised statements, and restrict the database role the API connects as so it cannot read the payment token store at all. Add a CI check that fails on raw query interpolation.removes CVSS 9.8
2Add a server-side ownership check on every endpoint that accepts an object reference, verifying the resource belongs to the caller's tenant before returning it. Filtering in the client is not a substitute.removes CVSS 8.1
3Introduce per-account and per-source rate limiting with progressive backoff on all authentication endpoints, and alert on sustained failure rates. Consider a second factor for accounts with billing access.removes CVSS 7.5
4Apply context-aware output encoding wherever customer-supplied text is rendered, and add a strict Content-Security-Policy as defence in depth.removes CVSS 7.4
5Block access to version-control and build directories at the web server, rotate the exposed API key immediately, and add a secret scanner to CI so a committed key is caught before deployment.removes CVSS 7.2
2. Assessment description

Assessment description

2.1. Nominated systems

The assessment covered ACME Customer Portal's nominated systems, as listed below.

ComponentDescription
portal.acme-example.testCustomer-facing web portal, production.
api.acme-example.test/v2REST API backing the portal and the mobile client.

2.2. Delimitations and restrictions

Testing ran against the production environment during business hours by agreement. Anything that risked writing to or destroying production data was performed read-only or not at all.

Explicit delimitations

Denial-of-service and load testing were out of scope. No social engineering was attempted against staff. The payment provider's own systems were not in scope.

3. Results and recommendations

Results

3.1. Severity ratings

Each identified vulnerability has been assessed and classified on a four-level scale based on its potential impact on the system's confidentiality, integrity and availability, and on how easily it could be exploited by an attacker.

SeverityDescription
CriticalGives an attacker total or partial control over a system or access to/manipulation of sensitive data.
HighCan give an attacker access to sensitive data, but may require special circumstances to fully exploit.
MediumRequires special circumstances or social engineering to fully succeed.
LowCan negatively affect certain security aspects but does not by itself directly compromise the system.

3.2. Outline of identified vulnerabilities

The table below maps each identified vulnerability to its assigned severity rating.

#VulnerabilityCrit.HighMed.Low
3.3.1SQL injection in the order search endpoint
3.3.2IDOR exposes other tenants' invoices
3.3.3No rate limiting on the authentication endpoint
3.3.4Stored XSS in the support ticket view
3.3.5Exposed .git directory reveals source and a live API key
3.3.6Session cookie set without Secure and SameSite attributes

3.3. Technical description of findings

The following section provides a technical description of each identified vulnerability, including background, description, evidence, and recommended remediation.

3.3.1

SQL injection in the order search endpoint

Critical Verified by hand
CWE-89 · A03:2021 – Injection · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CVSS
9.8
Prerequisites
None
Time to exploit
12 min
Evidence
HTTP capture

Background

SQL injection occurs when untrusted input is concatenated directly into a database query without proper parameterization. A successful exploit can let an attacker read, modify, or delete arbitrary data, and in some configurations execute commands on the underlying host.

Description

The sort_by parameter of /api/v2/orders/search is concatenated into the SQL query without parameterisation. A time-based payload produced a measurable five-second delay, confirming execution; a follow-up union query returned column names from the customers table.

From there the orders, customers and payment_tokens tables were all readable. No write operations were attempted.

Exploit path — three hops
WWW Public surface unauthenticated API Application /api/v2/orders/search SQL Data store query executed
Figure 4: Each hop was reached from the one before it. Nothing here required a credential the public internet does not already have.
Request / response
Request: GET /api/v2/orders/search?sort_by=id%2C(SELECT%201%20FROM%20PG_SLEEP(5)) HTTP/1.1 Host: api.acme-example.test Authorization: Bearer <redacted> Response: HTTP/1.1 200 OK X-Response-Time: 5041ms {"results":[]}
Listing 1: Time-based confirmation of the injection — a five-second delay on demand

Recommendations

Replace the string concatenation in the order search query with parameterised statements, and restrict the database role the API connects as so it cannot read the payment token store at all. Add a CI check that fails on raw query interpolation.

3.3.2

IDOR exposes other tenants' invoices

High Verified by hand
CWE-639 · A01:2021 – Broken Access Control · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
CVSS
8.1
Prerequisites
Any authenticated account
Time to exploit
6 min
Evidence
HTTP capture

Background

Insecure Direct Object Reference (IDOR) occurs when an application exposes an internal reference (e.g. a database ID) without verifying that the logged-in user is actually authorized to access that specific resource. In a multi-tenant environment this is particularly serious, as it can break the isolation between different customers' data.

Description

/api/v2/invoices/{id} returns any invoice by numeric id without checking that it belongs to the authenticated customer. Walking a range of six sequential ids returned four invoices belonging to other tenants, including line items and billing addresses.

Object reference walk
Ownership check absent /api/v2/invoices/{id}
#48211 own record
#48212 returned to the wrong caller
#48213 returned to the wrong caller
#48214 own record
#48215 returned to the wrong caller
#48216 returned to the wrong caller
Figure 5: Four of six sequential identifiers returned a record belonging to a different customer. The endpoint never checks who is asking.
Request / response
Request: GET /api/v2/invoices/48214 HTTP/1.1 Host: api.acme-example.test Authorization: Bearer <account A> Response: HTTP/1.1 200 OK {"invoice_id":48214,"tenant":"Contoso Nordics AB","total":"48 200 SEK","billing_address":"..."}
Listing 2: An invoice belonging to a different customer, returned to account A

Recommendations

Add a server-side ownership check on every endpoint that accepts an object reference, verifying the resource belongs to the caller's tenant before returning it. Filtering in the client is not a substitute.

3.3.3

No rate limiting on the authentication endpoint

High Verified by hand
CWE-307 · A07:2021 – Identification and Authentication Failures · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CVSS
7.5
Prerequisites
None
Time to exploit
continuous
Evidence

Background

Missing rate limiting on a sensitive endpoint (such as authentication) allows an attacker to make an effectively unlimited number of attempts, making brute-force and credential-stuffing attacks practical.

Description

/api/v2/auth/login accepts an unlimited number of attempts from a single source. Twelve thousand requests were sent over eleven minutes without a block, a delay, a CAPTCHA or an alert. Credential stuffing against this endpoint is practical at scale.

What the endpoint did about it
Attempts accepted every one
Requests throttled none
Accounts locked none
Alerts raised none
Figure 6: Three of the four controls that would normally bound this endpoint returned nothing at all. The first row is the only one with a value in it.

Recommendations

Introduce per-account and per-source rate limiting with progressive backoff on all authentication endpoints, and alert on sustained failure rates. Consider a second factor for accounts with billing access.

3.3.4

Stored XSS in the support ticket view

High Verified by hand
CWE-79 · A03:2021 – Injection · CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:L/A:N
CVSS
7.4
Prerequisites
A standard customer account
Time to exploit
20 min
Evidence

Background

Cross-site scripting (XSS) occurs when untrusted input is rendered into a page without adequate output encoding, allowing an attacker to run arbitrary script in another user's browser session — typically leading to session hijacking or account takeover.

Description

Ticket bodies are rendered into the support console without output encoding. A payload submitted from the lowest-privilege customer account executed in the browser of a support agent viewing the queue, in a session with access to every customer's records.

Crossing the privilege boundary
Lowest-privilege account stores the payload any customer can open a ticket
privilege boundary
Support agent session executes it read access to every customer record
Figure 7: The account that stores the payload cannot reach the data. The session that runs it can. Nothing crosses the middle except the stored text.

Recommendations

Apply context-aware output encoding wherever customer-supplied text is rendered, and add a strict Content-Security-Policy as defence in depth.

3.3.5

Exposed .git directory reveals source and a live API key

High Verified by hand
CWE-538 · A05:2021 – Security Misconfiguration · CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CVSS
7.2
Prerequisites
None
Time to exploit
4 min
Evidence

Background

Exposure of sensitive information through directory or file exposure occurs when build artifacts, version-control metadata, or other internal files are reachable over the web, often revealing source code, secrets, or infrastructure details.

Description

The web server serves /.git/ without restriction. The repository was reconstructed from it, giving full commit history and configuration. One commit contains a payment provider API key that is still valid.

What the exposure reaches
SRC
build metadatasource historycredential material
WWW
reachable without authentication
Figure 8: Everything on this lane was retrievable without authenticating. The last item is a credential that still works.

Recommendations

Block access to version-control and build directories at the web server, rotate the exposed API key immediately, and add a secret scanner to CI so a committed key is caught before deployment.

3.3.6

Session cookie set without Secure and SameSite attributes

Medium Verified by hand
CWE-1004 · A05:2021 – Security Misconfiguration · CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CVSS
5.4
Prerequisites
A privileged network position, or a cross-site request
Time to exploit
Evidence

Description

The session cookie is issued without the Secure flag and without a SameSite attribute. It is therefore transmitted over plaintext connections if one is ever reached, and is attached to cross-site requests.

Recommendations

Set `Secure`, `HttpOnly` and `SameSite=Lax` on the session cookie, and enforce HTTPS with HSTS so a plaintext request is never made in the first place.

4. Retest and next steps

What happens next

Every finding in this report was proven by hand, which means every one of them can be proven fixed the same way. A retest re-runs the exact steps recorded under each finding and reports which of them no longer work — so a fix is confirmed by the same evidence that found the problem, not by a claim that it has been deployed.

Get in touch when fixes are ready and we will scope a retest. It does not have to be all of them at once.

hi@ravnsec.com
Issued by RAVN Security · 2026-06-12 · Customer confidential
Appendix A

Vulnerability coverage

A list of findings says what was found. It cannot say what was looked for, which is the question that decides whether a short report means a secure application or a shallow test. The table below is the methodology: every class assessed, and what came back.

ClassWhat was testedResult
A01:2021 Broken access control Object references, tenant isolation, privilege boundaries, forced browsing. 1 finding3.3.2
A02:2021 Cryptographic failures Transport security, credential storage, token handling, cookie attributes. Tested — none found
A03:2021 Injection SQL, NoSQL, command and template injection; cross-site scripting in every rendered context. 2 findings3.3.1 · 3.3.4
A04:2021 Insecure design Business-logic abuse, workflow sequencing, trust assumptions between components. Tested — none found
A05:2021 Security misconfiguration Exposed directories and metadata, default credentials, verbose errors, headers. 2 findings3.3.5 · 3.3.6
A06:2021 Vulnerable components Known-vulnerable libraries and services reachable from the tested surface. Tested — none found
A07:2021 Identification and authentication Rate limiting, session handling, password reset, multi-factor coverage. 1 finding3.3.3
A08:2021 Software and data integrity Unsigned updates, insecure deserialisation, build and dependency pipelines. Tested — none found
A09:2021 Logging and monitoring Whether the activity in this report produced anything the customer could see. Tested — none found
A10:2021 Server-side request forgery URL-handling endpoints reachable from user input. Tested — none found

“Tested — none found” means the checks described above did not surface an instance of that class. It is not a guarantee that none exists: no assessment of finite length can offer one, and any report that implies otherwise is overselling itself.