MEIDAY

Sample report, fictional data

External penetration test report

Wood & Thread, an online furniture store (fictional company)

Findings
FindingSeverityCVSS scoreRetest
F-01High7.1Fixed
F-02Medium6.1Fixed
F-03Medium5.3Not fixed
F-04Low3.1Inconclusive
F-05InfoNo scoreFixed
Report version
1.1, including retest
System
Online store including its API
Test type
External penetration test, two user types, OSCP finding validation
Test window
09:00 to 17:00, on the agreed days

Executive summary

We tested the Wood & Thread online store and its API from the outside, with a customer user and an admin user. We found five issues: one high, two medium, one low and one informational.

The most urgent is F-01: any logged-in customer can read other customers’ orders, including names and addresses. The fix is a targeted server change. F-03 comes next, because the admin panel is open to password guessing.

The retest found that three findings were fixed, one stayed open and one had an inconclusive result and will get another attempt.

Severity levels in the report and what to do with each
SeverityCVSS scoreWhat it means for you
Critical9.0–10.0A weakness that could allow especially serious damage. Needs urgent attention.
High7.0–8.9A weakness with significant impact or potential for serious exploitation. Fix it soon.
Medium4.0–6.9A weakness that may affect the system or its users. Prioritize the fix according to the risk.
Low0.1–3.9A relatively low risk, but still check the context and the possible impact.
InformationalNo scoreSomething worth knowing, such as details the server reveals unnecessarily. On its own it is not a weakness.

Scope

In-scope targets
TargetAddressNotes
shop.example203.0.113.10Public website and store
api.shop.example203.0.113.11Store APIs, as part of the system
shop.example/admin203.0.113.10Admin panel, admin test user

User types: customer, admin. Dedicated test users were provided by the client.

Out of scope: the external payment provider, the mailing system, the warehouse internal network and load testing.

Method and limitations

Testing was done from the outside with autonomous AI-based testing tools, at a limited rate and in the time window approved in writing. No actions changed real customer data.

Before delivery, MEIDAY went over the report to check that everything looks right. The OSCP add-on was ordered for this test: an OSCP-certified tester tried to reproduce each finding by hand, and the result is shown on every finding.

Limitations: source code, cloud configuration and business processes that need manual staff approval (such as refunds) were not tested. Autonomous testing can miss business-logic flaws. No finding in an area does not mean the area has no weaknesses.

Findings

  1. F-01Access to another customer’s orders through the APIHigh7.1
  2. F-02Reflected XSS in the search parameterMedium6.1
  3. F-03No limit on login attempts to the admin panelMedium5.3
  4. F-04The checkout page can be framed by another siteLow3.1
  5. F-05The server discloses its software versionInfoNo score
F-01Severity: HighRetest: Fixed

7.1 CVSS 3.1

Component
GET /api/orders/{id} · api.shop.example
Classification
CWE-639
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N
Reproduction
Reproduced, 3 of 3 attempts

Access to another customer’s orders through the API

What we found

A logged-in user can read another user’s order by changing the order number in the URL. The server checks that the user is logged in but not that the order belongs to them.

Steps to reproduce

  1. Log in as test customer A and open one of their orders. The request is GET /api/orders/10482.
  2. Log in as test customer B in a separate browser session.
  3. With customer B’s token, send the same request for order 10482.
  4. The server returns 200 with customer A’s name, address and items. The expected response is 404.

Request and response (sensitive values redacted)

GET /api/orders/10482 HTTP/1.1Host: api.shop.exampleAuthorization: Bearer [customer B token] HTTP/1.1 200 OKContent-Type: application/json {"id":10482,"customer":"[redacted]","address":"[redacted]","items":[…]}

Business impact

Unauthorized access to other customers’ orders can expose names, delivery addresses and order details. How much is exposed depends on how many orders can be reached.

How to fix

  1. In the order handler, look the order up by both its ID and the logged-in user’s ID, for example WHERE id = ? AND customer_id = ?.
  2. If no order matches, return 404, the same response as for an order that does not exist.
  3. Apply the same check to every endpoint that takes an order ID.
  4. Add an automated test that logs in as one user, requests another user’s order and expects 404.
F-02Severity: MediumRetest: Fixed

6.1 CVSS 3.1

Component
GET /search?q= · shop.example
Classification
CWE-79
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
Reproduction
Reproduced in Chromium and Firefox

Reflected XSS in the search parameter

What we found

The search value is shown on the results page without proper HTML output encoding. A crafted link can run script in the browser of whoever clicks it.

Steps to reproduce

  1. Open shop.example/search with a test value that contains HTML markup in the q parameter.
  2. View the page source: the value appears inside <p>Results for: …</p> exactly as sent, without encoding.
  3. The browser runs the script in the test value. This happened in Chromium and in Firefox.

Request and response (sensitive values redacted)

GET /search?q=[test payload redacted] HTTP/1.1Host: shop.example HTTP/1.1 200 OKContent-Type: text/html …<p>Results for: [payload appears here unencoded]</p>…

Business impact

An attacker who sends a customer a link can show fake content inside the site or act on their behalf while they are logged in.

How to fix

  1. HTML-encode the search value before it is written into the page. Most template engines do this by default, so check that the results template does not turn it off.
  2. Add a Content-Security-Policy header that blocks inline script, for example script-src 'self'.
  3. Open the test link again and check that the value shows as plain text.
F-03Severity: MediumRetest: Not fixed

5.3 CVSS 3.1

Component
POST /admin/login · shop.example
Classification
CWE-307
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
Reproduction
Reproduced, at the agreed low rate

No limit on login attempts to the admin panel

What we found

The admin panel is reachable from the internet and does not adequately limit password attempts, with no delay and no temporary lockout. We tested at a low rate in the agreed window, without guessing real passwords.

Steps to reproduce

  1. Send a login request to /admin/login with the admin test user and a wrong password.
  2. Repeat it 200 times from the same address, at the agreed low rate.
  3. Every attempt gets the same response. No delay, lockout or CAPTCHA appears.

Request and response (sensitive values redacted)

POST /admin/login HTTP/1.1Host: shop.exampleContent-Type: application/x-www-form-urlencoded username=[admin test user]&password=[wrong password] HTTP/1.1 200 OK…<p class="error">Invalid username or password</p>… (Attempt 200 of 200 from 203.0.113.45: same response, no delay, no CAPTCHA)

Business impact

Admin passwords can be guessed without the system stopping it. A weak password could allow unauthorized access to the admin panel.

How to fix

  1. Limit failed logins per address and per user, for example a growing delay after 5 failures and a 15-minute lockout after 10.
  2. Turn on two-factor authentication for every admin user.
  3. If the admins work from known addresses, allow the admin panel only from those addresses.
  4. Log failed admin logins and alert on bursts.
F-04Severity: LowRetest: Inconclusive

3.1 CVSS 3.1

Component
/checkout · shop.example
Classification
CWE-1021
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:L/A:N
Reproduction
Reproduced

The checkout page can be framed by another site

What we found

The page sends neither X-Frame-Options nor frame-ancestors, so another site can show it inside an iframe and hide a misleading layer over it.

Steps to reproduce

  1. Create a test page on another domain that loads https://shop.example/checkout in an iframe.
  2. Open the test page. The checkout page loads inside the frame and its buttons can be clicked.

Request and response (sensitive values redacted)

GET /checkout HTTP/1.1Host: shop.example HTTP/1.1 200 OKContent-Type: text/html(no X-Frame-Options, no Content-Security-Policy: frame-ancestors)

Business impact

A customer can be tricked into clicking a button on the checkout page without understanding what they are clicking.

How to fix

  1. Add the header Content-Security-Policy: frame-ancestors 'none' to the checkout page, or list only the domains allowed to frame it.
  2. Add X-Frame-Options: DENY as well, for older browsers.
  3. Clear the CDN cache and check that the header appears on every response, including cached ones.
F-05Severity: InfoRetest: Fixed

No CVSS score (informational)

Component
All responses · shop.example
Classification
CWE-200
Reproduction
Reproduced

The server discloses its software version

What we found

The Server header returns the web server name and its exact version number.

Steps to reproduce

  1. Send any request to shop.example.
  2. The Server header in the response shows the web server name and its exact version.

Request and response (sensitive values redacted)

GET / HTTP/1.1Host: shop.example HTTP/1.1 200 OKServer: [server name]/[version]

Business impact

Version disclosure does not by itself indicate a vulnerability, but it can help identify versions with known vulnerabilities.

How to fix

  1. In the web server settings, turn off the version number in the Server header.
  2. Keep the server on a supported version and update it regularly.

Retest

The retest ran 21 days after the report, on the same findings and in the same scope.

Retest results
FindingSeverityStatusNote
F-01HighFixedIn the retest the server returned 404 for every attempt to read another user’s order.
F-02MediumFixedThe search value is now shown as text, with proper HTML output encoding. A CSP was added.
F-03MediumNot fixedIn the retest there is still no limit. Two-factor authentication was enabled, so the risk dropped, but the finding stays open.
F-04LowInconclusiveThe header was added in the test environment but did not appear on every response in production, probably because of caching. We will arrange one more attempt after the cache is cleared, at no cost.
F-05InfoFixedThe header now returns only the server name, without a version.

External penetration test report · Wood & Thread, an online furniture store (fictional company) · Report version 1.1, including retest

Sample report, fictional data

Want to know what it will cost?

Answer four short questions in the calculator and get a price estimate, including VAT.

Go to the price calculator

Display preferences

Your preferences are saved in this browser only. You can use the site without this panel. Found an accessibility problem? Contact us.

Text size
Background
Contrast
Underline links
Animation
Accessibility statement