File Upload SSRF Exfiltrates Live AWS Credentials from Retail SaaS Production
The Risk
A standard paying customer of this retail platform could get the company's live cloud server keys out of their production servers with three short requests. Those keys are the equivalent of the master keys to their cloud account. They rotate themselves every few hours, but the attack can be repeated at any time to get fresh keys. This is the cheapest possible path to full cloud compromise of a SaaS provider. No advanced skill, no exploit chain, no victim to trick, just a paid account and a few API requests.
The Vulnerability
The platform exposed a digital file upload endpoint that accepted an externalUrl parameter. The backend fetched the URL server-side and stored the full response body verbatim as a downloadable file attachment on the merchant's product. There was no allowlist on the URL, no blocklist on internal IP ranges, no DNS re-validation, and no restriction on response content. Six other endpoints (product image, gallery, category image, store logo, invoice logo, email logo) shared the same underlying fetch behavior but exposed only a binary status oracle. The files endpoint was the escalation path because it read and stored the full response body.
The cloud metadata service on the production EC2 fleet was reachable over plain HTTP from the backend. The legacy version of the metadata service was active, so no session token was required. That combination, internal reachability plus an unsafe server-side fetch that stored response bodies, was enough.
The Attack
- Authenticate to the platform as a standard paid merchant and create an empty product.
- POST to the files endpoint with
externalUrlpointing at the cloud metadata identity credentials path. The backend fetched the metadata service and stored the credentials JSON as a file attachment on the product. - POST again with
externalUrlpointing at the instance identity document path. That returned the account ID, instance ID, region, availability zone, and private IP of the production host. - GET the file back through the normal file download endpoint. The response body was a full set of live AWS temporary credentials: access key, secret key, and session token.
- Delete the test product to clean up.
The attack was also confirmed via an out-of-band callback to a researcher-controlled HTTPS server. The callback logged an inbound GET request from a production IP in the provider's cloud region, confirming the outbound fetch was running inside the production environment. During verification, a second request landed on a different EC2 instance in a different availability zone, which returned the same IAM credentials. That matched the expected behavior of a shared instance role on the production fleet.
The exfiltrated credentials were never used for any cloud API calls. They were extracted solely to prove impact, and the metadata service rotates them automatically, so repeating the attack produces a fresh valid set on demand.
The Impact
The credentials returned by the metadata service belong to a shared IAM instance role on the entire production fleet. That role is used by the backend to do its legitimate work, so the permissions attached to it are not guessable from the outside, but they are at least sufficient to run the platform's own production workloads. From a fresh set of those credentials an attacker can:
- Enumerate the account's other services and infrastructure via standard AWS APIs.
- Use any permission granted to the instance role: object storage, database access, message queues, secret managers, depending on configuration.
- Pivot laterally using private network information leaked in the instance identity document.
- Refresh indefinitely, because the metadata service rotates credentials on its own schedule and the SSRF vector is repeatable.
There is no merchant-level privilege escalation required. Any paying customer of the platform is a starting position for full cloud account compromise.
Remediation
- Require the modern version of the cloud metadata service on every production host so that SSRF cannot fetch credentials with a simple GET request. This is a single infrastructure flag and blocks the escalation path entirely.
- Implement an SSRF blocklist on every endpoint that accepts an external URL parameter. Reject internal and link-local IP ranges in all four RFC-1918 private ranges plus the loopback and metadata ranges, and reject the IPv6 equivalents.
- Validate the resolved IP after DNS resolution, not just the hostname string. Re-validate after every HTTP redirect so attackers cannot redirect from a public host to an internal one.
- Where possible, fetch externally supplied URLs through an outbound proxy with an explicit allowlist instead of direct egress from application servers.
- Scope the IAM instance role down to the minimum the backend actually needs. A leaked shared-fleet role with excessive permissions is a common multiplier for this class of bug.
- Audit the cloud provider's access logs for the leaked access keys to confirm they were never used outside the researcher's scope.