Five Findings From Five Engagements: Anonymized Patterns From One Week of Customer Scans
Per our engagement-handling policy, customer identities never appear in our public writeups. The findings in this article are real; the customers are anonymised as "a leading e-commerce retailer", "a public-sector portal", "a regulated financial institution", "a global manufacturing customer", and "a SaaS analytics platform". The chains, decision rules, and Proof Capsule contracts that fired are all reproducible from the references at the end.
The lesson across all five is the same one we keep coming back to: the bugs are not new. The bugs are the same five classes the OWASP Top 10 has called out for a decade. What is new is the projection rate, the same class compounds across three independent customers in the same week. Patterns are findings.
Finding 1: IDOR via numeric order-id enumeration on a leading e-commerce retailer
The endpoint pattern: GET /api/v1/orders/{order_id}. The session: an authenticated customer-A session token. The probe: increment order_id by one and observe the response. The decision rule: response.tenant_id != session.tenant_id AND status == 200.
The bug is textbook CWE-639 (authorization through user-controlled key). The platform engineering team had implemented authentication correctly, the API rejected anonymous requests with a 401. They had not implemented authorization correctly, the API authorized based on the presence of a session, not on the relationship between the session's tenant and the requested order's tenant.
The customer's CDN cached responses by URL plus session-cookie hash. A cross-tenant 200 OK got cached in the CDN. Three minutes later, a different attacker pulling the same order_id without authentication got the cached cross-tenant payload back. The IDOR became a public data-disclosure bug at the CDN edge.
We caught the IDOR with ECOM-RED-052 (numeric IDOR probe) and the CDN amplification with ECOM-CHAIN-014 (CDN-cache-leak chain that subscribes to ECOM-RED-052 and re-issues the URL anonymously through a public DNS resolver to test edge behaviour). The chained finding ships a single Proof Capsule per CDN edge node observed.
The fix is at the application: enforce record.tenant_id == session.tenant_id on every read path, and add a Vary: Authorization header so the CDN's cache key includes the session that produced the response.
Finding 2: SSRF in an image-proxy endpoint on a public-sector portal
The endpoint pattern: GET /image-proxy?url=https://.... Used by the portal to render avatars from external identity providers. The probe: substitute the url parameter with metadata-service URLs (http://169.254.169.254/latest/meta-data/), internal CIDR ranges, and DNS-rebind primitives.
The portal had a hostname allowlist, which the SSRF probe defeated by following an HTTP redirect from an allowlisted hostname back to an internal address. The portal then re-issued the request from inside the cluster, hit the cloud-metadata service, and returned the IAM credentials in the rendered image's metadata as an oversized HTTP response.
OWASP API Security Top 10 2023 lists SSRF as A-07. It is one of two API-class bugs that compounds catastrophically when the cluster has any of the major cloud metadata services exposed without IMDSv2 enforcement.
We caught the SSRF with API-SSRF-013 (parameter-substitution probe) and the redirect-bypass with API-SSRF-EXT-007 (the cross-issuer redirect class). The metadata-service hit was scored Critical because the IAM credentials were returnable. The Proof Capsule includes a benign demonstration: the probe fetches http://169.254.169.254/latest/meta-data/instance-id and returns the instance-id only; no credentials are exfiltrated.
The fixes are layered: enforce IMDSv2 cluster-wide, deny egress from the image-proxy pod to internal CIDR ranges via network policy, validate the resolved IP at request time (not just the hostname) against an allowlist, and reject HTTP redirects from external to internal addresses.
Finding 3: Subdomain takeover on a regulated financial institution
The pattern: a marketing subdomain careers.example-financial.com pointed at a CNAME to an external SaaS provider the institution had stopped using nine months earlier. The external SaaS provider had released the tenant name. An attacker could re-claim the tenant, control the subdomain, and serve any content under a careers.example-financial.com URL, including phishing pages with valid certificates issued by Let's Encrypt via HTTP-01 challenge, because the DNS still pointed at the provider's edge.
CISA documents this class in their KEV catalogue updates periodically as ATT&CK T1584 (Compromise Infrastructure → Domains). The mechanic is years old; the prevalence has not declined.
We caught it with RECON-DNS-031 (orphan-CNAME probe) and confirmed exploitability with RECON-CHAIN-018 (the tenant-claim-availability probe that asks the external SaaS whether the orphan-named tenant is currently registered). The decision rule is cname_target_responds=false AND saas_tenant_register_endpoint_returns_available=true.
The fix is mechanical: enumerate every external DNS record monthly, validate the target responds with content the marketing team recognises, and decommission the record when the relationship ends. The harder part is process, three of the five domains we found had been orphaned because the team that originally provisioned them no longer existed.
Finding 4: Hardcoded credentials in a build artifact on a global manufacturing customer
The pattern: a CI pipeline that built a customer-facing thick-client application embedded a .env file containing an AWS access key with s3:GetObject and s3:PutObject on the customer's manufacturing-telemetry bucket. The build artifact was a .dmg shipped to thousands of field technicians' laptops. The credentials sat inside Resources/app.asar in plain text.
OWASP Mobile Top 10 M-10 (Extraneous Functionality) covers this class. The CI team had treated the build pipeline as trusted; the team that signed the artifact had no visibility into the embedded resources.
We caught it with MOBILE-CREDS-005 (extract-and-scan probe against the .app bundle) and SUPPLY-CHAIN-022 (the upstream-build-pipeline check that flags any artifact embedding strings matching the AWS key regex from the AWS access-key format spec).
The Proof Capsule for this finding is short: extract the asar, grep for the key, attempt a benign aws s3 ls with the credentials, capture the bucket listing's first byte (a single object name). No data is read; the proof of access is the listing's existence.
The fix: rotate the key (the customer did this within 90 minutes of disclosure), pull the secret out of the build into a runtime config fetched after first-launch authentication, and add a build-step check that fails the CI job when any of the common-credential regexes match a string inside the bundle.
Finding 5: Open Redis on a SaaS analytics platform
The pattern: a Redis instance on tcp/6379 exposed to the public internet with no AUTH and no TLS. We see this class fortnightly; this time the Redis was the cache layer for a SaaS analytics platform handling 90 million events per day.
The probe is one TCP connection and one INFO command. The blast radius is the full key namespace. The Redis was being used as a job queue, so dumping the keys returned a stream of in-flight customer event data, including authenticated session tokens that the application was caching after login.
We caught it with INFRA-NET-024 (open-port-by-protocol probe) and INFRA-NET-CHAIN-009 (the cache-content-class chain that takes the open Redis and looks for token-shaped values among the keys). The decision rule for the chain is redis_response_contains_jwt_pattern OR session_token_pattern.
The fix is one minute of work, bind Redis to 127.0.0.1 or to the private VPC subnet, enable Redis AUTH, and add a network-policy rule rejecting inbound tcp/6379 from the internet. The harder part is asking why the customer's infrastructure-as-code pipeline allowed the deployment to ship with the Redis bound to 0.0.0.0 in the first place; the answer is usually that the IaC was copied from a stale tutorial in 2023 and nobody re-read it.
The pattern across patterns
Four of the five findings exist because trust boundaries between layers are implicit, not enforced. The application trusts the session is authorized. The image-proxy trusts the URL is external. The CDN trusts the cache key. The build pipeline trusts the artifact. The Redis trusts the network.
Implicit trust boundaries do not survive a real adversary. They survive a friendly pentest because the friendly pentester is asked to stop when they find one bug. Continuous testing finds them all in the same week because the same review hour gets a five-finding writeup instead of one, and the engineering team gets to see the pattern instead of the symptom.
We will keep writing these up weekly. We will keep anonymising the customers. The bugs we find this week are the bugs your platform is shipping right now.
Verifiable security.