← THE INDEX  ·  WRITEUP

Two SSRF Vulnerabilities in a Cloud Image Pipeline

How inconsistent URL-validation coverage across four outbound-HTTP code paths produced two independent SSRF vectors in the same platform.

Summary

A major cloud media-processing platform had solid SSRF filtering on its primary file-upload endpoint. The same outbound HTTP client, called from two other code paths, had no filtering at all. One path was the notification/webhook callback parameter; the other was an image-format processing pipeline that resolved external URL references embedded in uploaded files during server-side rendering.

Both were reported through the platform's bug bounty program and resolved under coordinated disclosure. Vendor and specific instance identifiers are omitted per program policy.

Impact

Webhook/callback SSRF (High). The notification URL parameter accepted any internal address that the upload parameter correctly blocked: AWS instance metadata at 169.254.169.254, ECS credential endpoints, localhost Redis and Elasticsearch, and all RFC1918 ranges. An attacker who set a malicious callback URL could trigger SSRF with no API key and no rate limit once the configuration was in place, because a sub-feature allowed pre-configured notification URLs to be triggered without authentication.

The JSON POST body sent to the callback included several attacker-controllable fields, meaning internal services that parse incoming webhooks were also reachable with partially crafted payloads.

Image-render SSRF (Medium). The image processing library followed external URL references embedded in uploaded files (for example, an SVG <image xlink:href="..."> element) when converting to a raster format. Responses were rendered as pixel data rather than returned as raw text, limiting direct credential exfiltration. Image-format responses from internal monitoring dashboards and status pages would be fully readable. Timing analysis confirmed internal network connection attempts: transformations referencing the AWS metadata endpoint consistently took 12-plus seconds versus under one second for normal transforms.

Root cause

The platform applied SSRF filtering per feature rather than per network boundary. The upload/fetch path had a complete filter. The notification system and the image processing pipeline were built separately, by different teams or at different times, and each made its own outbound HTTP requests without routing through the same validation layer.

Four separate code paths caused the server to make outbound HTTP requests. Only one was protected:

  1. File upload/fetch path (protected)
  2. Notification/webhook callback path (unprotected)
  3. Image processing/rendering path (unprotected)
  4. Additional async processing paths (untested)

This is the canonical SSRF coverage gap: an organization knows about SSRF and implements filtering, but applies it to one entry point rather than one network egress layer. Every outbound request, regardless of which feature triggered it, should pass through the same validation.

Proof of concept

The following reproduces the vulnerability pattern without vendor-identifiable details. All specific hostnames, API keys, and instance identifiers have been replaced with generic placeholders.

Image render proof

The screenshot below is the actual output from the image processing pipeline fetching an external URL and rendering its content as pixel data. It confirms the server made an outbound HTTP request to the attacker-controlled URL and embedded the response in the output image. Vendor identifiers have been cropped per program policy.

Disclosure and fix

Both vulnerabilities were reported through the platform's bug bounty program on HackerOne and resolved under coordinated disclosure. Patches were applied to the notification/callback path and the image processing pipeline. The vendor confirmed the upload/fetch path's existing SSRF filter was extended to cover all outbound request paths.

Standard remediations: apply the same IP/URL blocklist (RFC1918, loopback, link-local, CGNAT 100.64/10) to every parameter that triggers an outbound request, validate after DNS resolution to prevent rebinding attacks, and restrict image processing libraries from resolving external URLs using policy configuration:

<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
<policy domain="coder" rights="none" pattern="HTTP" />