← Back to all reports

One Unauthenticated Route Exposes an Entire B2B Order Book

Reported Jun 30, 2026
Severity Critical
Platform Web / API
Vulnerability Class Broken Access Control / IDOR (CWE-639)
Target Type B2B Distribution / Order-Management Platform
Impact Anonymous read of the entire order book, enumerable by order number

The Risk

An attacker could download the company's entire business customer order book without logging in, no password and no account needed. Every record laid bare a customer's name and contact, their full shipping and billing street addresses, the internal account numbers the company uses for them, their order and billing reference numbers, the amount owed and when it was due, and even the parcel tracking number. Order numbers ran in simple counting order, so anyone could walk from the very first order placed to the newest, one number at a time, and pull well over a million records reaching back nearly two years. This is the kind of exposure that fuels fake-invoice fraud, redirected shipments, and targeted scams against named buyers, and it covered customers in more than two dozen countries.

The Vulnerability

The platform exposed a single order-lookup route that answered requests with no session, no token, no cookie, and no Authorization header. Every other route on the same host was correctly locked down. A sibling route serving the same order data returned HTTP 401 without a session, and every other API route on the host (products, account records, carts, token issuance) answered 401 as well. Only one route, an order-feed endpoint of the shape /api/orders-feed, was open to the world, while its authenticated twin /api/orders required a valid session.

Called with no parameters, the open route returned a bulk feed of the most recent orders across every customer. That alone was a mass exposure rather than a single-record lookup. The route also honored an orderNumber parameter, and that parameter accepted arbitrary sequential integers, allowing any specific order to be addressed directly by its identifier regardless of who placed it. This is a textbook insecure direct object reference: the identifier is the only thing standing between an anonymous request and a customer's full record, and the identifier is guessable.

The endpoint was not an intentional public feature that happened to leak. It backed a production customer-facing order-status page, which issued this exact request under the hood. What should have been a per-customer, session-scoped lookup was instead a database-wide, unauthenticated one.

The Attack

Every step was a plain unauthenticated GET. No browser, cookie, or token was involved.

Bulk Feed With Zero Authentication

The bare endpoint returned a bulk order feed immediately:

curl -sS 'https://example-host.tld/api/orders-feed'

The response was HTTP 200 with content-type: application/json and a list of the newest orders across all customers. No credentials were sent. In a single call, the feed surfaced roughly two dozen distinct customers, each record carrying a populated street address, spanning several countries at once. This confirmed the feed mixed the entire order book rather than scoping to one site or one buyer.

Direct Single-Order Lookup

Any specific order could be targeted directly by its number:

curl -sS 'https://example-host.tld/api/orders-feed?orderNumber=<n>'

This returned exactly one record, the targeted order, with its full billing and shipping detail. An attacker was not limited to the records the default feed happened to surface; they could address any order in the system by its identifier.

Full-History Reach and a Built-In Oracle

The lookup was not limited to the rolling feed. Requesting the lowest order number returned a real order dated back to 2024, proving the endpoint reached the entire historical database. The endpoint also acted as its own enumeration oracle: a valid order number returned HTTP 200 with the full record, while an unassigned or invalid number returned HTTP 400 with a fixed "order number is not valid" error body. That binary 200-versus-400 signal let an attacker deterministically map exactly which numbers were real and locate the boundaries of each range, turning a blind guess-walk into a complete map of the database.

Dense Ranges, No Rate Limiting

The identifiers were dense and contiguous. Consecutive integers each resolved to a different real customer's order, and the ranges reached back nearly two years. The largest single range covered well over a million sequential order numbers; additional ranges added many tens of thousands more. Pagination and filter parameters were ignored and failed open into the bulk list; only orderNumber was honored. Rapid back-to-back requests all returned HTTP 200 with no throttling, no 429, no 403, and no rate-limit headers, so the entire span was harvestable at speed.

The Impact

The exposure was the complete order-to-cash dataset for the platform's business customers, readable anonymously and enumerable end to end. Every record exposed:

  • Customer full name and ordering contact
  • Complete shipping and billing street addresses
  • Internal ERP customer account numbers (bill-to and ship-to)
  • Purchase order number and invoice number
  • Order total, payment terms, and due date
  • Carrier tracking number
  • Full line items (product code / description, list and net price, tax, freight)

An example record, using placeholder data to illustrate the shape of what came back:

{
  "orderNumber": 100000001,
  "customerName": "Example Distributors Ltd",
  "poNumber": "PO-EXAMPLE-001",
  "orderDate": "2026-06-30T06:40:45-04:00",
  "orderTotal": 1234.56,
  "shipTo_name": "Example Distributors Ltd",
  "shipTo_addr": "1 Placeholder Way",
  "shipTo_city": "Sampletown",
  "shipTo_country": "XX",
  "billTo_acct": "000000000",
  "shipTo_acct": "000000000",
  "paymentTerms": "N060"
}

The scale was mass, not incidental. The reachable order numbers totaled well over a million records spanning nearly two years, covering the full business customer base across more than two dozen countries and many order channels. Because the no-parameter feed served a rolling window of the newest orders, simply polling the endpoint harvested every new order as it was placed, on top of the historical database already reachable by number.

This created direct regulatory exposure as a personal-data breach, plus concrete downstream harm: invoice fraud using real purchase order and invoice numbers, social engineering against named purchasers, parcel redirection using real names, addresses, and tracking numbers, and competitor pricing intelligence from the full commercial detail.

Remediation

  • Require authentication and a valid session on the order-feed route, matching every sibling route on the same host. There is no reason this endpoint should differ.
  • Enforce authorization on every lookup: a request may only return orders that belong to the authenticated account. Bind order visibility to the session's customer, not to a guessable identifier.
  • Replace sequential order numbers as external identifiers with non-guessable, non-enumerable tokens for any customer-facing lookup.
  • Return a uniform response for both invalid and unauthorized order numbers so the endpoint cannot be used as an enumeration oracle.
  • Add rate limiting and anomaly detection on order-lookup traffic to stop bulk harvesting.
  • Audit access logs for prior bulk or sequential enumeration of this route and treat any confirmed harvest as a reportable personal-data breach.