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:
As an outsider with no account and no credentials — everything reachable by anyone on the internet.
As an ordinary logged-in user, using the application the way a normal employee or customer would.
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
01[AI]Map the surface1 284 pages and addresses found across 38 subdomains
02[AI]Probe parameters12 possible weaknesses flagged for a person to check
03[AI]Discard the noise7 of them did not hold up and were discarded
04[HUM]Exploit by handthe search box could be made to query the database directly
T+00:47[HUM]Data access provencustomer and order tables read in full
T+02:15[HUM]Cross the tenant boundaryinvoices 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 endpoint9.8
3.3.2 IDOR exposes other tenants' invoices8.1
3.3.3 No rate limiting on the authentication endpoint7.5
3.3.4 Stored XSS in the support ticket view7.4
3.3.5 Exposed .git directory reveals source and a live API key7.2
3.3.6 Session cookie set without Secure and SameSite attributes5.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 endpointCWE-89 · Critical
IDOR exposes other tenants' invoicesCWE-639 · High
No rate limiting on the authentication endpointCWE-307 · High
entry pointportal.acme-example.test
Stored XSS in the support ticket viewCWE-79 · High
Exposed .git directory reveals source and a live API keyCWE-538 · High
Session cookie set without Secure and SameSite attributesCWE-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.
1
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.
removes CVSS 9.8
2
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.
removes CVSS 8.1
3
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.
removes CVSS 7.5
4
Apply 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
5
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.
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.
Component
Description
portal.acme-example.test
Customer-facing web portal, production.
api.acme-example.test/v2
REST 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.
Severity
Description
Critical
Gives an attacker total or partial control over a system or access to/manipulation of sensitive data.
High
Can give an attacker access to sensitive data, but may require special circumstances to fully exploit.
Medium
Requires special circumstances or social engineering to fully succeed.
Low
Can 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.
#
Vulnerability
Crit.
High
Med.
Low
3.3.1
SQL injection in the order search endpoint
–
–
–
3.3.2
IDOR exposes other tenants' invoices
–
–
–
3.3.3
No rate limiting on the authentication endpoint
–
–
–
3.3.4
Stored XSS in the support ticket view
–
–
–
3.3.5
Exposed .git directory reveals source and a live API key
–
–
–
3.3.6
Session 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.
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.
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
HighVerified 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 checkabsent/api/v2/invoices/{id}
#48211own record
#48212returned to the wrong caller
#48213returned to the wrong caller
#48214own record
#48215returned to the wrong caller
#48216returned 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
HighVerified 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 acceptedevery one
Requests throttlednone
Accounts lockednone
Alerts raisednone
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.
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 accountstores the payloadany customer can open a ticket
privilege boundary
Support agent sessionexecutes itread 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
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
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.
Unsigned updates, insecure deserialisation, build and dependency pipelines.
Tested — none found
A09:2021Logging and monitoring
Whether the activity in this report produced anything the customer could see.
Tested — none found
A10:2021Server-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.